OCCT的Shape基础概念
OCCT的Shape基础概念Shape分类occt中Shape发挥着类似拓扑的概念。常规的类型有Solid、Shell、Face、Wire、Edge、Vertex作为容器概念的类型有Compound和CompSolid。Solid指的是单一的实心体可以类似对标ACIS中的Lump概念但ACIS的Lump并限制实心体而是“独立联通曲域”的概念这一点Parsolid中的Region一词更为确切。occt中没有Lump或Region的概念或者说Shape并不对比商业内核的拓扑它只是“形状”只为表示一个模型的形状而存在并没有确切的拓扑规则。occt中并没有抽象顶层实体BODY的概念因此sheet-body可以是单一的Face对象也可以是Shell对象或者Face与Shell的集合容器Wire-body可以是单一的Wire对象也可以是Edge的集合。其中Wire是个很容易误解的概念它是类似ACIS中Loop的概念一般对一个Face而言其下的第一个Wire 是外环其余的是内环同时它也可以是线框体的概念。occt中没有ACIS中Coedge的概念因为它没有拓扑的概念相邻面的Edge对象只有“方向不同”这唯一的差异。Shape的组成Shape是一个struct它只有三个成员一个TShape指针、一个Loc矩阵、一个方向枚举。// 三个成员变量classTopoDS_Shape{Handle(TopoDS_TShape)myTShape;// 指向底层TShape的句柄TopLoc_Location myLocation;// 位置矩阵TopAbs_Orientation myOrientation;// 方向FORWARD/REVERSED等};Shape有三种层次的拷贝直接赋值几乎无成本仅三个成员拷贝浅拷贝TShape重新构建TShape对象但是底层仍会服用Geom等几何对象Geom_Surface、Geom_Curve深拷贝几何重建Geom底层几何对象、重建TShape对象成本极高TopoDS_Shape shape1boxMaker.Shape();TopoDS_Shape shape2shape1;// 直接赋值// 浅拷贝示例BRepBuilderAPI_Copycopier(shape1,false);// 浅拷贝TopoDS_Shape shape2copier.Shape();// 深拷贝示例BRepBuilderAPI_Copycopier(shape1,true);// 设置为深拷贝TopoDS_Shape shape2copier.Shape();Compound与CompSolidCompound是一个纯粹的shape容器其成员可以是任意类型的Shape包括Compound。天然贴近“装配”和“非流形结构复合体”概念// 2. 创建一个Compound并添加所有形状TopoDS_Builder builder;TopoDS_Compound compound;builder.MakeCompound(compound);// Add方法接受任何TopoDS_Shape类型builder.Add(compound,box);builder.Add(compound,sphere);builder.Add(compound,edge);CompSolid是Solid容器它要求成员必须是Solid类型并且组成Solid的Face中必须有共同关系。但事实上用户可以将随意的多个Solid构建成CompSolid即使它们并无公共Face的关系。但是后续对这个 CompSolid 调用几何算法如计算体积、质量属性、网格划分或布尔运算时算法会默认遵循 CompSolid 的数学定义即公共面处共享边界如果内部 Solid 是分离的算法会因为找不到预期的公共面结构而计算失败或直接忽略内部缝隙导致结果错误。如果想要构建出合理的CompSolid对象一种方式是自行组装每个Solid在组装的过程中使用公共Face另一种方式是使用CellBuilder或Sew工具这类强大的工具可以获得此类非流形结构工具的结果可能是Compound需要二次加工成CompSolid还有一种方式是使用Reshape使紧贴的两个Solid共同同一个Face。手动装配// Box1的Shell包含6个面BRepBuilderAPI_MakeShell shell1Builder;shell1Builder.Add(face1_Bottom);shell1Builder.Add(face1_Top);shell1Builder.Add(face1_Front);shell1Builder.Add(face1_Back);shell1Builder.Add(face1_Left);shell1Builder.Add(face1_Right);shell1Builder.Build();// Box2的Shell包含6个面其中左面是共享的BRepBuilderAPI_MakeShell shell2Builder;shell2Builder.Add(face2_Bottom);shell2Builder.Add(face2_Top);shell2Builder.Add(face2_Front);shell2Builder.Add(face2_Back);shell2Builder.Add(face1_Right);// 共享面shell2Builder.Add(face2_Right);shell2Builder.Build();// 使用 BRepBuilderAPI_MakeSolid Shell分别构建Solid1和Solid2// 使用 TopoDS_Builder 构建 CompSolid仅组合并不做几何有效性检查TopoDS_Builder builder;TopoDS_CompSolid compSolid;builder.MakeCompSolid(compSolid);builder.Add(compSolid,solid1);builder.Add(compSolid,solid2);// 使用BRepCheck_Analyzer进行详细检查// 对于CompSolidBRepCheck_Analyzer好像要求必须存在方向相反的共享面否则将视为InValidBRepCheck_Analyzeranalyzer(compSolid);CellBuilder算法驱动// 使用两个贴面的BoxBRepBuilderAPI_MakeBoxbox1(gp_Pnt(0,0,0),gp_Pnt(10,10,10));BRepBuilderAPI_MakeBoxbox2(gp_Pnt(10,0,0),gp_Pnt(20,10,10));TopTools_ListOfShape arguments;arguments.Append(box1.Shape());arguments.Append(box2.Shape());// 使用CellBuilderBOPAlgo_Cells cellBuilder;cellBuilder.SetArguments(arguments);cellBuilder.Perform();if(!cellBuilder.IsDone()){throwstd::runtime_error(CellBuilder failed);}// 获取所有的CellconstTopTools_ListOfShapecellscellBuilder.Areas();// CellBuilder会正确处理共享面生成的Cell都是独立的Solid// 并且相邻的Cell共享同一个Face即共享TShape// 使用 TopoDS_Builder 构建 CompSolid仅组合并不做几何有效性检查TopoDS_Builder builder;TopoDS_CompSolid compSolid;builder.MakeCompSolid(compSolid);builder.Add(compSolid,solid1);//solid1:cells中的Solidbuilder.Add(compSolid,solid2);//solid2:cells中的SolidReshape操作// 1. 创建两个独立的 Box// Box1 位于原点尺寸 10x10x10BRepPrimAPI_MakeBoxboxMaker1(gp_Pnt(0,0,0),gp_Pnt(10,10,10));TopoDS_Shape shape1boxMaker1.Shape();// Box2 从 x10 到 x20尺寸相同BRepPrimAPI_MakeBoxboxMaker2(gp_Pnt(10,0,0),gp_Pnt(20,10,10));TopoDS_Shape shape2boxMaker2.Shape();// 2. 提取目标面// 从 shape1 中提取右面 (x10 的面)TopoDS_Face faceFromShape1findFaceByPosition(shape1,10.0);// 从 shape2 中提取左面 (x10 的面)TopoDS_Face faceFromShape2findFaceByPosition(shape2,10.0);// 3. 核心步骤使用 BRepTools_ReShape 记录替换请求BRepTools_ReShape reshaper;// 记录一个替换规则在后续应用中将 faceFromShape2 替换为 faceFromShape1reshaper.Replace(faceFromShape2,faceFromShape1);// 4. 应用替换规则生成新的 shape2// Apply 方法会遍历 shape2 的拓扑结构执行所有记录的替换请求并返回一个新形状[citation:2][citation:7]TopoDS_Shape newShape2reshaper.Apply(shape2);Orientation概念ACIS或Parasolid中也有此概念Face的方向一定朝着远离内部的方向Orientation便可以衡量Face的方向与底层Surface曲面方向的同异Loop/Coedge的方向要依赖内环和外环的区分满足右手法则Orientation便可以衡量Coedge和Edge的方向的同异而Edge的方向决定了Vertex的顺序但这又和底层Cuver的方式存在同异问题。总而言之各级拓扑均有Orientation的概念取值true或false最终衡量拓扑方向与底层几何方向的同异。occt的Orientation也是衡量拓扑方向与底层几何方向的同异但它更为复杂取值有四种分别是FORWARD、REVERSED、INTERNAL 和 EXTERNAL它们表示的是“材料或实体位于边界拓扑元素的哪一侧”。拓扑的“内部方向”是哪里Vertex顶点Vertex的内部定义并不直观因为顶点本身是零维点没有两侧的概念。在边的参数空间中顶点将边分为之前和之后两个区域默认内部即FORWARD方向指的是沿着边的自然参数增长方向。Edge边Edge的内部定义基于其所在的曲面Face的二维参数空间。默认内部即FORWARD方向是沿着边的自然方向位于曲面法向与边切向的叉积所指向的左侧区域即前进方向的左侧区域。Face默认内部即FORWARD方向是位于曲面法向的负侧四种取值FORWARD (正向)表示“内部方向”就是材料侧。这是最常见、最符合直觉的状态REVERSED(反向)表示“内部方向”是空白侧其反方向是材料侧INTERNAL(内部)表示“内部方向”及其反方向都是材料侧例如一个内部面EXTERNAL (外部)表示“内部方向”及其反方向都是空白侧。这通常用于表示一个“线框”模型中的孤立元素它不定义任何“材料侧”方向值的上下文特点一个Shape的Orientation取值取决于从哪个视角父形状来看待。TopExp_Explorer在遍历子形状时会基于父形状来设置子形状的方向值。OCCT的官方建议也非常明确如果你需要基于几何数据如曲面进行精确计算应该先将形状“归一化”到FORWARD状态以避免被方向标志干扰。许多occt算法对象返回的Shape例如History记录的Shape其方向都是FORWARD状态。当想获取Face的内环和外环时要先将Face转成FORWARD状态然后再TopExp_Explorer遍历获取Wire方向值为FORWARD便是外环方向值为REVERSED即为内环。小结整体而言occt的Shape是一个“积木”结构“整体”只关心自己可由哪些“组件”构成不关心“组件”是否还被其它“整体”使用。同时一个“整体”中不能有两个完全相同的“组件”但可以方向不同。它的这种高度自由的状态天然支持各种非流行结构但需要用户自行去识别各种“异常”场景远不如商业内核易用。