znlgis 博客

GIS开发与技术分享

第12章:几何表达、布尔运算与三角网格生成

本章在前一章的架构基础上”动手”——具体走通 IFC 几何 → OCCT 实体 → 三角网格的全流程,并解析布尔运算、扫掠、面修复等关键算法在 Xbim 中的入口。

1. IfcRepresentation 体系回顾

IfcProductDefinitionShape
  └── IfcShapeRepresentation*               (Body / Axis / FootPrint / Profile)
        ├── ContextOfItems → IfcGeometricRepresentationContext
        ├── RepresentationType: "SweptSolid" / "Brep" / "Tessellation" / ...
        └── Items[]: IfcRepresentationItem
              ├── IfcExtrudedAreaSolid / IfcRevolvedAreaSolid / IfcSweptDiskSolid
              ├── IfcBooleanResult / IfcBooleanClippingResult
              ├── IfcFacetedBrep / IfcManifoldSolidBrep
              ├── IfcTriangulatedFaceSet / IfcPolygonalFaceSet
              ├── IfcMappedItem  → IfcRepresentationMap
              └── IfcGeometricCurveSet, IfcSurfaceCurveSweptAreaSolid, ...

记住几个常见 RepresentationType:

类型 Items 包含
SweptSolid IfcExtrudedAreaSolid / IfcRevolvedAreaSolid
Brep IfcManifoldSolidBrep 系列
Tessellation IfcTriangulatedFaceSet / IfcPolygonalFaceSet
Clipping IfcBooleanClippingResult(拉伸 + 半空间裁切)
MappedRepresentation IfcMappedItem(实例化复用)
Curve2D / Curve3D 线段、轮廓

2. 拉伸(IfcExtrudedAreaSolid)

最常见的 IFC 几何之一。

IfcExtrudedAreaSolid
   SweptArea         : IfcProfileDef     (2D 轮廓)
   Position          : IfcAxis2Placement3D (拉伸坐标系)
   ExtrudedDirection : IfcDirection
   Depth             : IfcPositiveLengthMeasure

Xbim 的转换路径:

  1. IfcProfileDefTopoDS_Wire(二维线框):
    • IfcRectangleProfileDefIfcCircleProfileDefIfcIShapeProfileDefIfcArbitraryClosedProfileDef
  2. BRepBuilderAPI_MakeFace 闭合为面;
  3. 调用 BRepPrimAPI_MakePrism(face, dir * depth) 拉伸成 TopoDS_Solid
  4. 应用 Position 矩阵变换;
  5. 包装为 XbimSolid 返回托管层。
var solid = engine.CreateSolid((IIfcExtrudedAreaSolid)rep);
Console.WriteLine($"V = {solid.Volume}, A = {solid.SurfaceArea}");

3. 旋转、扫掠、放样

IFC OCCT 调用 说明
IfcRevolvedAreaSolid BRepPrimAPI_MakeRevol 围绕轴线旋转生成
IfcSurfaceCurveSweptAreaSolid BRepOffsetAPI_MakePipeShell 沿引导线扫掠
IfcSweptDiskSolid BRepOffsetAPI_MakePipe + 圆截面 管道(DN)几何
IfcSectionedSpine 自定义放样 断面变化的扫掠
IfcAdvancedBrep OCCT 直接构造 NURBS B-Rep IFC4 引入,支持精确曲面

4. 布尔运算

IFC 用 IfcBooleanResult 表达布尔操作:

IfcBooleanResult
   Operator        : (UNION | INTERSECTION | DIFFERENCE)
   FirstOperand    : IfcBooleanOperand
   SecondOperand   : IfcBooleanOperand

IfcBooleanOperand 可以是 Solid、HalfSpace 或另一个 BooleanResult,递归形成 CSG 树。

Xbim 调用 OCCT 的对应类:

Operator OCCT 类
UNION BRepAlgoAPI_Fuse
DIFFERENCE BRepAlgoAPI_Cut
INTERSECTION BRepAlgoAPI_Common
var result = engine.CreateBooleanResult((IIfcBooleanResult)rep);

