NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 - 3D道路快速建模
在这个简短的练习中,我们将在 IfcOpenHouse 模型之上构建。 它是首批完全使用程序代码构建的 IFC 模型之一。 这是一个不错的模型,但它有一些缺点:例如,它没有定义一个 IfcSpace 实例来描述由墙壁和屋顶界定的内部空间。 在本练习中,我们将根据边界元素的几何形状自动建模该空间的几何形状,并计算模型的内部体积。
1、快速入门
# Specify to return pythonOCC shapes from ifcopenshell.geom.create_shape()
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_PYTHON_OPENCASCADE, True)
# Initialize a graphical display window
occ_display = ifcopenshell.geom.utils.initialize_display()
# Open the IFC file using IfcOpenShell
ifc_file = ifcopenshell.open("IfcOpenHouse.ifc")
# Display the geometrical contents of the file using Python OpenCascade
products = ifc_file.by_type("IfcProduct")
for product in products:
if product.is_a("IfcOpeningElement"): continue
if product.Representation:
shape = ifcopenshell.geom.create_shape(settings, product).geometry
display_shape = ifcopenshell.geom.utils.display_shape(shape)
if product.is_a("IfcPlate"):
# Plates are the transparent parts of the window assembly
# in the IfcOpenHouse model
ifcopenshell.geom.utils.set_shape_transparency(display_shape, 0.8)
导入 IFC 模型非常简单,然后我们得到了对 IfcProduct 子类型实例的引用。 这些类包括所有具有 3d 表示的建筑元素。 这还包括 IfcOpeningElements,这是一种用于提取门窗开口的机制,通常是从墙上提取开口。 不会导入这些元素的几何形状(请注意,continue 表示继续迭代中的下一个元素)。 IfcOpenShell 可用于创建 3d 形状来表示建筑元素。
请注意,在 IFC 中,几何被分解为不同复杂度的 IfcRepresentationItems。 IfcOpenShell 隐藏了这种复杂性,并为每个产品提供统一的边界表示 (BRep)。
2、过滤元素
但是,对于本练习,我们并不对所有类型的产品感兴趣。 最初我们只对建筑物的四面墙感兴趣。
# Get a list of all walls in the file
walls = ifc_file.by_type("IfcWall")
# Create a list of wall representation shapes
# and compute the bounding box of these shapes
wall_shapes = []
bbox = OCC.Bnd.Bnd_Box()
for wall in walls:
shape = ifcopenshell.geom.create_shape(settings, wall).geometry
wall_shapes.append((wall, shape))
OCC.BRepBndLib.BRepBndLib_Add(shape, bbox)
ifcopenshell.geom.utils.display_shape(shape)
# Calculate the center/average of the bounding box
bounding_box_center = ifcopenshell.geom.utils.get_bounding_box_center(bbox)
print "Bounding box center: %.2f %.2f %.2f" % (
bounding_box_center.X(),
bounding_box_center.Y(),
bounding_box_center.Z())
occ_display.DisplayMessage(bounding_box_center, "Center", update=True)
除了简单地获得墙壁的 3d 形状外,内部空间的中点是通过计算几何边界框的平均值来确定的。 稍后需要这个中间点来确定墙的哪一侧作为内部空间体积的基础。
面(Face)是由基准表面(Surface Plane)上的连线(Wire)组成的。 连线由边(Edge)和顶点(Vertex)组成。 边有对应的基础曲线(Curve)。
在OpenCascade和大多数其他几何内核中,几何和拓扑之间存在区别,有关这些概念的更多信息,请阅读这个博客文章。
3、提取面
# Now create halfspace solids from the inner faces of the wall
halfspaces = []
for wall, shape in wall_shapes:
topo = OCC.Utils.Topo(shape)
for face in topo.faces():
surf = OCC.BRep.BRep_Tool.Surface(face)
obj = surf.GetObject()
assert obj.DynamicType().GetObject().Name() == "Geom_Plane"
plane = OCC.Geom.Handle_Geom_Plane.DownCast(surf).GetObject()
if plane.Axis().Direction().Z() == 0:
face_bbox = OCC.Bnd.Bnd_Box()
OCC.BRepBndLib.BRepBndLib_Add(face, face_bbox)
face_center = ifcopenshell.geom.utils.get_bounding_box_center(face_bbox).XYZ()
face_normal = plane.Axis().Direction().XYZ()
face_towards_center = bounding_box_center.XYZ() - face_center
face_towards_center.Normalize()
dot = face_towards_center.Dot(face_normal)
if dot < -0.8:
ifcopenshell.geom.utils.display_shape(face)
face_plane = plane.Pln()
new_face = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeFace(face_plane).Face()
halfspace = OCC.BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(
new_face, bounding_box_center).Solid()
halfspaces.append(halfspace)
我们需要确定墙体积的哪些面指向中间点,以便它们可以成为内部空间边界体积的一部分。
OpenCascade 有几种类型的形状:实体、壳、面、线、边和顶点以及它们的组合。 IfcOpenShell 为墙返回的形状可能是实体,但我们现在真的不需要这样做。 我们可以简单地遍历绑定实体的面。 然后我们可以获得对该面的底层表面的引用。
由于 IfcOpenHouse 模型仅由平面几何组成,我们知道表面符合平面。 如果垂直于平面的法向量的方向指向中点,我们知道它是我们感兴趣的面之一。这是通过计算两个向量的点积来完成的,请在维基百科上阅读更多内容。 请注意,0.8 常数是相当随意的。 从技术上讲,窗户开口减法的一些面也指向空间的中间点。
请注意,人脸不一定需要有界。 从通过迭代边界壁的表面获得的平面方程,我们可以获得半空间实体。 你可以想象所有这些半空间实体的交集形成了我们内部空间的体积。 但首先使用类似的技巧来获取屋顶元素的底面。
# Create halfspace solids from the bottom faces of the roofs
roofs = ifc_file.by_type("IfcRoof")
for roof in roofs:
shape = ifcopenshell.geom.create_shape(settings, roof).geometry
topo = OCC.Utils.Topo(shape)
for face in topo.faces():
surf = OCC.BRep.BRep_Tool.Surface(face)
plane = OCC.Geom.Handle_Geom_Plane.DownCast(surf).GetObject()
assert obj.DynamicType().GetObject().Name() == "Geom_Plane"
if plane.Axis().Direction().Z() > 0.7:
face_plane = plane.Pln()
new_face = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeFace(face_plane).Face()
halfspace = OCC.BRepPrimAPI.BRepPrimAPI_MakeHalfSpace(
new_face, bounding_box_center).Solid()
halfspaces.append(halfspace)
4、创建空间体积
不幸的是,仅从无限实体创建有界实体在 OpenCascade 中效果不佳。 因此,我们从一个我们知道肯定适合我们空间的整个体积的有界实体开始。 从此框中减去从 IFC 文件中的产品创建的半空间。
# Create an initial box from which to cut the halfspaces
common_shape = OCC.BRepPrimAPI.BRepPrimAPI_MakeBox(
OCC.gp.gp_Pnt(-10, -10, 0),
OCC.gp.gp_Pnt(10, 10, 10)).Solid()
for halfspace in halfspaces:
common_shape = OCC.BRepAlgoAPI.BRepAlgoAPI_Common(
common_shape, halfspace).Shape()
ifcopenshell.geom.utils.display_shape(common_shape)
# Calculate the volume properties of the resulting space shape
props = OCC.GProp.GProp_GProps()
OCC.BRepGProp.BRepGProp_VolumeProperties(shape, props)
print "Space volume: %.3f cubic meter" % props.Mass()
这个例子只是触及了 Python、OpenCascade 和 IfcOpenShell 的皮毛。 OpenCascade 提供了多种形状分析和修复工具,可以提取描述性度量,例如本例中的空间体积。
原文链接:Using IfcOpenShell and pythonOCC to construct new geometry
BimAnt翻译整理,转载请标明出处