OpenCascade源码分析之BRepMesh_IncrementalMesh(网格离散化操作)

一、引言

在使用opencascade读取连续曲面模型的时候,一般来说我们都会调用BRepMesh_IncrementalMesh对其进行离散化,然而可能对于离散过程的执行还是没有什么概念,接下来从源码出发来看看它进行了什么操作。

二、源码分析

在调用离散的时候,我们一般会用到下面的代码

TopoDS_Shape cur;    //需要离散的拓扑
IMeshTools_Parameters aMeshParams;  //离散化的参数
BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh(cur, this->aMeshParams);

BRepMesh_IncrementalMesh.cpp 的代码为

//=======================================================================
//function : Constructor
//purpose  :
//=======================================================================
BRepMesh_IncrementalMesh::BRepMesh_IncrementalMesh(const TopoDS_Shape&          theShape,const IMeshTools_Parameters& theParameters,const Message_ProgressRange& theRange): myParameters(theParameters)
{myShape = theShape;Perform(theRange);
}
//=======================================================================
//function : Perform
//purpose  :
//=======================================================================
void BRepMesh_IncrementalMesh::Perform(const Message_ProgressRange& theRange)
{//新建一个上下文//myParameters.MeshAlgo返回了参数中的IMeshTools_MeshAlgoType//如果没有设置IMeshTools_MeshAlgoType默认为Delaunay三角化Watson方法//也可以设置为Delabella方法Handle(BRepMesh_Context) aContext = new BRepMesh_Context (myParameters.MeshAlgo);//theRange消息进度条,没什么用 不用管Perform (aContext, theRange);
}//=======================================================================
//function : Perform
//purpose  :
//=======================================================================
void BRepMesh_IncrementalMesh::Perform(const Handle(IMeshTools_Context)& theContext, const Message_ProgressRange& theRange)
{//对于我们自己设置的离散参数进行判断,修改一些不合理的值//例如当Deflection<Precision::Confusion()时,提醒用户值无效 精度太低initParameters();//IMeshTools_Context继承至IMeshData_Shape//IMeshData_Shape中的SetShape()设置了成员变量TopoDS_Shape myShapetheContext->SetShape(Shape());//设置离散参数theContext->ChangeParameters()            = myParameters;// 算法结束时不清理临时的数据类型theContext->ChangeParameters().CleanModel = Standard_False;//进度条的设置Message_ProgressScope aPS(theRange, "Perform incmesh", 10);//创建一个IMeshTools_MeshBuilder对象,将上下文传入IMeshTools_MeshBuilder aIncMesh(theContext);//执行操作//这边是核心部分!!将会放到后面单独讲解//这边是核心部分!!将会放到后面单独讲解//这边是核心部分!!将会放到后面单独讲解aIncMesh.Perform(aPS.Next(9));if (!aPS.More()){myStatus = IMeshData_UserBreak;return;}myStatus = IMeshData_NoError;//返回了上下文中的成员变量Handle (IMeshData_Model)  myModel;const Handle(IMeshData_Model)& aModel = theContext->GetModel();if (!aModel.IsNull()){//遍历所有的面for (Standard_Integer aFaceIt = 0; aFaceIt < aModel->FacesNb(); ++aFaceIt){//IMeshData是一个namespace具体里面包含的东西在下面的链接中//https://dev.opencascade.org/doc/refman/html/namespace_i_mesh_data.htmlconst IMeshData::IFaceHandle& aDFace = aModel->GetFace(aFaceIt);myStatus |= aDFace->GetStatusMask();for (Standard_Integer aWireIt = 0; aWireIt < aDFace->WiresNb(); ++aWireIt){const IMeshData::IWireHandle& aDWire = aDFace->GetWire(aWireIt);myStatus |= aDWire->GetStatusMask();}}}aPS.Next(1);//设置已经完成标志setDone();
}

总体流程就是如上啦,当然 其中的核心代码还没有讲,接下来讲核心代码

aIncMesh.Perform(aPS.Next(9));   //核心代码!!

Perform中的内容如下