布尔失败的常见原因:

  • 共面相切:两个面恰好共面,OCCT 无法判断布尔结果。要么微调容差,要么微小偏移。
  • 零厚度面:被扣掉之后剩 0 体积,OCCT 抛错。
  • 面方向反向:OCCT 对面朝向敏感;Xbim 的 ShapeFix/Healing 会先做修复。
  • 递归过深:上百次嵌套布尔会让性能下降到分钟级。

Xbim.Geometry.Engine 内部对每次失败会尝试若干 fallback:调整容差、跳过某次操作、改用 IfcBooleanClippingResult 的近似。

5. 半空间(IfcHalfSpaceSolid)

IFC 用半空间表达”裁切平面”:

IfcHalfSpaceSolid
   BaseSurface : IfcSurface
   AgreementFlag : Boolean    // false = 法向同侧

Xbim 把它转成 OCCT 的”无界面 + 方向”组合,通常作为 IfcBooleanResultSecondOperand 用来切墙、切板。

IfcPolygonalBoundedHalfSpace(带多边形边界的半空间)则是切土方、屋顶坡时常用。

6. Brep 与离散网格

6.1 IfcFacetedBrep / IfcManifoldSolidBrep

IfcClosedShellIfcFaceIfcFaceBoundIfcPolyLoop 组成,是离散多边形构造的实体。Xbim 直接用 BRepBuilderAPI_MakeFace + MakeShell + MakeSolid 构造对应 OCCT 拓扑,并用 ShapeFix_Solid 修复方向。

6.2 IfcTriangulatedFaceSet(IFC4)

IFC4 引入的高效”三角网格”表达:

IfcTriangulatedFaceSet
   Coordinates   : IfcCartesianPointList3D    (顶点)
   CoordIndex    : LIST OF LIST OF integer    (三角形索引)
   Normals       : optional
   ...

Xbim 转换时直接构造 XbimShapeTriangulation,跳过 OCCT B-Rep 中间层(更快)。很多用 web-ifc / IfcOpenShell 导出的 IFC4 文件会用这种 representation,性能极佳。

6.3 IfcPolygonalFaceSet

类似但允许任意 N 边形面,Xbim 内部先做扇形剖分再做三角化。

7. 三角化(Mesh / Tessellation)

OCCT 通过 BRepMesh_IncrementalMesh 把 B-Rep 转成三角网格。Xbim 调用:

BRepMesh_IncrementalMesh mesher(shape, deflection, /*relative*/ false, angle);
mesher.Perform();

参数:

  • deflection:弦高偏差,决定圆弧近似精度(mm);
  • angle:相邻三角形的最大法线夹角(rad);
  • relative:deflection 是否按 BRep 包围盒尺寸归一化。

之后 Xbim 提取 Poly_Triangulation,写入 XbimShapeTriangulation 或直接序列化到 wexbim 流:

using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
engine.WriteTriangulation(bw, geomObject, tolerance: precision, deflection: 5, angle: 0.5);

8. Xbim3DModelContext:批量处理

应用层很少手工处理每个 IfcRepresentationItem,而是用 Xbim3DModelContextXbim.ModelGeometry.Scene):

using Xbim.ModelGeometry.Scene;

var context = new Xbim3DModelContext(model);
context.CreateContext(progDelegate: (p, m) => Console.Write($"\r{p}% {m}    "));

它做的事:

  1. 找到模型中所有有 Body representation 的产品;
  2. 对每个独特的 representation 执行一次几何转换(共享几何只算一次,对应 IfcMappedItem/IfcRepresentationMap);
  3. 计算每个产品的最终变换矩阵;
  4. 按颜色/材料分桶,方便后续渲染;
  5. 把网格数据写入 IModel.GeometryStore(在 .xbim 中是单独的几何流);
  6. 内部使用并行任务(Parallel.ForEach)加速。

之后,可以遍历几何:

