Mesh presentations
Open CASCADE除了支持3D物体的精确的几何表示,还提供了处理网格形式物体的三角剖分表示的函数。
网格函数提供了:
- 存储网格数据的数据结构, 处理这些数据的基本算法
- 构造三角网格的数据结构和算法
- 扩展Open CASCADE的3D可视化功能的工具,显示网格,现实相关的前后处理的数据
Open CASCADE包含两个网格转换器:
- VRML(虚拟现实建模语言)转换器。转换后可以有两种表示形式:shaded或者wireframe. 前者将形状表示为一组由网格算法得出的三角形,后者将形状表示为曲线的集合。
- STL转换器。
Mesh algorithm
形状三角形化的算法由BRepMesh_IncrementalMesh类提供,其将形状的三角网格化添加到拓扑数据结构中。三角网格化用来显示形状。
#include <IMeshData_Status.hxx>
#include <IMeshTools_Parameters.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
Standard_Boolean meshing_explicit_parameters()
{
const Standard_Real aRadius = 10.0;
const Standard_Real aHeight = 25.0;
BRepPrimAPI_MakeCylinder aCylinder(aRadius, aHeight);
TopoDS_Shape aShape = aCylinder.Shape();
const Standard_Real aLinearDeflection = 0.01;
const Standard_Real anAngularDeflection = 0.5;
BRepMesh_IncrementalMesh aMesher (aShape, aLinearDeflection, Standard_False, anAngularDeflection, Standard_True);
const Standard_Integer aStatus = aMesher.GetStatusFlags();
return !aStatus;
}
Standard_Boolean meshing_imeshtools_parameters()
{
const Standard_Real aRadius = 10.0;
const Standard_Real aHeight = 25.0;
BRepPrimAPI_MakeCylinder aCylinder(aRadius, aHeight);
TopoDS_Shape aShape = aCylinder.Shape();
IMeshTools_Parameters aMeshParams;
aMeshParams.Deflection = 0.01;
aMeshParams.Angle = 0.5;
aMeshParams.Relative = Standard_False;
aMeshParams.InParallel = Standard_True;
aMeshParams.MinSize = Precision::Confusion();
aMeshParams.InternalVerticesMode = Standard_True;
aMeshParams.ControlSurfaceDeflection = Standard_True;
BRepMesh_IncrementalMesh aMesher (aShape, aMeshParams);
const Standard_Integer aStatus = aMesher.GetStatusFlags();
return !aStatus;
}
默认的网格算法有两个主要的选择,线性偏移和角度偏移。线性偏移限制了曲线和细分曲面之间的距离,角度偏移限制了折线段中子段之间的角度。
第一步,根据特定的参数离散一个面上所有的边;
第二步,细分面。
控制面内部的网格化有其他的选择:DeflectionInterior和AngleInterior。前者限制了三角形和面的距离,后者(只用于b样条面的细分)限制了三角形每个顶点上法线(N1,N2,N3)之间的角度。在边界边上的顶点,使用额外的Angular deflection.
注意,如果给定的线性偏移值小于形状公差,算法会考虑形状公差而不是该线性偏移值。
应用程序应该给出偏移参数,以计算出一个满意的网格。
Angular Deflection相对简单,允许使用默认值(12-20度)。线性偏移具有绝对意义,应用程序需要针对模型给出正确的值。给定一个比较小的值,可能会导致很大的网格,会占用很多内存,进而导致需要很大的计算量和很慢的渲染速度;给定一个比较大的值,可能会导致很丑陋复杂的网格。
BRepMesh Architecture
Goals
- 消除掉数据结构、辅助工具和算法之间紧密的连接,创建一个可扩展的方案,易于维护和改良;
- 将某些具体操作的代码分开放在多个功能模块,有利于简化调试和可读性;
- 引入新的数据结构,使得操作一个特定实体(边、线、面)的离散模型成为可能,以便执行局部的计算,而不是运行整个模型;
- 实现一个新的三角剖分算法,取代现有的功能,其中包含需要移到上层的过于复杂的解决方案。此外,提供了根据表面类型改变算法的可能性(最初是为了加速平面网格划分)。
General workflow
- Creation of model data structure:传递给算法的源TopoDS_Shape被分析分解为面和边。在数据模型中创建了对应每个拓扑实体的反射。请注意,底层算法使用数据模型作为输入,并通过公共接口访问它,该接口允许创建自定义数据模型,并在特定实体之间建立必要的依赖关系(参见"Data model interface");
- Discretize edges 3D & 2D curves:为了创建一个连贯的骨架作为面网格划分的基础,对每个模型边的三维曲线以及相关的二维曲线集进行离散。如果源形状的边已经包含适合指定参数的多边形数据,则从该形状中提取并按原样存储在模型中。每条边单独处理,不考虑邻接边;
- Heal discrete model:源TopoDS_Shape可能会在模型的设计、交换或修改过程中引入问题,例如开放连接或自交叉。此外,粗略离散的边会引入自交等问题。该阶段负责对离散模型进行分析,以便发现和修复问题,或在问题无法解决的情况下拒绝对模型零件进行进一步处理;
- Preprocess discrete model:定义在网格面之前执行的特定于实现方法的动作。默认情况下,该操作在模型面上迭代进行,检查现有三角剖分的一致性,在不一致的情况下从多边形数据中清除拓扑面和相邻边,或将离散模型的一个面标记为不需要计算的;
- Discretize faces:表示基于2D离散数据为特定面执行网格生成的核心部件。该操作将与面的边相关的多边形数据缓存到数据模型中进行进一步处理,并将生成的网格存储到TopoDS_Face中;
- Postprocess discrete model:为在网格面之后执行的实现方法定义的特定的动作。默认情况下,该操作将前一阶段获得的多边形数据存储到源模型的TopoDS_Edge对象中。
Common interfaces
组成结构包含两个单元:IMeshData(参照Data model interface)和IMeshTools,相应的为数据模型和算法工具定义了公共接口。IMeshTools_Context类表示这些单元之间的连接器。该类缓存了数据模型和对应于工作流六个阶段中的工具,提供了安全调用相应工具的方法(与IntTools_ModelAlgo的设计相似,为了与OCCT核心工具保持一致性)。所有的阶段,除了第一阶段,使用数据模型作为输入,在整个结构上执行了特定的动作。因此,为了统一操作数据模型的接口,定义了API类IMeshTools_ModelAlgo。勇于处理数据模型的每个工具应该继承这个接口,使得能够在上下文中缓存它。与其他类不同的是,模型构建器接口是由另一个类IMeshTools_ModelBuilder定义的,因为这个阶段的含义不同。启动整个工作流的入口点由IMeshTools_MeshBuilder表示。
IMeshTools_Context的默认实现在BRepMesh_Context类中给出,通过默认算法工具的实例来初始化上下文。
工厂接口IMeshTools_MeshAlgoFactory提供了为特定曲面改变三角剖分算法的可能性。根据作为参数传递的表面类型,工厂通过IMeshTools_MeshAlgo接口返回三角剖分算法的实例。在离散化阶段使用。
AlgoFactory的默认实现在BRepMesh_MeshAlgoFactory中给出,根据传递的表面类型选择不同复杂度的返回算法。反过来,它被用作brepmesh_facediscrete算法的初始化器,表示面离散化阶段的开始(启动器)。
- IMeshTools_CurveTessellator:为算法提供一个公共接口,负责在3D和2D多边形曲线上创建离散多边形,创建能够从TopoDS_Edge提取现存多边形的工具,无论实现细节如何,能够在曲线上获得离散点和相应的参数(查看使用派生类的例子BRepMesh_CurveTessellator, 在BRepMesh_EdgeDiscret中的BRepMesh_EdgeTessellationExtractor);
- IMeshTools_ShapeExplorer:最后两个接口表示访问者设计模式,目的是将拓扑形状元素(边和面)的迭代与在特定元素上执行的操作分开;
- IMeshTools_ShapeVisitor:提供对目标拓扑形状的边和面进行操作的通用接口。它可以与IMeshTools_ShapeExplorer一起使用。BRepMesh_ShapeVisitor中可用的默认实现执行数据模型的初始化。这种方法的优点是,可以根据特定的数据模型更改IMeshTools_ShapeVisitor的实现,而shape explorer的实现保持不变。
Create model data structure
旨在保留基础算法所需的离散和临时数据的数据结构是网格划分步骤的第一阶段创建的。通常,模型表示适合目标任务的源拓扑形状的实体之间的依赖性。
Data model interface
IMeshData单元提供了用于整个工作流的不同阶段的具体数据模型API。IMeshData_Shape作为保持拓扑形状的所有数据结构的基础类和工具,为了避免可能存在的复制-粘贴。
接口的默认实现方式在BRepMeshData单元中给出。默认数据模型的主要目标是提供以并行模式对边缘离散化的功能。因此,curve、pcurve和其他类是基于STL的,像NCollection这样的smart-pointers在某些情况下不提供线性安全性。此外,它密切反映了源形状的拓扑结构,即数据模型中的边数与源模型中的边数相等;每条边包含一组与其相邻面相关联的pcurves,使得在单独的线程中能够为所有pcurves或特定边的3D曲线创建离散多边形。
优点:在必要的情况下,数据模型(可能带有用于处理的算法)可以很容易地被另一种实现所替代,这种。现支持元素之间另一种依赖关系。
另一种例子是一个不同的数据模型,不需要在相邻面之间创建同步化的离散多边形的网格,也就是说,在必要的情况下,仅仅为了可视化或者快速计算时,加速创建一个几乎每个面都被细分的细分曲面(the approach used in XDEDARW_Props)。
Collecting data model
在这个阶段,数据模型由实体根据源形状的拓扑结构填充。数据模型的默认实现在BRepMeshData单元中给出,并将模型表示为两个集合:一组边和一组面。每个面由一个或几个wires组成,其中第一个总是代表外部wire,而其他的是内部wire。每根wire依次描述有向边的有序序列。每条边的特征是一条3D曲线和零条(在自由边的情况下)或更多与这条边相邻的面相关的2D曲线。三维曲线和二维曲线分别表示定义在参考面对应的三维和二维空间中的一组点的参数对。曲线和pcurve之间的另一个区别是,pcurve有一个对它所定义的面的引用。
模型填充算法由BRepMesh_ShapeVisitor类表示,在BRepMesh_ShapeExplorer的帮助下,在目标形状的边和面上执行迭代,创建模型作为拓扑形状的反射。请注意,算法在数据模型的公共接口上操作,并创建了一个结构,而不需要了解实现细节和底层数据结构。collecting functionality的入口点是BRepMesh_ModelBuilder类。
Discretize edges 3D & 2D curves
在这个阶段,只考虑数据模型的边。每条边都被单独处理(有可能在多个线程中运行处理)。检查边是否有已存在的多边形数据。如果至少有一个表示存在并适合网格参数,它将被复原并用作对整组pcurves和边对应的3D曲线细分的参考数据(参见BRepMesh_EdgeTessellationExtractor)。否则,将创建一个新的细分算法并使用该算法来生成初始多边形(参见BRepMesh_CurveTessellator),并且边缘将被标记为过时的。此外,还对模型边进行了偏移更新,重新计算了相同的距离、相同的参数和简并参数。有关实现细节,请参阅BRepMesh_EdgeDiscrete。
IMeshData单元定义接口IMeshData_ParametersListArrayAdaptor,用于将任意数据结构适应于NCollection_Array1容器API。该解决方案使用NCollection_Array1和IMeshData_Curve作为BRepMesh_EdgeParameterProvider工具的源代码,旨在考虑生成相同参数属性的一致参数化。
Heal discrete model
一般来说,这一阶段代表在整个离散模型上执行的一组操作,以解决由于设计、转换或粗略离散化引起的问题而引起的不一致性。根据目标三角剖分算法,可以执行不同的操作序列,例如,处理自交有不同的方法,要么通过降低目标精度来放大边离散化,要么在相交点分割链接。在这一阶段,将整个边集综合考虑,并考虑它们的邻接性。在BRepMesh_ModelHealer中给出了模型处理的默认实现,它执行以下操作:
- 在模型面上迭代并检查wires的一致性,即wires是否闭合且不包含自交点。数据结构的设计避免了冲突,因此可以在并行模式下运行该过程;
- 强制连接参数空间中相邻边的两端,闭合可能断开的部分之间的间隙。该操作的目的是创建一个相对于目标面的参数空间定义的正确离散模型。 这意味着不需要进行具体的计算来确定U和V容差;
- 在形成面形状的边上创建交点。解决自交有两种可能的解决方案:
- 减小特定边的偏差,更新其离散模型。然后工作流中"intersection check - amplification"重复5次。结果是,目标边包含更精细的细分,继续网格化或者该面被标记为IMeshData_SelfIntersectingWire状态,拒绝后续操作;
- 通过交点分割目标边,并同步更新与每个边关联的带有曲线和pcurves的多边形。与放大过程相比,该操作提供了一个更健壮的解决方案,且结果有保证,但从同步功能的角度来看,实现起来更困难。
Preprocess discrete model
这个阶段实现了在面网格化之前执行的动作。根据目标的不同,它可以被改变或省略。默认情况下,BRepMesh_ModelPreProcessor实现了检查现有三角形的拓扑面一致性的功能,即与目标偏移参数的一致性;三角形引用的节点索引不超过三角形剖分中存储的节点数量。如果该面未能通过某些检查,则从三角剖分中清除该面,并从现有多边形中清除其相邻边。这并不影响离散模型,也不需要任何重新计算,因为除了其多边形一致性,模型保存了整个边集的细分。
Discretize faces
面的离散化是网格划分算法的一般部分。在此阶段,利用前面步骤获得并处理的边细分数据,形成目标面的轮廓,并作为三角剖分算法的输入。默认实现是由BRepMesh_FaceDiscret类提供的,它代表了三角剖分算法的开始。它遍历数据模型中所有可用的面,通过IMeshTools_MeshAlgoFactory,根据与每个面相关联的曲面类型创建一个三角剖分算法的实例,并执行它。每个面分别处理,因此可以以并行方式进行。面的离散化的类图如下图所示。(太大,看原网址吧)
面的网格剖分算法有以下结构:
- BRepMesh_BaseMeshAlgo实现了IMeshTools_MeshAlgo接口和继承算法的基本功能。该类的主要目标是用作为输入参数的面模型数据来初始化BRepMesh_DataStructureOfDelaun的实例以及适用于嵌入算法的辅助数据结构。尽管实现了三角剖分算法,这种结构目前被认为是常见的OCCT。然而,用户可以自由地实现自定义算法和通过IMeshTools_MeshAlgo接口可以访问的支持的数据结构,例如连接不支持TopoDS_Shape离开箱的第三方网格工具。为此,这种结构提供了以插件的形式将连接器分发到各种算法的可能性;
- BRepMesh_DelaunayBaseMeshAlgo和BRepMesh_SweepLineMeshAlgo类实现了直接操作BRepMesh_DataStructureOfDelaun实例的核心网格化功能。算法代表了网格生成工具,从数据结构中添加新的点到最终的网格;
- BRepMesh_NodeInsertionMeshAlgo类表示一个包装器,旨在扩展从BRepMesh_BaseMeshAlgo继承的算法,以支持生成表面节点并将它们插入到结构中的功能。在这个层次上,创建了一个分类工具的实例,可以用来接受-拒绝内部节点。此外,将点的UV坐标缩放到相应方向的指定范围所需的计算也被执行。由于这两种三角剖分算法都在结构提供的静态数据上工作,因此添加新的节点是在初始化阶段。表面节点由一种叫做距离分配器的辅助工具生成,并作为模板参数传递(参见Range spitter)。
- 类BRepMesh_DelaunayNodeInsertionMeshAlgo和BRepMesh_SweepLineNodeInsertionMeshAlgo实现算法特定的功能,增加内部节点,补充由BRepMesh_NodeInsertionMeshAlgo提供的功能;
- BRepMesh_DelaunayDefectionControlMeshAlgo扩展了BRepMesh_DelaunayNodeInsertionMeshAlgo的功能,通过额外的过程控制生成的三角形
BRepMesh为用户提供了一种将默认三角剖分算法切换到自定义算法的方法,该算法可以由用户实现,也可以在全局范围内使用。目前有三个基类可以用于集成第三方算法:
- BRepMesh_ConstrainedBaseMeshAlgo基类,用于提供带约束的三角形的生成工具,不需要BRepMesh的共同处理;
- BRepMesh_CustomBaseMeshAlgo为不支持约束的通用算法提供入口点,并假定只用于生成基本网格。在第三方求解器生成的背景网格上,使用组件本身提供的标准功能处理约束边;
- BRepMesh_CustomDelaunayBaseMeshAlgo包含由BRepMesh使用的工具的初始化部分,用于使用第三方算法的结果进行检查或优化。
网格算法可以通过实现IMeshTools_MeshAlgoFactory和相关接口来提供,并将其传递给BRepMesh_Context:: setfacediscrete()。OCCT有两种基本的2D网格算法:BRepMesh_MeshAlgoFactory(默认使用)和BRepMesh_DelabellaMeshAlgoFactory。
该例子表明了在Draw环境下如何完成:
psphere s 10
### Default Algo ###
incmesh s 0.0001 -algo default
### Delabella Algo ###
incmesh s 0.0001 -algo delabella
下面的代码片段展示了传递自定义网格划分到BRepMesh_IncrementalMesh:
IMeshTools_Parameters aMeshParams;
Handle(IMeshTools_Context) aContext = new BRepMesh_Context();
aContext->SetFaceDiscret (new BRepMesh_FaceDiscret (new BRepMesh_DelabellaMeshAlgoFactory()));
BRepMesh_IncrementalMesh aMesher;
aMesher.SetShape (aShape);
aMesher.ChangeParameters() = aMeshParams;
aMesher.Perform (aContext);
Range splitter
范围分割工具提供了定义在使用离散模型数据计算的范围内生成内部表面节点的功能。基本的功能是由BRepMesh_DefaultRangeSplitter提供的,在平面的情况下,可以使用它而不需要修改。默认拆分器不生成任何内部节点。
BRepMesh_ConeRangeSplitter, BRepMesh_CylinderRangeSPlitter和BRepMesh_SphereRangeSplitter是默认拆分器的特化,用于快速生成对应类型的解析曲面的内部节点。
BRepMesh_UVParamRangeSplitter实现了基本功能,将面边界的离散点考虑到节点生成。
它的后继者BRepMesh_TorusRangeSplitter和BRepMesh_NURBSRangeSplitter扩展了相应的环面和NURBS曲面的基本功能。
Postprocess discrete model
这个阶段实现了在面网格化之后执行的动作。根据目标的不同,它可以被改变或省略。默认情况下,BRepMesh_ModelPostProcessor将存储在数据模型中的多边形数据提交到TopoDS_Edge。