void IMeshTools_MeshBuilder::Perform (const Message_ProgressRange& theRange)
{//清除状态位ClearStatus ();//获得之前设置的上下文const Handle (IMeshTools_Context)& aContext = GetContext ();if (aContext.IsNull ()){SetStatus (Message_Fail1);return;}Message_ProgressScope aPS(theRange, "Mesh Perform", 10);//开始离散化的六个步骤 这边的if中每个都执行了相应的步骤,然后返回是否成功if (aContext->BuildModel ()){if (aContext->DiscretizeEdges ()){if (aContext->HealModel ()){if (aContext->PreProcessModel()){if (aContext->DiscretizeFaces(aPS.Next(9))){if (aContext->PostProcessModel()){SetStatus(Message_Done1);}else{SetStatus(Message_Fail7);}}else{if (!aPS.More()){SetStatus(Message_Fail8);aContext->Clean();return;}SetStatus(Message_Fail6);}}else{SetStatus(Message_Fail5);}}else{SetStatus(Message_Fail4);}}else{SetStatus (Message_Fail3);}}else{const Handle (IMeshTools_ModelBuilder)& aModelBuilder =aContext->GetModelBuilder ();if (aModelBuilder.IsNull ()){SetStatus (Message_Fail1);}else{// Is null shape or another problem?SetStatus (aModelBuilder->GetStatus ().IsSet (Message_Fail1) ?Message_Warn1 : Message_Fail2);}}aPS.Next(1);aContext->Clean ();
}

注:下面的详细步骤 都在BRep_XXXX中实现,IMeshTools_ModelAlgo只是作为基类存在,其中的函数都是virtual的

1、aContext->BuildModel () 创建一个需要离散化的模型

此阶段建立了一个模型

