M
by Martin156131 on 3 Jun 2024, edited 13 Jun 2024
#
+3 votes
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?
A
by Arv on 5 Jun 2024
#
+1 votes
@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.
M
by Martin156131 on 11 Jun 2024
#
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?
M
by Moult on 12 Jun 2024
#
+1 votes
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
B
by bernd on 12 Jun 2024
#
+1 votes
@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.
M
by Moult on 12 Jun 2024
#
+1 votes
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.
G
by Gorgious on 13 Jun 2024
#
+1 votes
FWIW Blender also has BVHTree and KDTree utilities https://docs.blender.org/api/current/mathutils.bvhtree.html
M
by Moult on 13 Jun 2024
#
+1 votes
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...