using (var reader = model.GeometryStore.BeginRead())
{
    foreach (var shape in reader.ShapeInstances)
    {
        // shape.IfcProductLabel
        // shape.Transformation
        // shape.RepresentationLabel
    }

    foreach (var geom in reader.ShapeGeometries)
    {
        // geom.ReferenceCount, geom.BoundingBox, geom.ShapeData(三角化数据)
    }
}

ShapeInstances 是”产品级”实例,ShapeGeometries 是被复用的几何。这种”实例 vs 几何”的分离让大型模型可以高效渲染。

9. Mapped Items(实例化复用)

IFC 用 IfcMappedItem 表达”同一份几何被多次放置”:

IfcMappedItem
   MappingSource : IfcRepresentationMap   (引用了一个 IfcShapeRepresentation)
   MappingTarget : IfcCartesianTransformationOperator

例如一栋楼的 1000 扇相同窗只需要 1 个 IfcRepresentationMap,每个窗用 1 个 IfcMappedItem 引用 + 自己的局部变换。Xbim 会自动识别这种共享,几何引擎只做 1 次三角化。

10. 颜色与表面样式

虽然不属于”几何”,但渲染必备:

IfcStyledItem
   Item    : IfcRepresentationItem
   Styles  : IfcPresentationStyle

IfcSurfaceStyle 含表面颜色、镜面、透明度等。Xbim3DModelContext 在打包时会把样式与几何关联到同一个 XbimGeometryStyle,方便渲染端按样式分桶。

11. 实战:导出某构件的 OBJ 网格

using Xbim.Common.Geometry;

void ExportObj(IfcStore model, string outPath)
{
    var ctx = new Xbim3DModelContext(model);
    ctx.CreateContext();

    using var sw = new StreamWriter(outPath);
    int vbase = 0;
    using var r = model.GeometryStore.BeginRead();

    foreach (var inst in r.ShapeInstances)
    {
        var geom = r.ShapeGeometry(inst.ShapeGeometryLabel);
        var data = geom.ShapeData;
        var ms   = new MemoryStream(data);
        var tri  = ms.ReadShapeTriangulation();

        tri.Transform(inst.Transformation);
        foreach (var v in tri.Vertices)
            sw.WriteLine($"v {v.X} {v.Y} {v.Z}");
        foreach (var face in tri.Faces)
            foreach (var t in face.Indices.Chunk(3))
                sw.WriteLine($"f {t[0]+1+vbase} {t[1]+1+vbase} {t[2]+1+vbase}");

        vbase += tri.Vertices.Count;
    }
}

ReadShapeTriangulation / Transform 等 API 名因版本略有差异,请参考 Xbim.Common.Geometry.XbimShapeTriangulation。)

12. 性能与诊断

  • CreateContext 是耗时大头:100MB 的 IFC 通常需要数分钟甚至几十分钟。
  • 缓存 wexbim:第一次转换写入 .xbim,之后只读取已转好的几何。
  • 诊断报告Xbim3DModelContext 内部记录 BooleanFailuresShapeConstructionFailures,可通过 model.Tag 上的统计或日志查看。
  • 并行度:默认按 CPU 核数,可通过 Environment.ProcessorCount 控制;I/O 密集时建议适当下调。

13. 几何引擎的”修复”功能

针对脏 IFC,Xbim 内置:

  • 容差自适应:先用 Precision,失败后逐步放大;
  • 面修复:调用 OCCT ShapeFix_ShapeShapeUpgrade_UnifySameDomain
  • 边/顶点缝合:BRepBuilderAPI_Sewing
  • 退化检测:跳过零体积/零面积的 representation;
  • 异常隔离:单个产品几何失败不会中断整个 CreateContext

14. 小结

  • IFC 几何由参数化、布尔、Brep、网格四种风格组成;
  • Xbim.Geometry 把它们映射到 OCCT 的 BRepBuilderAPIBRepPrimAPIBRepAlgoAPIBRepMesh 等 API;
  • 应用层一般通过 Xbim3DModelContext 一键完成批量转换并落入 wexbim 流;
  • 大模型的几何转换成本高,应该缓存结果;
  • 布尔失败、容差不当是常见故障源,必须依赖日志诊断。

下一章我们直接利用这些结果做可视化。