virtual Standard_Boolean BuildModel (){if (myModelBuilder.IsNull()){return Standard_False;}//创建一个需要离散化的模型的HandlemyModel = myModelBuilder->Perform(GetShape(), myParameters);return !myModel.IsNull();}

Peform执行了下面的代码

//! Exceptions protected method to create discrete model for the given shape.
//! Returns nullptr in case of failure.
Handle (IMeshData_Model) Perform (const TopoDS_Shape&          theShape,const IMeshTools_Parameters& theParameters){ClearStatus ();try{OCC_CATCH_SIGNALS//返回模型Handle (IMeshData_Model)return performInternal (theShape, theParameters);}catch (Standard_Failure const&){SetStatus (Message_Fail2);return NULL;}}

performInternal

Handle (IMeshData_Model) BRepMesh_ModelBuilder::performInternal (const TopoDS_Shape&          theShape,const IMeshTools_Parameters& theParameters)
{Handle (BRepMeshData_Model) aModel;//包围盒Bnd_Box aBox;//给模型添加包围盒BRepBndLib::Add (theShape, aBox, Standard_False);if (!aBox.IsVoid ()){// Build data model for further processing.aModel = new BRepMeshData_Model (theShape);//Relative为是否启用相对计算的边缘公差if (theParameters.Relative){Standard_Real aMaxSize;//获取给定包围盒的最大维度BRepMesh_ShapeTool::BoxMaxDimension (aBox, aMaxSize);aModel->SetMaxSize(aMaxSize);}else{//用于面的挠度将是其边缘和面中的最大挠度aModel->SetMaxSize(Max(theParameters.Deflection,theParameters.DeflectionInterior));}//通过添加面孔和自由边缘来构建形状的离散模型。//计算相应形状的挠度,并检查它是否适合现有的多边形表示形式。Handle (IMeshTools_ShapeVisitor) aVisitor =new BRepMesh_ShapeVisitor (aModel);IMeshTools_ShapeExplorer aExplorer (theShape);aExplorer.Accept (aVisitor);SetStatus (Message_Done1);}else{SetStatus (Message_Fail1);}return aModel;
}

2、aContext->DiscretizeEdges ()对边进行离散

  virtual Standard_Boolean DiscretizeEdges(){if (myModel.IsNull() || myEdgeDiscret.IsNull()){return Standard_False;}// Discretize edges of a model.return myEdgeDiscret->Perform(myModel, myParameters, Message_ProgressRange());}

同样的Perform调用了BRepMesh_EdgeDiscret.cxx中的performInternal

Standard_Boolean BRepMesh_EdgeDiscret::performInternal (const Handle (IMeshData_Model)& theModel,const IMeshTools_Parameters&    theParameters,const Message_ProgressRange&    theRange)
{(void )theRange;myModel      = theModel;myParameters = theParameters;if (myModel.IsNull()){return Standard_False;}//For中的格式为: begin,end,func,isParallel//多线程或者单线程跑OSD_Parallel::For (0, myModel->EdgesNb (), *this, !myParameters.InParallel);myModel.Nullify(); // Do not hold link to model.return Standard_True;
}

具体的代码太长了 感兴趣的话 阅读BRepMesh_EdgeDiscret.cxx吧

3、aContext->HealModel () 修复模型

virtual Standard_Boolean HealModel(){if (myModel.IsNull()){return Standard_False;}return myModelHealer.IsNull() ?Standard_True :myModelHealer->Perform (myModel, myParameters, Message_ProgressRange());}

这块的具体实现在BRepMesh_ModelHealer.hxx和BRepMesh_ModelHealer.cxx中,感兴趣的可以自己进行阅读
其中主要的功能有下:

  • connectClosestPoints 将非常近的点进行合并
  • fixFaceBoundaries 修复面的边界(边界可能会出现断连的情况)

4、aContext->PreProcessModel() 前处理

和上面一样的Perform函数 然后调用了performInternal

Standard_Boolean BRepMesh_ModelPreProcessor::performInternal(const Handle(IMeshData_Model)& theModel,const IMeshTools_Parameters&   theParameters,const Message_ProgressRange&   theRange)
{(void )theRange;if (theModel.IsNull()){return Standard_False;}const Standard_Integer aFacesNb    = theModel->FacesNb();const Standard_Boolean isOneThread = !theParameters.InParallel;//并行运行或者单线程运行//对锥面进行分割 对所有3d的曲线以及参数曲线进行分割和缝合OSD_Parallel::For(0, aFacesNb, SeamEdgeAmplifier        (theModel, theParameters),                      isOneThread);//判断三角化的一致性 判断是否有过期的三角数据OSD_Parallel::For(0, aFacesNb, TriangulationConsistency (theModel, theParameters.AllowQualityDecrease), isOneThread);// 从过时的多边形中清理边缘和面.Handle(NCollection_IncAllocator) aTmpAlloc(new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE));NCollection_Map<IMeshData_Face*> aUsedFaces(1, aTmpAlloc);for (Standard_Integer aEdgeIt = 0; aEdgeIt < theModel->EdgesNb(); ++aEdgeIt){const IMeshData::IEdgeHandle& aDEdge = theModel->GetEdge(aEdgeIt);if (aDEdge->IsFree()){if (aDEdge->IsSet(IMeshData_Outdated)){TopLoc_Location aLoc;BRep_Tool::Polygon3D(aDEdge->GetEdge(), aLoc);BRepMesh_ShapeTool::NullifyEdge(aDEdge->GetEdge(), aLoc);}continue;}for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb(); ++aPCurveIt){// Find adjacent outdated face.const IMeshData::IFaceHandle aDFace = aDEdge->GetPCurve(aPCurveIt)->GetFace();if (!aUsedFaces.Contains(aDFace.get())){aUsedFaces.Add(aDFace.get());if (aDFace->IsSet(IMeshData_Outdated)){TopLoc_Location aLoc;const Handle(Poly_Triangulation)& aTriangulation =BRep_Tool::Triangulation(aDFace->GetFace(), aLoc);// Clean all edges of oudated face.for (Standard_Integer aWireIt = 0; aWireIt < aDFace->WiresNb(); ++aWireIt){const IMeshData::IWireHandle& aDWire = aDFace->GetWire(aWireIt);for (Standard_Integer aWireEdgeIt = 0; aWireEdgeIt < aDWire->EdgesNb(); ++aWireEdgeIt){const IMeshData::IEdgeHandle aTmpDEdge = aDWire->GetEdge(aWireEdgeIt);BRepMesh_ShapeTool::NullifyEdge(aTmpDEdge->GetEdge(), aTriangulation, aLoc);}}BRepMesh_ShapeTool::NullifyFace(aDFace->GetFace());}}}}return Standard_True;
}

总结一下 前处理基本上是对一些过期的三角数据和边数据进行清理

5、aContext->DiscretizeFaces(aPS.Next(9)) 面的离散

这里的算法比较庞大,基本上是根据平面不同的类型来执行不同的算法

switch (theSurfaceType){case GeomAbs_Plane:return theParameters.InternalVerticesMode ?new NodeInsertionMeshAlgo<BRepMesh_DefaultRangeSplitter>::Type :new BaseMeshAlgo::Type;break;case GeomAbs_Sphere:return new NodeInsertionMeshAlgo<BRepMesh_SphereRangeSplitter>::Type;break;case GeomAbs_Cylinder:return theParameters.InternalVerticesMode ?new NodeInsertionMeshAlgo<BRepMesh_CylinderRangeSplitter>::Type :new BaseMeshAlgo::Type;break;case GeomAbs_Cone:return new NodeInsertionMeshAlgo<BRepMesh_ConeRangeSplitter>::Type;break;case GeomAbs_Torus:return new NodeInsertionMeshAlgo<BRepMesh_TorusRangeSplitter>::Type;break;case GeomAbs_SurfaceOfRevolution:return new DeflectionControlMeshAlgo<BRepMesh_BoundaryParamsRangeSplitter>::Type;break;default:return new DeflectionControlMeshAlgo<BRepMesh_NURBSRangeSplitter>::Type;}

这里可以读一下这个大佬的文章,总结的还不错
https://blog.csdn.net/YongsenDY/article/details/115304488

6、aContext->PostProcessModel() 后处理

后处理将三角剖分中的多边形存储到TopoDS_Edge
具体的源码在BRepMesh_ModelHealer.cxx中可以自行查看

三、总结

离散的步骤主要如下:

  1. 设置离散的参数
  2. 执行IncrementalMesh算法开始离散
  3. 设置上下文的参数(包括用什么算法离散、以及初始化六个步骤用到的类的实例)
  4. 开始离散
    4.1. 创建离散化模型
    4.2. 对边进行离散
    4.3. 修复模型
    4.4. 前处理
    4.5. 对不同类型的面用不同的算法离散
    4.6. 后处理,将数据存储到TopoDS上

OpenCascade源码分析之BRepMesh_IncrementalMesh(网格离散化操作)相关推荐

  1. UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件

    系列 UGUI源码分析系列总览 相关前置: UGUI CanvasUpdateSystem源码分析 UGUI源码分析:LayoutSystem布局系统 UGUI源码分析:LayoutGroup中的纵横 ...

  2. 【Android 插件化】VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )

    文章目录 一.目前的 API 现状 二.安装应用源码分析 1.安装按钮执行的操作 2.返回到 HomeActivity 执行的操作 一.目前的 API 现状 下图是 VirtualApp 官方给出的集 ...

  3. LDD3源码分析之时间与延迟操作

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 编译环境:Ubuntu 10.10 内核版本:2.6.32-38-generic-pae LDD3源码路径:exam ...

  4. 英雄远征Erlang源码分析(11)-物品相关操作解析

    游戏中,玩家物品的管理是通过每个玩家独有的物品管理进程,结合ETS缓存表实现的. 物品相关模块有mod_goods.erl,lib_goods.erl,goods_util.erl和lib_make. ...

  5. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),Con ...

  6. Netty技术细节源码分析-FastThreadLocal源码分析

    本文是该篇的修正版 本文的github地址:点此 Netty 的 FastThreadLocal 源码解析 该文中涉及到的 Netty 源码版本为 4.1.6. Netty 的 FastThreadL ...

  7. [unity3d]recast navigation navmesh 导航网格 寻路算法 源码分析

    recast navigation navmesh导航网格算法源码分析 Author:  林绍川 recast navigation navmesh是unity3d ue4内置的寻路算法 本文为了方便 ...

  8. Opencascade源码学习之模型数据

    Opencascade源码学习之模型数据 1.模型数据 2.几何工具 1.插值和拟合 1.分析一组点 2.基本插值和近似 3.2D 插值 4.3D 插值 5.2D 拟合 6.3D 拟合 7.曲面拟合 ...

  9. Opencascade源码学习之模型数据——TKGeomBase模块文件介绍

    Opencascade源码学习之模型数据--TKGeomBase模块文件介绍 1.AdvApp2Var 2.AppCont 3.AppDef 4.AppParCurves 5.Approx 6.Bnd ...

最新文章

  1. 发布 SharePoint Server 2007 Starter Page
  2. 在本机快速创建YUM源
  3. 关于img图片的onerror属性
  4. eclipse创建android工程,在eclipse创建android 工程
  5. ASMSupport教程4.2
  6. 学习韩立刚老师IT运维课程,成为韩立刚老师正式学生,在全国范围为你就近推荐工作。...
  7. C BackgroundWorker类详细说明
  8. PHP 获取服务器详细信息
  9. 为啥Redis/Mongo这么快,就不能直接替代mysql吗?
  10. python入门经典100例-【python】编程语言入门经典100例--14
  11. 嵌入式Linux进程信息及内存布局
  12. 仿鱼爪新媒交易账号过户转让平台源码担保第三方账号交易系统公众号服务号抖音快手小红书
  13. word 2010 尾注 尾注序号 连续尾注 尾注分隔符 删除
  14. SQL Server跟踪(Trace)--系统跟踪日志;从小白到大神,文章细节满满,细致到令人发指。
  15. Linux 下检查 VT-d / IOMMU 是否开启
  16. 2022年11月10篇论文推荐
  17. Android7.1.1 remap鼠标右键为返回键
  18. 回文是指正读反读均相同的字符序列,如“abba”和“abdba”均是回文但“good”不是回文,试写一个算法判断给定字符是否为回文。
  19. 什么是IO流?怎样区分不同种类的IO流?
  20. java移位运算符详解

热门文章

  1. CPU的指令集(指令系统)
  2. 步进电机的抖动和噪音从何而来 如何使步进电机完全静音
  3. 【HTML505】HTML基础05_区块_布局
  4. DOC与DOCX的区别
  5. java iterator 转 list_JAVA Iterator 转成 List
  6. 制造业大量招程序员,是拧螺丝吗?
  7. 微信小程序使用qrcode生成二维码(可用于微信收款)
  8. sklearn机器学习:岭回归Ridge
  9. AJAX聊天室无刷新技术方案
  10. 【JS】Day34-35