[IfcOpenShell-Python] How to calculate a distance between two IFC entities?

edited June 3 in General

In the past, I used successfully PythonOCC to determine the volume from one IFC entity. So I thought, why not also use PythonOCC for this problem and got the following code:

import ifcopenshell
from ifcopenshell import geom
import OCC.Core.BRepExtrema as BRepExtrema

File = "two-cube-built-element.ifc"
ifc_file = ifcopenshell.open(File)

Units = ifc_file.by_type("IFCSIUNIT")
for unit in Units:
    if unit.UnitType == "LENGTHUNIT":
        length_unit = unit.Name
        break

# Define settings
settings = geom.settings()
settings.set(settings.USE_PYTHON_OPENCASCADE, True) # tells ifcopenshell to use pythonocc

ifc_products = ifc_file.by_type("IfcBuiltElement")
Shapes = [ ifcopenshell.geom.create_shape(settings, product) for product in ifc_products] 

# Create the BRepExtrema_DistShapeShape object
dist = BRepExtrema.BRepExtrema_DistShapeShape(Shapes[0].geometry, Shapes[1].geometry)

# Perform the distance calculation
dist.Perform()

# Get the minimum distance
min_distance = dist.Value()

print("Minimum distance between the two IfcBuiltElement:", min_distance, length_unit.lower())

For my test IFC file (attached see below), I got the right result! See image:

And it is the same value as in BIMVision:

My next step is to calculate the distance between the bottom faces of both cubes, like in the image below:

My question is, before I go down deeper in this rabbit hole named PythonOCC, to solve my new problem: Are there other possibilities how I can solve this with a python script? Maybe other Python libraries?

Johntheoryshawsteverugi

Comments

  • @Martin156131 This can be achieved in ifcopenshell by creating a Geometry Tree and then using the select_ray method. This returns a list which also holds the distance between the ray origin and the intersected object. More info here https://docs.ifcopenshell.org/ifcopenshell-python/geometry_tree.html#selecting-elements-using-a-ray.
    I've used this in a few scripts.

    Martin156131
  • A little update, I got this script, which calculate the distance between the bottom faces of both cubes:

    import ifcopenshell
    from ifcopenshell import geom
    from OCC.Core import TopAbs
    from OCC.Core.TopExp import TopExp_Explorer
    from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
    import OCC.Core.BRepExtrema as BRepExtrema
    import OCC.Core.gp as gp
    from OCC.Core.BRep import BRep_Tool
    
    File = "two-cube-built-element.ifc"
    ifc_file = ifcopenshell.open(File)
    
    Units = ifc_file.by_type("IFCSIUNIT")
    for unit in Units:
        if unit.UnitType == "LENGTHUNIT":
            length_unit = unit.Name
            break
    
    # Define settings
    settings = geom.settings()
    settings.set(settings.USE_PYTHON_OPENCASCADE, True) # tells ifcopenshell to use pythonocc
    
    ifc_products = ifc_file.by_type("IfcBuiltElement")
    
    Cube1, Cube2 = [ ifcopenshell.geom.create_shape(settings, product).geometry for product in ifc_products] 
    
    # Function to get the bottom face of a shape
    def get_bottom_face(compound):
        # Create a TopExp_Explorer to iterate over the faces in the compound
        explorer = TopExp_Explorer(compound, TopAbs.TopAbs_FACE)
    
        # Iterate over the faces in the compound
        while explorer.More():
            face = explorer.Current()
    
            # Check if the face is the bottom face
            # You can modify this condition based on your specific cube representation
            if is_bottom_face(face):
                return face
    
            # Move to the next face
            explorer.Next()
    
        return None
    
    # Function to check if a face is the bottom face
    def is_bottom_face(face):
        # You can implement your logic to identify the bottom face based on your specific cube representation
        # For example, you can check the normal of the face or its position in relation to the cube
    
        # Here's a simple example assuming the bottom face has a normal pointing downwards
        surface = BRepAdaptor_Surface(face)
        normal = surface.Plane().Axis().Direction()
        return normal.IsEqual(gp.gp_Dir(0, 0, -1), 1e-6)  # Tolerance is set to 1e-6
    
    # Get the bottom faces of Cube1 and Cube2
    bottom_face_cube1 = get_bottom_face(Cube1)
    bottom_face_cube2 = get_bottom_face(Cube2)
    
    # Calculate the minimal distance between the bottom faces
    dist = BRepExtrema.BRepExtrema_DistShapeShape(bottom_face_cube1, bottom_face_cube2)
    dist.Perform()
    min_distance = dist.Value()
    
    print("Minimum distance between the bottom faces of the two IfcBuiltElement:", min_distance, length_unit.lower())
    

    With this code, I got the right result:

    @Arv Thanks for the hint. Is there a way to specify that I want the distance between the bottom faces of the two cubes?

  • You can use ifcopenshell.util.shape.get_volume() to get the volume and not worry about OCC.

    I wouldn't recommend using BRepExtrema_DistShapeShape because it's actually extremely slow. We use an implementation in the geometry tree (typically for IfcClash) using an implementation from NVidia Omniverse's PhysX written by very clever people who invent these types of algos. It's got lots of toys I'd highly recommend investigating.

    It's exposed as a many-many but should work with two objects too: https://docs.ifcopenshell.org/ifcopenshell-python/geometry_tree.html#detecting-clearance-clashes-between-elements

    theoryshaw
  • @Moult said:
    You can use ifcopenshell.util.shape.get_volume() to get the volume and not worry about OCC.

    very useful, I did not know about this.

    BTW: I do use for all kind of geometry algos I need to do a FreeCAD shape and the methods of FreeCAD. It might not be the fastest, but it has hundreds of methods to evaluate geometry including geometry checking.

    Martin156131
  • Indeed, another option is mathutils.geometry from Blender. However for distance checks, BVH trees are extremely useful so I'd highly recommend the IfcOpenShell geometry tree.

    Martin156131
  • FWIW Blender also has BVHTree and KDTree utilities https://docs.blender.org/api/current/mathutils.bvhtree.html

  • While we're at it, I'd also like to shoutout to shapely which is awesome library for 2D geometry analysis. Great for cross sectional profiles, cut shapes, wall topology...

Sign In or Register to comment.