﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-eryar-随笔分类-2.OpenCASCADE</title><link>http://www.cppblog.com/eryar/category/17808.html</link><description>PipeCAD - Plant Piping Design Software. &lt;br&gt;
RvmTranslator - Translate AVEVA RVM to OBJ, glTF, etc.</description><language>zh-cn</language><lastBuildDate>Sun, 03 Dec 2023 16:48:40 GMT</lastBuildDate><pubDate>Sun, 03 Dec 2023 16:48:40 GMT</pubDate><ttl>60</ttl><item><title>OpenCASCADE HLR 轮廓线</title><link>http://www.cppblog.com/eryar/archive/2023/12/03/occt_hlr_contour_ana.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 03 Dec 2023 12:53:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/03/occt_hlr_contour_ana.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230218.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/03/occt_hlr_contour_ana.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230218.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230218.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;">OpenCASCADE HLR Quadric Surface Outline Edge</h1>
<p>Key Words: HLR, Outline Edge, Sihouette Edge</p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中关于隐藏线消除HLR算法的描述就是一句话：These algorithms are based on the principle of comparing each edge of the shape to be visualized with each of its faces, and calculating the visible and the hidden parts of each edge. 即根据面判断每条边Edge的遮挡关系，计算出边Edge可见和不可见部分。所以HLR算法的输入主要为边和面，计算遮挡关系依赖线面求交算法。对于精确的HLR算法依赖精确的线面求交算法，PolyAlgo算法依赖多段线与网格求交算法。输入的边中除了BREP中的边以外，还有一类是根据投影方向计算得来的，即外轮廓线Outline，也称为Contour线。ACIS的PHLR中称轮廓线Sihouette Edge。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204423504-1679258260.png" alt="" /></p>
<p>轮廓线的计算是HLR中比较关键的一步，本文以OpenCASCADE中简单的二次曲面的轮廓线计算入手来理解曲面的轮廓线概念，为理解任意曲面轮廓线计算打下基础。</p>
<h2>2 Outline Builder</h2>
<p>OpenCASCADE的HLR中使用类HLRTopoBRep_OutLiner来计算外轮廓线。轮廓线的计算依赖投影方向及投影方式，主要计算逻辑在函数Fill()中：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204440944-1736661418.png" alt="" /></p>
<p>投影方式主要分为透视投影Perspective和平行投影，工程图生成一般使用平行投影方式。实际计算类是Contap_Contour，在类Contap_Contour中又根据投影曲面类型分为两种类型来处理：</p>
<ul>
<li>二次曲面</li>
<li>任意曲面</li>
</ul>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204448284-503267688.png" alt="" /></p>
<p>其中函数PerformAna()为计算平面、球面、圆柱面、圆锥面的外轮廓线，最终会使用类Contap_ContAna。其中Ana为Analytical解析曲面的意思，这里指能用解析表达式表示的二次曲面。</p>
<h2>3 Contap_ContAna</h2>
<p>类Contap_ContAna能计算球面、圆柱面和圆锥面的外轮廓线Contour，下面我们主要来看看这三类面的外轮廓线计算结果。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204520587-275740948.png" alt="" /></p>
<p>对于平行投影球面会生成以投影方向为法向，以球半径为半径的一个圆，代码如下所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204533447-647385516.png" alt="" /></p>
<p>如下图所示中的绿色的线：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204542473-1297893842.png" alt="" /></p>
<p>对于平行投影圆柱面会生成两条直线，若投影方向与圆柱面法向平行时不生成轮廓线，这时就是使用圆柱体中的上下两个圆的边。代码如下所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204549730-207895601.png" alt="" /></p>
<p>生成的两条直线方向为圆柱面的轴方向：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231203204557306-1576414130.png" alt="" /></p>
<p>圆锥面的轮廓线生成函数逻辑类似，留给读者自行分析理解。</p>
<h2>4 Conclusion</h2>
<p>综上所述，BREP的HLR算法需要计算模型的外轮廓线。如球体的BREP边有两个退化边（极点），及两个重合边，若来投影实质上只有重合边中的一条边有用，而这个边还是个半圆。从理解 简单的二次曲面外轮廓线计算函数入手，再去深入理解任意曲面的外轮廓线计算方法。</p>
<p>理解HLR实现原理，可以重构HLR代码，也可以完全自己动手，开发出满足实际需求的自动出图程序，自动出图是工程类设计软件中相对核心的功能，目前国内基于PDMS做自动出图相关软件开发的就有很多家。本着开放的心态分享这些相对比较关键功能的原理，让国内这些产品能摆脱基于AutoCAD/BricsCAD开发接口或PDMS Draft的限制，开发出更好用、更自由灵活的软件。</p><img src ="http://www.cppblog.com/eryar/aggbug/230218.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-03 20:53 <a href="http://www.cppblog.com/eryar/archive/2023/12/03/occt_hlr_contour_ana.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE-HLR Edge</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_hlr_edges.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:23:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_hlr_edges.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230217.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_hlr_edges.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230217.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230217.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE-HLR Edge</strong></h1>
<h2 style="text-align: left;">1 Introduction</h2>
<p>用计算机生成三维物体的真实图形，是计算机图形学研究的重要内容。真实图形在仿真模拟、几何造型、广告影视和科学计算可视化等许多领域都有着广泛应用。在用显示设备描述物体的图形时，必须把三维信息经过某种投影变换在二维的显示平面上绘制出来。从三维投影到二维的降维操作，会导致图形的二义性。要消除这类二义性，就必须在绘制时消除被遮挡的不可见的线或面，习惯上称之为消除隐藏线Hidden Line Removal和隐藏面Hidden Face Removal。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231202114645317-1936571585.png" alt="" /></p>
<p>这是渲染显示上对消隐的需求，在根据三维模型自动生成工程图的工程设计软件中，对消隐的需求有所不同。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231202114655229-1942560946.png" alt="" /></p>
<p>工程设计软件与机械设计软件不同，工程设计软件一次出图消隐的模型量大，对出图的算法要求主要有：</p>
<ul>
<li>主要使用消隐线算法；</li>
<li>消隐得到的线能找到与三维模型的关系，方便标注模型信息，如模型名称、规格等；</li>
<li>自动标注布局算法，能对标注的名称、规格等自动布局，减少手工调整；</li>
<li>自动尺寸标注；</li>
<li>符号化处理，如管道模型能用一条线符号化处理；</li>
</ul>
<p>其实最后总结成一句话就是一键根据模型生成能交付的图纸。虽然现在技术上具备三维模型下车间的能力，但是目前二维图纸依然是设计交付、加工制造主要依据。工程类设计软件主要的功能就是快速建模，碰撞检测和自动图纸生成。当模型量大时，消隐速度快及自动生成的标注文字排列整齐（或满足工程习惯）成了二维图纸自动生成的核心技术，也是程序处理中的难点。</p>
<h2>2 HLR</h2>
<p>几何内核一般都提供HLR算法，用来根据模型投影生成二维工程图。OpenCASCADE的HLR提供了隐藏线消隐算法。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231202114708483-1108243475.png" alt="" /></p>
<p>https://www.spatial.com/zh/products/cgm-hlr-hidden-line-removal</p>
<p>OpenCASCADE 提供了两种消隐算法：HLRBRep_Algo和HLRBRep_PolyAlgo。这些算法都是基于相同的原理：比较形状每条边相对每个面的可见性，并 计算每条边的可见部分与消隐部分。算法通过计算在指定投影方向上的物体显示特性，去除或标记被面遮挡的边。这两个算法也与一些提取功能配合使用，如重构一 个简化的模型等，简化后新的模型由边组成，就是在投影方向上的轮廓线。</p>
<h2>3 边的分类</h2>
<p>OpenCASCADE的HLR中将边分为以下类型：</p>
<ul>
<li>Sharp Edges</li>
<li>Smooth Edges</li>
<li>Sewn Edges</li>
<li>Outline Edges</li>
<li>Isoparameter Edges</li>
</ul>
<p>从类HLRBRep_HLRToShape和类HLRBRep_PolyHLRToShape中给出了这些边的一些定义。其中Sharp Edge表示C0连续（非G1连续）的边，就是一般Edge；</p>
<p>Smooth Edge表示G1连续（非G2 连续）的边；</p>
<p>Sewn Edge表示G2连续的边；</p>
<p>Outline Edge表示模型的轮廓边，这种类型的边不在BREP数据中，需要根据投影方向生成；</p>
<p>Isoparameter Edge表示面的等参线生成的边，这种类型的边不也不在BREP数据中；</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202312/534255-20231202114734232-65562309.png" alt="" /></p>
<p>其中Sharp Edge、Smooth Edge和Sewn Edge一般都是BREP中的EDGE数据，而Outline Edge和Isoparameter Edge是根据设置额外生成的边。理解边的这些定义，方便对HLR算法进行理解。HLR算法是相对简单的算法，主要是就将上述五种类型的边与面进行求交，判断遮挡关系。</p>
<p>目前OpenCASCADE中的HLR算法代码写得有点乱，上次在深圳ogg的俄罗斯开发人员提到要重构HLR部分的代码。深入理解 HLR算法，为自动生成图纸功能打下基础。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230217.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:23 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_hlr_edges.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE 线面求交</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_intcs.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:23:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_intcs.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230216.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_intcs.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230216.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230216.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;">OpenCASCADE 线面求交</h1>
<p style="text-align: center;">eryar@163.com</p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中几何曲线与曲面求交使用类GeomAPI_IntCS，是对类IntCurveSurface_HInter的简单封装。在IntCurveSurface_HInter中对曲线和曲面求交分为以下几种类型：</p>
<ul>
<li>PerformConicSurf：二次曲线与曲面求交，其中又分为两类：二次曲线与二次曲面求交和二次曲线和自由曲面求交；</li>
<li>InternalPerformCurveQuadric：自由曲线与二次曲面求交；</li>
<li>InternalPerform：自由曲线和自由曲面求交；</li>
</ul>
<p>本文主要介绍曲线与曲面求交的实现原理。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203020991-1269188047.png" alt="" /></p>
<h2>2 二次曲线与二次曲面求交</h2>
<p>二次曲线与二次曲面求交使用IntAna_ConicQuad计算，主要思路是将曲线用参数方程表示，代入二次曲面的代数方程。二次曲面可以使用二次多项式表示，将二次曲线与二次曲面相交表示成一个多项式方程，使用math_DirectPolynomialRoots对多项式方程进行求解。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203040239-785246703.png" alt="" /></p>
<h2>3 二次曲线与自由曲面求交</h2>
<p>二次曲线与自由曲面求交将曲面使用IntCurveSurface_Polyhedron在U，V上采样离散得到grid网格。这个类实现与IntPolyh_MaillageAffinage类功能有重复。</p>
<p>IntCurveSurface_ThePolygon多段线与Intf_InterferencePolygonPolyhedron 网格求交，根据多段线与网格求交情况，找到初始值，使用IntImp_IntCS计算精确值。与曲面求交的Marching算法类似，使用迭代法去计算精确交点。迭代方程为IntImp_ZerCSParFunc，写出这个方程的Value()值计算和Derivatives()微分计算公式。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203114981-549642634.png" alt="" /></p>
<p>将曲线与曲面求交问题转化为求曲面参数u,v和曲线参数w，使曲线C(w)曲面S(u,v)上的点重合，建立函数如下：</p>
<p>F(u,v,w)=S(u,v) - C(w)</p>
<p>所求的精确交点满足方程F(u,v,w)=0，F为一含有三个坐标的矢量，对应函数Value()：</p>
<p>Fx(u,v,w)=Sx(u,v) - Cx(w) = 0</p>
<p>Fy(u,v,w)=Sy(u,v) - Cy(w) = 0</p>
<p>Fz(u,v,w)=Sz(u,v) - Cz(w) = 0</p>
<p>上面为含有三个方程的以u,v,w为变量的非线性方程组，精确交点就是非线性方程组的解。使用类math_FunctionSetRoot应用Newton-Raphson迭代法求解非线性方程组的解。使用Newton迭代法有个前提条件是要求非线性方程组一阶可导，即要写出Jacobian迭代矩阵，即上述函数Derivatives()的实现原理：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203248821-1999638872.png" alt="" /></p>
<h2>4 自由曲线与二次曲面求交</h2>
<p>自由曲线与二次曲面求交IntCurveSurface_TheQuadCurvExactInter ，通过类IntCurveSurface_TheQuadCurvFuncOfTheQuadCurvExactHInter建立二次曲面与曲线之间的函数，是求解曲线上参数U的一元函数。</p>
<h2>5 自由曲线与自由曲面求交</h2>
<p>自由曲线与自由曲面求交和二维自由曲线求交类似，采用的离散法。即将曲线通过采样离散成多段线Polygon，将曲面采样生成网格Polyhedron，通过类IntCurveSurface_TheInterferenceOfHInter来计算多段线与网格的相交。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203305755-1176650810.png" alt="" /></p>
<p>包Intf主要用来计算二维多段线、三维多段线及网格的相交。根据离散计算的粗交点，再根据类IntCurveSurface_TheExactHInter使用迭代法求得精确交点。这个思想与曲面和曲面求交相同。</p>
<h2>6 求交结果</h2>
<p>曲线与曲面求交的结果主要也是保存在类IntCurveSurface_Intersection对象中，这个类的设计与二维曲线求交类似，不够直接。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231130203356660-1455224346.png" alt="" /></p>
<p>可以看到IntCurveSurface_Intersection这个类的构造函数是protected的，意思是不能直接使用，通过派生类IntCurveSurface_HInter调用SetValues()函数将求交结果保存起来。求交结果为交点IntCurveSurface_IntersectionPoint和交线IntCurveSurface_IntersectionSegment。</p>
<p>其中交点中IntCurveSurface_IntersectionPoint保存了三维坐标点，交点在曲面上的U,V参数，交点在曲线上的参数U及相交状态。交线主要是线现面和重合部分的几何奇异情况数据。</p>
<p>从类图上可以看出，这个套路同样用到了HLR算法中，理解这个套路对理解HLR算法有帮助。</p>
<h2>7 Conclusion</h2>
<p>综上所述，OpenCASCADE中将曲线与曲面求交根据曲线和曲面类型的不同分别处理。二次曲线曲面求交依赖IntAna包，自由曲线和自由曲面求交使用离散法，最终实现算法与两个曲面求交的Marching算法类似，通过离散得到的精交点，再代入迭代方程求得精确解。其中把曲线或曲面离散的采样点没有考虑曲线或曲面的曲率等，采样点数量较大，会影响性能 。曲面采样离散代码与曲面求交中的有重复。从几何求交类中可以看到没有容差的输入，可以思考一下这个问题。</p>
<p>TKGeomAlgo中除了拟合算法外，大部分代码主要就是线线求交、线面求交及面面求交算法。理解这些算法的实现原理，为Boolean算法的求交逻辑打下基础。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230216.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:23 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_intcs.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE二维曲线求交</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_curve2d.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:22:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_curve2d.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230215.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_curve2d.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230215.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230215.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE二维曲线求交</strong></h1>
<h2>1 Introduction</h2>
<p>OpenCASCADE中对二维曲线求交和三维曲线求交是不同的，三维曲线求交统一使用离散法，二维曲线求交根据曲线类型的不同分种类型进行处理。二维曲线求交中还提供了计算自交的直接接口。在TKGeomAlgo中，主要内容就是拟合、求交算法，理解求交算法的实现原理，达到能阅读和修改源码的状态，能够分析和解决实际遇到的问题，理解OpenCASCADE的能力边界，根据需要选择所需要的功能，使软件结果可控。本文主要介绍二维曲线相交的实现原理。</p>
<p>由于OpenCASCADE开发时间相对久远，在二维曲线求交相关代码中大量使用了宏定义的方式来实现C++ 的模板template能力，宏定义在类的XXX_0.cxx文件中，对应模板实现在*.gxx中：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231127190113965-477868197.png" alt="" /></p>
<p>这种实现方式会让代码的可读性变差，不利于代码维护。应该用C++的方式对这些*.gxx代码重构，增强代码可读性和可维护性。</p>
<h2>2 求交分类</h2>
<p>二维求交使用类Geom2dAPI_InterCurveCurve， 这个类是对类Geom2dInt_GInter的封装。在类Geom2dInt_GInter中，如果只输入一条曲线，可以计算自交，如果输入两条曲线，计算两条曲线的相交。</p>
<ul>
<li>IntCurve_IntConicConic：二次曲线与二次曲线求交。二次曲线与二次曲线求交都先使用几何方法计算交点，再判断是否在参数范围内；</li>
<li>Geom2dInt_TheIntConicCurveOfGInter：二次曲线与任意曲线求交。二次曲线与任意曲线求交通过类Geom2dInt_MyImpParToolOfTheIntersectorOfTheIntConicCurveOfGInter建立距离方程，使用类math_FunctionAllRoots来对方程进行求解；</li>
<li>Geom2dInt_TheIntPCurvePCurveOfGInter：任意曲线与任意曲线求交。自由曲线求交使用离散法IntCurve_IntPolyPolyGen，使用类Geom2dInt_ThePolygon2dOfTheIntPCurvePCurveOfGInter将曲线通过采样点生成多段线Polyline，使用类Intf_InterferencePolygon2d计算多段线之间的粗交点，再使用类IntCurve_ExactIntersectionPoint通过粗交点找到曲线上的精确交点；</li>
</ul>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231127190138515-806665455.png" alt="" /></p>
<p>这些类都是从类IntRes2d_Intersection派生：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231127190147208-1703732345.png" alt="" /></p>
<p>从上图可知，二维求交结果类IntRes2d_Intersection相关派生类可知二维求交与HLR算法也有关系，理解二维曲线求交逻辑，对理解HLR代码也有帮助。</p>
<h2>3 自交计算</h2>
<p>当只输入一条曲线时，可以对曲线进行自交计算，主要实现逻辑为：若为普通二次曲线，则不会自交；若是其他曲线，使用离散法对曲线进行自交计算。代码如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231127190200122-488555626.png" alt="" /></p>
<h2>4 求交结果</h2>
<p>二维曲线求交结果保存到类IntRes2d_Intersection中，主要包含两部分：</p>
<ul>
<li>IntRes2d_IntersectionPoint：交点数据，保存交点坐标值，交点在两个曲线上的参数，及两条曲线在交点处的过渡状态Transition；</li>
<li>IntRes2d_IntersectionSegment：交线数据，当两条曲线有重叠时的几何奇异情况时，求交结果为交线；</li>
</ul>
<p>因为类IntRes2d_Interseciton的构造函数protected，所以不能直接使用这个类，都是通过其派生类使用函数SetValues()将计算得到的交点和交线数据保存起来。这里类的设计比较繁琐，代码可读性较差。</p>
<h2>5 Conclusion</h2>
<p>OpenCASCADE对于二维曲线求交进行分类处理，根据曲线类型是二次曲线、参数曲线分成三类：二次曲线与二次曲线求交、二次曲线与参数曲线求交和参数曲线与参数曲线求交，不同的求交类型采用不同的策略可以提高求交性能和稳定性。使用离散法计算二维曲线自交。从求交结果来看，也处理了几何奇异问题，即曲线重叠情况。</p>
<p>对于曲线求交还有很大改进空间：</p>
<ul>
<li>使用C++编码风格重构*.gxx代码，提高代码可读性，方便代码维护；</li>
<li>对于自由曲线求交的离散法中计算两条多段线算法中引入BVH来加速；</li>
<li>将曲线离散成多段线时考虑&#8203;曲线的曲率变化，不要均匀采样，减少多段线数量；</li>
<li>对于三维曲线求交都是使用了离散法，建议像二维曲线求交这样进行分类处理，以及引入BVH，提高性能和稳定性；</li>
</ul>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230215.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:22 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_curve2d.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲线上点的反求</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_extrema_pc.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:21:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_extrema_pc.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230214.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_extrema_pc.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230214.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230214.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲线上点的反求</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>曲线可以用代数方程表示，如圆可以用X^2+Y^2=R^2表示，也可以用参数方程X(u)=RCos(u), Y(u)=RSin(u)表示。要判断点是不是在线上，用曲线代数方程可以很直接得出结果，但是使用参数方程就没有那么直接。这也是参数曲线上点的反求问题，参数曲线上点的反求问题应用广泛，如前面所述判断点是否在曲线上、点向曲线投影、点与线的求交、点在参数曲线上的参数等，都与点的反求问题相关。本文主要结合代码介绍OpenCASCADE曲线上点的反求实现原理及使用过程中的一些注意事项。</p>
<h2>2 实现原理</h2>
<p>在《The NURBS Book》书中将点的反求问题归结为点向曲线投影距离最短的问题，如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223308624-288654811.png" alt="" /></p>
<p>建立函数f(u)=C&rsquo;(u).(C(u) - P)表示点到曲线距离，当f(u)=0时为点到曲线的最短距离，不管点P是否在曲线上。几何意义是点到曲线任意点的向量与任意点处的切向量点积为零，表示在两个向量垂直的时候求得极值点。注意数学方程中垂直这个几何意义。</p>
<p>OpenCASCADE中实现曲线上点的反求原理与《The NURBS Book》书中一致。点的反求使用类GeomLib_Tool::Parameter()函数：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223325985-1610888534.png" alt="" /></p>
<p>输入曲线、点和最大距离，计算点是否在曲线上及若在曲线上，点对应参数曲线的参数U。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223340394-1887945485.png" alt="" /></p>
<p>类Extrema_ExtPC计算点P到线C的极值Extrema。根据代码注释可以看出点的反求数学方程与《The NURBS Book》书中一致：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223354086-1220908578.png" alt="" /></p>
<p>数学方程对应的类的变量为myF，类名为Extrema_FuncExtPC，从类math_FunctionWithDerivative派生，所以必须实现两个关键虚函数Value()和Derivative()。其代码注释说明了这两个函数的实现细节：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223407926-1740284446.png" alt="" /></p>
<p>其中F(u)对应函数Value():</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223417144-1935074213.png" alt="" /></p>
<p>DF(u)对应函数Derivative()，最后使用Newton法math_FunctionRoots对方程进行求根。</p>
<h2>3 注意事项</h2>
<p>OpenCASCADE中点的反求GeomLib_Tool::Parameter()、点向曲线投影GeomAPI_ProjectPointOnCurve、点与曲线的交点IntTools_Context::ComputeVE等算法都是使用了Extrema_ExtPC类。</p>
<p>&nbsp;</p>
<p>当使用GeomLib_Tool::Parameter()函数来判断点是否在曲线上时，注意端点处点的反求要满足垂直的条件，即使点与曲线某个端点距离小于MaxDist时，也是返回false。即对于曲线端点处的情况需要自己预先处理，直接算点P与曲线端点距离与MaxDist比较，先处理端点。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223438492-2031001366.png" alt="" /></p>
<p>可以看到这里也处理的端点处的情况，但是最后没有与MaxDist有关系，最后容差是Precision::SquareConfusion()。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231121223453107-31137009.png" alt="" /></p>
<p id="1700577295832"></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230214.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:21 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_extrema_pc.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE - 曲线自交</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_edge_self_intersect.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:21:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_edge_self_intersect.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230213.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_edge_self_intersect.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230213.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230213.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE - 曲线自交</strong></h1>
<h2>1 Introduction</h2>
<p>OpenCASCADE为二维曲线提供了求交及自交的类 Geom2dAPI_InterCurveCurve：当传入一个二维几何曲线时可以计算自交self-intersections。但是没有提供直接的三维几何曲线求交的类，也没有直接的计算自交的类。有人同学问OpenCASCADE有没有三维曲线自交的功能，其实理解两个Edge求交算法后，可以自己实现一个自交函数。</p>
<h2>2 Self-Intersection</h2>
<p>因为OpenCASCADE中两条三维曲线求交的类是IntTools_EdgeEdge，其实现原理是基于包围盒的分割法。基于这个分割递归思想，实现自交也可以参考这个思路。算法的流程为：输入一条要计算自交的边Edge，对边进行离散采样，将采样得到的每段曲线的包围盒生成BVH进行相交检测，将BVH中包围盒相交的两条曲线调用IntTools_EdgeEdge来计算相交。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231115183815260-104802493.png" alt="" /></p>
<p>离散得到的曲线段会比较多，如果用两个循环来检测两两曲线段的相交情况性能差，可以引入BVH提高性能。</p>
<h2>3 Test</h2>
<p>可以通过插值Interpolate来构造曲线测试，指定几个自交点来构造插值曲线。计算结果如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231115183832716-125622246.png" alt="" /></p>
<p>与曲线求交原理类似，都是使用离散的方法，可以思考一下数值算法如何处理。</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230213.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:21 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_edge_self_intersect.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE 曲线求交</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_edge_edge.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:20:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_edge_edge.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230212.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_edge_edge.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230212.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230212.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE 曲线求交</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中提供了二维几何曲线的求交类Geom2dAPI_InterCurveCurve，对应到三维几何只提供了GeomAPI_IntCS, GeomAPI_IntSS，没有提供几何的GeomAPI_IntCC求交类。这些几何求交一般使用的是数值算法，即解方程。对于两条几何曲线P(u1), Q(u2)，求交就是解P(u1) - Q(u2) = 0这个方程。为什么对于三维几何曲线没有提供数值算法？</p>
<p>对于拓朴边提供了求交算法IntTools_EdgeEdge，这个类是使用类似于曲面求交的离散网格法，使用了离散包围盒法。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205207552-1984548654.png" alt="" /></p>
<p>基于包围合盒的算法是个递归算法，算法思路：</p>
<ol>
<li>1) 检查两条边在参数范围内的包围盒，若空间干涉，则进行下一步；否则退出本次判断；</li>
<li>2) 找出两条边包围盒的公共部分对应的参数，若没找到，则退出本次判断；</li>
<li>3) 并将第一条边在参数范围内分割成2或3部分，执行第一步；</li>
<li>4) 退出条件：没有相交或找到相交的参数值；</li>
</ol>
<p>第一次是分别分成2部分：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205214940-1013938627.png" alt="" /></p>
<p>在递归函数FindSolutions()中，只去对第一条边进行参数分割成3部分：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205223194-1459823810.png" alt="" /></p>
<h2>2 辅助函数</h2>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205245944-609466017.png" alt="" /></p>
<p>第一个辅助函数是FindParameters()，用来更新第二条边在第一条边的的包围盒中的参数范围，使用这个参数范围更新包围盒。</p>
<p>第二个辅助函数是CheckCoincidence()，用来检测两段边是否重合。第一步是快速计算，对边采样10个点，若通过初步粗检测，后面再深入计算。这些算法都不太高效。</p>
<p>第三个辅助函数是IsIntersection()用来判断两边条在参数范围内是否相交。</p>
<h2>3 测试</h2>
<p>将两条边求交过程中的包围盒显示出来，方便查看理解算法。先测试两个圆之间的相交：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205300955-1149046547.png" alt="" /></p>
<p>其中第一条边是绿色的圆，求交过程中的包围盒也用绿色表示；第二条边是红色的圆，求交过程中的包围盒也用红色表示。因为圆是闭合的，第一次都分割成2部分。将上面交点处理放大：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205308640-422815477.png" alt="" /></p>
<p>后面都是将第一条边分割成3部分，然后分别用这3部分的包围盒去找与第二条边相交的参数范围，再更新第二条边的包围盒。继续放大上面交点处：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205315774-595549752.png" alt="" /></p>
<p>发现对于两个圆的求交，执行了100次，效率不高。又用两个B样条曲线求交来测试：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202311/534255-20231112205323365-1135925213.png" alt="" /></p>
<p>发现对于B样条曲线求交速度较快。</p>
<h2>4 Conclusion</h2>
<p>曲线求交需要考虑重合部分，opencascae中没有使用数值算法来计算，而是采用基于包围盒的算法来处理。这种算法一般情况下可以快速找到求交解，有时递归较深，对于基本曲线可以像曲面求交一样分类处理以提高性能。opencascade中对于三维曲线求交算法性能还有优化空间。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230212.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:20 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_int_edge_edge.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[书]-OpenCASCADE参考书籍</title><link>http://www.cppblog.com/eryar/archive/2023/12/02/occt_book.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 02 Dec 2023 04:19:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/12/02/occt_book.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230210.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/12/02/occt_book.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230210.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230210.html</trackback:ping><description><![CDATA[<p>学而不思则罔，思而不学则殆。光看书籍的理论知识，没有实践看不到效果。光看occ的源码，没有理论支撑，不能抓住几何问题的本质。</p>
<p>除了在OpenCASCADE入门指南中推荐的书籍之外，还有一些进阶的书籍，放在那儿有时间就看看，总会有些收获。悟性不足，只有勤能补拙。对于看不懂的，只能用&ldquo;书读百遍，其义自见&rdquo;安慰一下自己。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202310/534255-20231030202652972-1591293169.jpg" alt="" /></p>
<p id="1698668816128">王元 数学大辞典&nbsp; 工具书&nbsp; 方便一些定义，公式，定理的查找。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202310/534255-20231030202814672-30785715.jpg" alt="" /></p>
<p id="1698668897899">《计算机辅助几何设计导论》比较全面地介绍了计算机辅助几何设计的发展历史及其主要内容和最新进展，包括现代的T样条曲线曲面。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202310/534255-20231030202949643-100965336.jpg" alt="" /></p>
<p>《样条函数与计算几何》叙述样条函数和计算几何的基本理论和方法，同时，总结了作者几年来在该领域中的研究成果.</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202310/534255-20231030203039481-1856922148.jpg" alt="" /></p>
<p id="1698669042045">《现代数学基础丛书165：散乱数据拟合的模型、方法和理论（第二版）》介绍了多元散乱数据拟合的一般方法，包括多元散乱数据多项式插值、基于三角剖分的插值方法、Boole和与Loons曲面、Sibson方法或自然邻近法、Shepard方法、Kriging方法、薄板样条方法、MQ拟插值法、径向基函数方法、运动*小二乘法、隐函数样条方法、R函数法等。同时还特别介绍了近年来国际上越来越热并在无网格微分方程数值解方面有诸多应用的径向基函数方法及其相关理论。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202310/534255-20231030203210737-959037925.jpg" alt="" /></p>
<p id="1698669133469">主要内容包括几何偏微分方程的构造方法、各种微分几何算子的离散化方法及其离散格式的收敛性、几何偏微分方程数值求解的有限差分法、有限元法以及水平集方法，还包括几何偏微分方程在曲面平滑、曲面拼接、N边洞填补、自由曲面设计、曲面重构、曲面恢复、分子曲面构造以及三维实体几何形变中的应用。</p>
<p id="1698668992693"></p>
<img src ="http://www.cppblog.com/eryar/aggbug/230210.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-12-02 12:19 <a href="http://www.cppblog.com/eryar/archive/2023/12/02/occt_book.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE 扫掠曲面</title><link>http://www.cppblog.com/eryar/archive/2023/09/29/occt_sweep.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Fri, 29 Sep 2023 13:38:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/29/occt_sweep.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230116.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/29/occt_sweep.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230116.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230116.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE 扫掠曲面</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p><strong>蒙皮</strong>（<strong>Skinning</strong>）就是将一簇截面曲线（section curves）融合在一起生成曲面的过程。蒙皮只是<strong>放样</strong>（<strong>Lofting</strong>）的新名词，放样可以追溯到计算机没未诞生的时候，从那时到现在，它一直在造船、汽车和航空工业中被广泛地应用。</p>
<p><strong>扫掠</strong>（<strong>Sweep</strong>）研究的是一条截面曲线沿任意路径曲线扫掠的问题。根据扫掠曲面的定义，扫掠曲面未必都能表示成NURBS形式，所以一般采用拟合算法来逼近。一种算法是基于蒙皮法，沿着路径曲线变换和采样N个截面，然后将它们作为截面曲线进行蒙皮。随着采样数量N的增加，生成的拟合曲面精度也将提高。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212453288-316905008.png" alt="" /></p>
<p>本文主要介绍OpenCASCADE中扫掠造型算法的使用，除了上面一般的扫掠曲面，还有一些高级用法。</p>
<h2>2 Sweep with Guide</h2>
<p>在DRaw Test Harness中输入命令setsweep可以看到有指定引导 线Guide的选项：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212521683-579214220.png" alt="" /></p>
<p>这个引导线Guide有什么用呢？下面给出一个示例：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212529525-408102835.png" alt="" /></p>
<p>其中Profile是扫掠截面，Spine为扫掠脊线，Guide为扫掠引导线。扫掠结果就是一个螺旋的钻头模型。在Draw Test Harness的例子中，给出两个关于引导线扫掠的示例，两个钻头：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212536763-1506916147.png" alt="" /></p>
<p>把这两个例子理解基本能掌握扫掠算法的使用方法，从这两个例子可以看出，OpenCASCADE扫掠造型能力还不错。</p>
<h2>3 Sweep on Face</h2>
<p>扫掠还有一个能力是使扫掠截面垂直于一个支撑面，这是一个有用的选项。下面还是在Draw Test Harness中测试一下：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212545567-771432609.png" alt="" /></p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929212633832-1400553507.png" alt="" /></p>
<p id="1695993995809"></p>
<p id="1695993947674"></p>
<h2>4 Conclusion</h2>
<p>OpenCASCADE中扫掠造型算法功能还比较强大，除了支持常规的扫掠外，还支持带引导线的扫掠，及带引导线的多个截面的变形扫掠，还支持截面始终垂直于支持面的扫掠选项。扫掠的关键是确定截面的变换规则，底层的蒙皮拟合算法还是比较稳定的。把Draw Test Harness中两个钻头的例子理解后，基本上应该能够掌握OpenCASCADE中扫掠造型的使用方法。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230116.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-29 21:38 <a href="http://www.cppblog.com/eryar/archive/2023/09/29/occt_sweep.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>布尔数据 面的相交</title><link>http://www.cppblog.com/eryar/archive/2023/09/29/occt_int_face.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Fri, 29 Sep 2023 10:45:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/29/occt_int_face.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230114.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/29/occt_int_face.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230114.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230114.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>布尔数据 面的相交</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中对面的相交定义如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929181943213-511029444.png" alt="" /></p>
<p>三维空间中两个带有Geometry Surface的面Face，当两个Surface之间的距离小于Face中的容差Tolerance，则认为是相交的。一般两个面之间相交得到的是交线，还有一些情况得到的是交点，如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929181951065-128083254.png" alt="" /></p>
<p>布尔运算中面的相交是相对复杂的问题，除了考虑上述交线和交点的问题以外，还要考虑有重叠的情况；对于新生成的交线，还要考虑生成PCurve；若面上有开孔，还要将穿过开孔区域的交线排除等；最后要考虑如何保存面相交的结果。相交的计算在函数：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929181958865-1494145023.png" alt="" /></p>
<p>最终是调用IntTools_FaceFace来计算两个面的相交。并将计算结果交线和交点，是否重叠等信息保存到BOPDS_InterfFF中：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182006298-397794912.png" alt="" /></p>
<h2>2 Face Info</h2>
<p>类BOPDS_FaceInfo用来存储以下信息：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182030971-668575657.png" alt="" /></p>
<p>注意PBo31和PBSc1，一个状态是On，一个状态是Section。在相交处理类BOPAlgo_PaveFiller中通过函数BOPAlgo_PaveFiller::UpdateFaceInfo将这些相交的状态更新。</p>
<h2>3 Tangent Face</h2>
<p>从前面的文章关于检测边与边、边与面是否有重叠时采用了固定采样点来处理的不严谨的逻辑来看，判断线的重叠是个复杂的问题，判断面与面的重叠就相对更复杂。本文先从简单入手，先看对于最简单的两个平面重叠的检测，引出大家对于任意两个面重叠区域检测的思考。对这种特殊的情况处理在IntTools_FaceFace中的函数PerformPlanes()中实现。其中使用二次曲面的几何求交法进行处理，源码如下：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182100531-1354022013.png" alt="" /></p>
<p>通过源码可以看出，若两个平面之间的法向夹角小于TolAng及距离小于Tol时，则认为两个面是一样的IntAna_Same；当距离大于Tol时，则认为没有相交IntAna_Empty。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182109904-192787472.png" alt="" /></p>
<p>对于重叠的平面，将theTangentFaces设置成true表示是重叠的。这里留下一个问题大家思考：<em><strong>如何判断自由曲面的重叠情况？</strong></em></p>
<h2>4 Hole</h2>
<p>当面上有孔洞时，还要对交线进行处理，以排除掉孔洞中的交线。当使用IntTools_FaceFace来计算两个面的交线时，可以看到交线的范围不正确，没有处理孔洞情况，甚至也没有处理面的边界。如下图所示红色的交线为使用IntTools_FaceFace计算得到的：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182132177-185509352.png" alt="" /></p>
<p>当使用BOPAlgo_PaveFiller计算交线并结合PaveBlock得到交线显示如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230929182140174-31164206.png" alt="" /></p>
<p>虽然计算两个面之间的相交处理最终是调用的IntTools_FaceFace，但是要得到正确的交线需要使用类BOPAlgo_PaveFiller。这里也留下问题供大家思考：<em><strong>为什么IntTools_FaceFace计算的交线范围不正确？为什么BOPAlgo_PaveFiller计算的交线正确？</strong></em></p>
<h2>5 Conclusion</h2>
<p>综上所述，布尔数据中面的相交的结果可能有交线，也可能有交点。将求交结果保存到FaceInfo中。从简单的两个平面重叠来看，将重叠的状态用变量theTangetFaces来保存。那任意两个曲面重叠如何判断呢？面的相交虽然提供类IntTools_FaceFace来计算，但是没有正确处理交线的范围，为什么BOPAlgo_PaveFiller中可以正确处理交线呢？</p>
<p>&nbsp;</p>
<p><span style="font-size: 18px;"><strong>祝大家中秋国庆节日快乐！</strong></span></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-29 18:45 <a href="http://www.cppblog.com/eryar/archive/2023/09/29/occt_int_face.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>布尔数据 边的相交</title><link>http://www.cppblog.com/eryar/archive/2023/09/27/occt_edge_int.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Wed, 27 Sep 2023 13:52:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/27/occt_edge_int.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230108.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/27/occt_edge_int.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230108.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230108.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>布尔数据 边的相交</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>在OpenCASCADE中对于边的相交分为三类：边与点，边与边，边与面，边与点的相交已经归结为点与边的相交处理了，边的相交主要处理边与边，边与面的相交。边与边、边与面的相交会引入一个新的数据结构-公共部分Common Part，用于保存重叠的公共部分数据。</p>
<h2>2 Edge/Edge Interferences</h2>
<p>对于两条边的相交是指在两条边的某些地方的距离小于边的容差之和，主要分为两种情况，一种是两条边只有一个交点的情况；一种是有重叠部分的情况；先看只有一个交点情况：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213139399-1355704272.png" alt="" /></p>
<p>我们在DRAW中通过脚本构造最简单的情况来测试。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213148563-188888646.png" alt="" /></p>
<p>在处理边与边相交的函数BOPAlgo_PaveFiller::PerformEE()中，对每两条边调用BOPAlgo_EdgeEdge进行求交。从这里可以看到Pave Block的使用，相当于对每两条边上的每对Pave Block部分进行求交。这里有一些优化空间，目前是使用的两个循环处理，可以尝试使用BVH来提升一些性能。当每对Pave Block对应的点的索引号一致时，即每对Pave Block的端点重叠时，使用快速计算的算法来判断是否有重叠。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213207083-727333736.png" alt="" /></p>
<p>对于边的求交结果保存到BOPDS_InterfEE中，都会保存是哪两条边相交及相交的公共部分。对于相交于一点的公共部分的类型为TopAbs_VERTEX，对于有重叠部分的公共部分类型为TopAbs_EDGE：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213219455-1330844151.png" alt="" /></p>
<p>当两边条有重叠部分时，如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213233801-157268512.png" alt="" /></p>
<p>如何检测两条边的公共部分呢？在函数IntTools_EdgeEdge::IsCoincident()中实现：</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
//function :  IsCoincident
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeEdge::IsCoincident() 
{
  Standard_Integer i, iCnt, aNbSeg, aNbP2;
  Standard_Real dT, aT1, aCoeff, aTresh, aD;
  Standard_Real aT11, aT12, aT21, aT22;
  GeomAPI_ProjectPointOnCurve aProjPC;
  gp_Pnt aP1;
  //
  aTresh=0.5;
  aNbSeg=23;
  myRange1.Range(aT11, aT12);
  myRange2.Range(aT21, aT22);
  //
  aProjPC.Init(myGeom2, aT21, aT22);
  //
  dT=(aT12-aT11)/aNbSeg;
  //
  iCnt=0;
  for(i=0; i &lt;= aNbSeg; ++i) {
    aT1 = aT11+i*dT;
    myGeom1-&gt;D0(aT1, aP1);
    //
    aProjPC.Perform(aP1);
    aNbP2=aProjPC.NbPoints();
    if (!aNbP2) {
      continue;
    }
    //
    aD=aProjPC.LowerDistance();
    if(aD &lt; myTol) {
      ++iCnt; 
    }
  }
  //
  aCoeff=(Standard_Real)iCnt/((Standard_Real)aNbSeg+1);
  return aCoeff &gt; aTresh;
}</code></pre>
<p>从上述代码可以看出，对于重叠部分的检测是将一条边根据检测范围分成23段采样点，计算每个点到另一条边的距离，满足条件的采样点的数量超过12个，基本认为是重叠的。从这里可以看出这样检测重叠稍微有点不严谨。固定采样点数量对于小段曲线来说数量过大，对于很长的曲线来说数量又偏小，这里有待提高。如果重叠，则将公共部分的数据保存起来：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213307293-1447779286.png" alt="" /></p>
<p>对于测试的TCL脚本不会走这个通用的判断流程，会直接有IntTools_EdgeEdge::ComputeLineLine()函数来处理这种特殊情况：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213326878-437572611.png" alt="" /></p>
<p>从保存的数据可以看出，公共部分的相交类型为TopAbs_VERTEX，及交点分别在两条边上的参数。关于有重叠部分的两条边相交，同学们可以自行使用DRAW脚本来测试一下。</p>
<h2>3 Edge/Face Interferences</h2>
<p>边与面的相交会遇到和边与边相交类似的情况，即会有重叠部分Common Part。也分为两种情况，一种情况是边与面只有一个交点的情况，交点可能会有多个；一种情况是有重叠部分的情况。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213344767-964332040.png" alt="" /></p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213352751-1688721007.png" alt="" /></p>
<p id="1695821627053"></p>
<p>我们可以在使用脚本来测试一下重叠的情况：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230927213408342-243270677.png" alt="" /></p>
<p>从代码中可以看出当边的端点在面上时，则会判断边与面会不会重叠Coincidence。判断逻辑与判断边是否重叠类似，都是使用固定23个采样点的方式处理，并加上定位器来判断点是否在面上，因为面上可能会有孔洞：</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
//function :  IsCoincident
//purpose  : 
//=======================================================================
Standard_Boolean IntTools_EdgeFace::IsCoincident() 
{
  Standard_Integer i, iCnt;
  Standard_Real dT, aT, aD, aT1, aT2, aU, aV;
  gp_Pnt aP;
  TopAbs_State aState;
  gp_Pnt2d aP2d;
  //
  GeomAPI_ProjectPointOnSurf&amp; aProjector=myContext-&gt;ProjPS(myFace);
  Standard_Integer aNbSeg=23;
  if (myC.GetType() == GeomAbs_Line &amp;&amp;
      myS.GetType() == GeomAbs_Plane)
    aNbSeg = 2; // Check only three points for Line/Plane intersection
  const Standard_Real aTresh = 0.5;
  const Standard_Integer aTreshIdxF = RealToInt((aNbSeg+1)*0.25),
                         aTreshIdxL = RealToInt((aNbSeg+1)*0.75);
  const Handle(Geom_Surface) aSurf = BRep_Tool::Surface(myFace);
  aT1=myRange.First();
  aT2=myRange.Last();
  Standard_Real aBndShift = 0.01 * (aT2 - aT1);
  //Shifting first and last curve points in order to avoid projection
  //on surface boundary and rejection projection point with minimal distance
  aT1 += aBndShift;
  aT2 -= aBndShift;
  dT=(aT2-aT1)/aNbSeg;
  //
  Standard_Boolean isClassified = Standard_False;
  iCnt=0;
  for(i=0; i &lt;= aNbSeg; ++i) {
    aT = aT1+i*dT;
    aP=myC.Value(aT);
    //
    aProjector.Perform(aP);
    if (!aProjector.IsDone()) {
      continue;
    }
    //
    aD=aProjector.LowerDistance();
    if (aD &gt; myCriteria) {
      if (aD &gt; 100. * myCriteria)
        return Standard_False;
      else
        continue;
    }
    //
    ++iCnt; 
    //We classify only three points: in the begin, in the 
    //end and in the middle of the edge.
    //However, exact middle point (when i == (aNbSeg + 1)/2)
    //can be unprojectable. Therefore, it will not be able to
    //be classified. Therefore, points with indexes in 
    //[aTreshIdxF, aTreshIdxL] range are made available 
    //for classification.
    //isClassified == TRUE if MIDDLE point has been chosen and
    //classified correctly.
    if(((0 &lt; i) &amp;&amp; (i &lt; aTreshIdxF)) || ((aTreshIdxL &lt; i ) &amp;&amp; (i &lt; aNbSeg)))
      continue;
    if(isClassified &amp;&amp; (i != aNbSeg))
      continue;
    aProjector.LowerDistanceParameters(aU, aV);
    aP2d.SetX(aU);
    aP2d.SetY(aV);
    IntTools_FClass2d&amp; aClass2d=myContext-&gt;FClass2d(myFace);
    aState = aClass2d.Perform(aP2d);
    if(aState == TopAbs_OUT)
      return Standard_False;
    if(i != 0)
      isClassified = Standard_True;
  }
  //
  const Standard_Real aCoeff=(Standard_Real)iCnt/((Standard_Real)aNbSeg+1);
  return (aCoeff &gt; aTresh);
}</code></pre>
<p>求交结果与边与边相交类型，会保存边与面的索引，及公共部分的数据。除了保存这些数据以外，还和点与面相交一样，更新面上的信息FaceInfo，即有哪些边在面上。</p>
<h2>4 Conclusion</h2>
<p>综上所述，边与边、边与面相交会得到公共部分Common Part，公共部分可能是点，也可能是重叠的边。在过滤相交的边与边、边与面时都有一定的优化空间，即使用BVH来加速检测相交部分。在快速判断边与边是否重叠、边与面是否重叠部分的代码采用固定数量的采样点的处理方式不太严谨。将相交的结果及过程数据都保存到BOPDS_DS中作为后面算法使用。</p>
<img src ="http://www.cppblog.com/eryar/aggbug/230108.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-27 21:52 <a href="http://www.cppblog.com/eryar/archive/2023/09/27/occt_edge_int.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[开源]-OpenCASCADE-IMGUI</title><link>http://www.cppblog.com/eryar/archive/2023/09/24/occt_imgui_glfw.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 24 Sep 2023 11:33:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/24/occt_imgui_glfw.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230102.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/24/occt_imgui_glfw.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230102.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230102.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;">[开源]-OpenCASCADE-IMGUI</h1>
<h2>1 IMGUI</h2>
<p>ImGui 是一个用于C++的用户界面库，跨平台、无依赖，支持OpenGL、DirectX等多种渲染API，是一种即时UI（Immediate Mode User Interface）库，保留模式与即时模式的区别参考<a href="https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/retained-mode-versus-immediate-mode" target="_blank" rel="noopener"><strong>保留模式与即时模式</strong></a>。ImGui渲染非常快，但界面上有大量的数据集需要渲染可能会有一些问题，需要使用一些缓存技巧。缓存只是避免数据的更新逻辑耗时太久影响渲染，实际渲染过程不存在瓶颈。</p>
<p>IMGUI很轻量，还支持跨平台，对于小的测试程序IMGUI是理想的GUI。</p>
<h2>2 OcctImgui</h2>
<p>基于opencascade的glfw sample加入IMGUI，这样就可以开发一些带有GUI的程序。这些程序小巧且能方便跨平台，看上去效果也不错。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230924095848466-2038356090.png" alt="" /></p>
<p id="1695520732450">现在将OcctImgui开源，开源地址：https://github.com/eryar/OcctImgui</p>
<p>使用Premake来生成解决方案，只需要将premake5.lua中的相关第三方库的路径修改一下，即可以直接编译运行。</p>
<h2>3 Next</h2>
<p>目前occt的视图作为整个背景，下一步可以做成像CADRays中那样，将occt的视图作为视图的一部分，这样就可以使用IMGUI的Docking功能。</p>
<p>使用IMGUI也可以开发出很Cool的界面，最后放两个基于IMGUI开发的图形界面：</p>
<p>https://github.com/adriengivry/Overload</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230924100632768-2050956857.png" alt="" /></p>
<p id="1695521195784">https://github.com/sasobadovinac/CADRays</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230924100716935-1153331757.png" alt="" /></p>
<p id="1695521239424">https://github.com/MeshInspector/MeshLib</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230924100821074-217645834.png" alt="" /></p>
<p id="1695521303538"></p><img src ="http://www.cppblog.com/eryar/aggbug/230102.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-24 19:33 <a href="http://www.cppblog.com/eryar/archive/2023/09/24/occt_imgui_glfw.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>布尔数据 BOPDS_DS</title><link>http://www.cppblog.com/eryar/archive/2023/09/23/BOPDS_DS.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 23 Sep 2023 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/23/BOPDS_DS.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230099.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/23/BOPDS_DS.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230099.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230099.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>布尔数据 BOPDS_DS</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>在OpenCASCADE中，布尔相关的算子Operator有General Fuse Operator(GFA)，Boolean Operator(BOA)，Section Operator(SA)，Splitter Operator(SPA)，这些布尔算子都共用一套数据结构BOPDS_DS，其中存储了输入数据及中间结果数据。布尔算子包含两部分：</p>
<ul>
<li>Intersection Part(IP)相交部分：相交部分IP主要用来计算模型之间的相交情况，并将计算结果保存到BOPDS_DS中；</li>
<li>Building Part(BP)构建部分：构建部件BP从BOPDS_DS中获取相交和其他数据来构建相应的结果；</li>
</ul>
<p>由此可见，布尔数据BOPDS_DS是布尔操作中的数据中转站，将布尔操作的输入数据及中间计算结果数据都保存起来。本文主要介绍BOPDS_DS保存的数据。</p>
<p>&nbsp;</p>
<h2>2 BOPDS_DS</h2>
<p>BOPDS_DS中存储的信息有：</p>
<ul>
<li>Arguments：输入模型数据；</li>
<li>Shapes：模型信息；</li>
<li>Interferences：相交数据；</li>
<li>Pave Blocks：字面意思是铺路砖，我理解的是对边Edge分块；</li>
<li>Common Blocks：公共部分，边与边，边与面的重叠部分；</li>
</ul>
<p>这里的Shapes是模型信息BOPDS_ShapeInfo，存储模型类型，包围盒等数据：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230923171853119-1848703200.png" alt="" /></p>
<p>这里应该不需要再另外保存myType，因为在myShape中可以直接获取类型信息。模型信息在初始化函数Init()中来设置，主要是包围盒等信息：</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
//function : Init
//purpose  : 
//=======================================================================
void BOPDS_DS::Init(const Standard_Real theFuzz)
{
  Standard_Integer i1, i2, j, aI, aNb, aNbS, aNbE, aNbSx;
  Standard_Integer n1, n2, n3, nV, nW, nE, aNbF;
  Standard_Real aTol, aTolAdd;
  TopAbs_ShapeEnum aTS;
  TopoDS_Iterator aItS;
  TColStd_ListIteratorOfListOfInteger aIt1, aIt2, aIt3;
  TopTools_ListIteratorOfListOfShape aIt;
  BOPDS_IndexRange aR;
  Handle(NCollection_BaseAllocator) aAllocator;
  TopTools_MapOfShape aMS;
  //
  // 1 Append Source Shapes
  aNb=myArguments.Extent();
  if (!aNb) {
    return;
  }
  //
  myRanges.SetIncrement(aNb);
  //
  aNbS=0;
  aIt.Initialize(myArguments);
  for (; aIt.More(); aIt.Next()) {
    const TopoDS_Shape&amp; aSx=aIt.Value();
    //
    aNbSx=0;
    TotalShapes(aSx, aNbSx, aMS);
    //
    aNbS=aNbS+aNbSx;
  }
  aMS.Clear();
  //
  myLines.SetIncrement(2*aNbS);
  //-----------------------------------------------------scope_1 f
  aAllocator=
    NCollection_BaseAllocator::CommonBaseAllocator();
  //
  //
  i1=0; 
  i2=0;
  aIt.Initialize(myArguments);
  for (; aIt.More(); aIt.Next()) {
    const TopoDS_Shape&amp; aS=aIt.Value();
    if (myMapShapeIndex.IsBound(aS)) {
      continue;
    }
    aI=Append(aS);
    //
    InitShape(aI, aS);
    //
    i2=NbShapes()-1;
    aR.SetIndices(i1, i2);
    myRanges.Append(aR);
    i1=i2+1;
  }
  //
  aTolAdd = Max(theFuzz, Precision::Confusion()) * 0.5;
  myNbSourceShapes = NbShapes();
  //
  // 2 Bounding Boxes
  //
  // 2.1 Vertex
  for (j=0; j&lt;myNbSourceShapes; ++j) {
    BOPDS_ShapeInfo&amp; aSI=ChangeShapeInfo(j);
    //
    const TopoDS_Shape&amp; aS=aSI.Shape();
    //
    aTS=aSI.ShapeType();
    //
    if (aTS==TopAbs_VERTEX) {
      Bnd_Box&amp; aBox=aSI.ChangeBox();
      const TopoDS_Vertex&amp; aV=*((TopoDS_Vertex*)&amp;aS);
      const gp_Pnt&amp; aP=BRep_Tool::Pnt(aV);
      aTol = BRep_Tool::Tolerance(aV);
      aBox.SetGap(aTol + aTolAdd);
      aBox.Add(aP);
    }
  }</code></pre>
<p>在初始化函数中通过两个递归函数TotalShapes()和InitShape()来收集所有模型数据，然后再分别计算点、边、面的包围盒。这些包围盒数据为后面使用BVH相交检测做准备。</p>
<h2>3 Interferences</h2>
<p>相交数据Interferences主要用来保存求交结果数据，使用了简单的派生关系，不同的相交类型得到不同的相交结果。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230923171957397-1228623867.png" alt="" /></p>
<p>保存的数据有：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230923172007638-528632991.png" alt="" /></p>
<p>其中Index1和Index2为相交的两个模型在BOPDS_DS中的索引号。对于点Vertex和边Edge的相交结果，保存了相交点在边上的参数myParam：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230923172019310-56442034.png" alt="" /></p>
<p>&nbsp;</p>
<h2>4 DRAW</h2>
<p>在DRAW中输入相关的命令可以方便地对这些数据结构进行Debug。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230923172029885-393241684.png" alt="" /></p>
<p>从源码可以看出，在做求交的初始函数中准备了三部分数据，一个是BOPDS_DS，一个是BOPDS_Iterator，还有一部分是缓存的求交工具的数据IntTools_Context。后面将结合DRAW代码对C++源码调试，分析布尔操作中求交数据BOPDS_DS保存的具体数据。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230099.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-23 17:37 <a href="http://www.cppblog.com/eryar/archive/2023/09/23/BOPDS_DS.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>布尔数据 BOPDS_Iterator</title><link>http://www.cppblog.com/eryar/archive/2023/09/18/BOPDS_Iterator.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 18 Sep 2023 13:13:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/18/BOPDS_Iterator.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230092.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/18/BOPDS_Iterator.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230092.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230092.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>布尔数据 BOPDS_Iterator</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中新的布尔工具TKBO相对已经废弃的TKBool代码更规范，更易于理解。与ModelingData和ModelingAlgorithms大的模块组织一样，主要也是数据结构Data Structure+算法Algorithm的组织形式。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230918205604156-1278856157.png" alt="" /></p>
<p>其中BOPDS为布尔中的数据结构部分，BOPAlgo为布尔中的算法部分。理解算法的前提是先理解数据结构DS(Data Structure)，所以先从数据结构入手，来深入理解布尔操作。本文先从简单的数据结构BOPDS_Iterator开始对源码进行分析。</p>
<p>&nbsp;</p>
<h2>2 BOPDS_Iterator</h2>
<p>从类的注释可以看出，迭代器BOPDS_Iterator有以下两个功能：</p>
<p>- 找出包围盒相交的Shape；</p>
<p>- 遍历相交的一对Shape；</p>
<pre class="language-cpp highlighter-hljs"><code>//! The class BOPDS_Iterator is
//! 1.to compute intersections between BRep sub-shapes
//! of arguments of an operation (see the class BOPDS_DS)
//! in terms of theirs bounding boxes
//! 2.provides interface to iterate the pairs of
//! intersected sub-shapes of given type
class BOPDS_Iterator 
{
public:</code></pre>
<p>其中核心的算法在函数Intersect()中，代码如下所示：</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
// function: Intersect
// purpose: 
//=======================================================================
void BOPDS_Iterator::Intersect(const Handle(IntTools_Context)&amp; theCtx,
                               const Standard_Boolean theCheckOBB,
                               const Standard_Real theFuzzyValue)
{
  const Standard_Integer aNb = myDS-&gt;NbSourceShapes();
  // Prepare BVH
  BOPTools_BoxTree aBoxTree;
  aBoxTree.SetSize (aNb);
  for (Standard_Integer i = 0; i &lt; aNb; ++i)
  {
    const BOPDS_ShapeInfo&amp; aSI = myDS-&gt;ShapeInfo(i);
    if (!aSI.HasBRep())
      continue;
    const Bnd_Box&amp; aBox = aSI.Box();
    aBoxTree.Add (i, Bnd_Tools::Bnd2BVH (aBox));
  }
  // Build BVH
  aBoxTree.Build();
  // Select pairs of shapes with interfering bounding boxes
  BOPTools_BoxPairSelector aPairSelector;
  aPairSelector.SetBVHSets (&amp;aBoxTree, &amp;aBoxTree);
  aPairSelector.SetSame (Standard_True);
  aPairSelector.Select();
  aPairSelector.Sort();
  // Treat the selected pairs
  const std::vector&lt;BOPTools_BoxPairSelector::PairIDs&gt;&amp; aPairs = aPairSelector.Pairs();
  const Standard_Integer aNbPairs = static_cast&lt;Standard_Integer&gt; (aPairs.size());
  Standard_Integer iPair = 0;
  const Standard_Integer aNbR = myDS-&gt;NbRanges();
  for (Standard_Integer iR = 0; iR &lt; aNbR; ++iR)
  {
    const BOPDS_IndexRange&amp; aRange = myDS-&gt;Range(iR);
    for (; iPair &lt; aNbPairs; ++iPair)
    {
      const BOPTools_BoxPairSelector::PairIDs&amp; aPair = aPairs[iPair];
      if (!aRange.Contains (aPair.ID1))
        // Go to the next range
        break;
      if (aRange.Contains (aPair.ID2))
        // Go to the next pair
        continue;
      const BOPDS_ShapeInfo&amp; aSI1 = myDS-&gt;ShapeInfo (aPair.ID1);
      const BOPDS_ShapeInfo&amp; aSI2 = myDS-&gt;ShapeInfo (aPair.ID2);
      const TopAbs_ShapeEnum aType1 = aSI1.ShapeType();
      const TopAbs_ShapeEnum aType2 = aSI2.ShapeType();
      Standard_Integer iType1 = BOPDS_Tools::TypeToInteger (aType1);
      Standard_Integer iType2 = BOPDS_Tools::TypeToInteger (aType2);
      // avoid interfering of the shape with its sub-shapes
      if (((iType1 &lt; iType2) &amp;&amp; aSI1.HasSubShape (aPair.ID2)) ||
          ((iType1 &gt; iType2) &amp;&amp; aSI2.HasSubShape (aPair.ID1)))
        continue;
      if (theCheckOBB)
      {
        // Check intersection of Oriented bounding boxes of the shapes
        const Bnd_OBB&amp; anOBB1 = theCtx-&gt;OBB (aSI1.Shape(), theFuzzyValue);
        const Bnd_OBB&amp; anOBB2 = theCtx-&gt;OBB (aSI2.Shape(), theFuzzyValue);
        if (anOBB1.IsOut (anOBB2))
          continue;
      }
      Standard_Integer iX = BOPDS_Tools::TypeToInteger (aType1, aType2);
      myLists(iX).Append (BOPDS_Pair (Min (aPair.ID1, aPair.ID2),
                                      Max (aPair.ID1, aPair.ID2)));
    }
  }
}</code></pre>
<p>在求交函数Intersect中使用BVH快速找出包围盒有相交的每对Shape，并以索引的形式记录下来。从这个函数中可以看出布尔操作是否使用OBB的选项的作用：当不使用OBB时，只以AABB包围盒来检测相交的Shape；当使用OBB时，在AABB的基础上进一步使用包围更紧密的OBB来检测相交，可以排除部分。当相交的模型中以AABB检测就能检测出来的，再打开OBB选项，不会提高性能，反而会有所降低。为了减少这个影响，在IntTools_Context中缓存Caching这些OBB，避免构造OBB带来的性能损失。</p>
<h2>3 Conclusion</h2>
<p>布尔迭代器BOPDS_Iterator通过BVH找出求交的模型中每对包围盒有相交的模型并提供遍历每对包围盒相交的模型的功能，为后面求交作准备。从其代码实现可以看出布尔选项使用OBB对性能提高是有限的，当使用AABB能检测出来的，再使用OBB会降低性能。当使用AABB检测出来相交，但OBB不相交的场景对性能提升明显。</p>
<p>今日是&#8220;九一八事变&#8221;92周年，落后就要挨打，吾辈仍需努力。</p><img src ="http://www.cppblog.com/eryar/aggbug/230092.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-18 21:13 <a href="http://www.cppblog.com/eryar/archive/2023/09/18/BOPDS_Iterator.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构建工具Premake</title><link>http://www.cppblog.com/eryar/archive/2023/09/18/occt_premake.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 18 Sep 2023 12:49:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/18/occt_premake.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230091.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/18/occt_premake.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230091.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230091.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>构建工具Premake</strong></h1>
<p>经常用Visual Studio写一些小程序来验证OpenCASCADE的功能，每次创建项目后都配置头文件，库路径，程序运行时还要配置Debug的环境变量，比较麻烦。也尝试过CMake和QMake，都不太理想。CMake学习曲线陡峭一点，还会生成一堆文件。QMake简单些，但是有的选项不支持。直到看到一个开源的游戏程序Overload，看其编译说明使用了Premake来构建。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230915142632471-1830199348.png" alt="" /></p>
<p>使用IMGUI生成的软件界面比较酷炫，使用Premake生成Visual Studio解决方案。</p>
<h2>1 什么是构建系统</h2>
<p>构建系统（BuildSystem）是用来从源码生成用户可以使用的目标（Targets）的自动化工具。目标可以包括库，可执行文件，或者生成的脚本等等。</p>
<p>项目模块依赖关系维护 ；</p>
<p>目标的可配置化（不同系统：Windows，Mac&#8230;；不同平台：Win32，Win64，Amd64&#8230;）</p>
<p>目标生成的自动化</p>
<h2>2 为什么使用构建系统</h2>
<p>主要用于提高开发人员的效率与稳定，测试与发布的效率</p>
<p>-减少开发人员的知识成本（比如对同一流程，但多种平台，多种开发环境差异化的了解）</p>
<p>-减少项目项目变动的维护成本</p>
<p>&#8211; VisualStudio 的编译选项规则，配置方法</p>
<p>&#8211; Xcode 的编译选项规则，配置方法</p>
<p>&#8211; 其他。。。</p>
<p>-减少模块的维护成本</p>
<p>&#8211; 协同人员对工程文件，目录的修改冲突</p>
<p>减少平台，系统生成的差异化成本（可以简洁地配置不同的平台与系统）</p>
<p>&#8211; 通过简单的配置，可以灵活，快速地添加，修改，更新模块</p>
<p>&#8211; 可以方便的处理依赖关系，提高稳定性和编译速度</p>
<p>使用脚本和配置文件，实现自动清理，生成所需平台，系统，调试环境，测试流程，然后发布版本。</p>
<p>总的来说，就是使生成过程更加简洁，灵活，高效，自动化。</p>
<h2>3 常见的构建系统</h2>
<p>主流的可以跨平台，支持C++的构建系统</p>
<p>- CMake</p>
<p>- Scons</p>
<p>- Premake</p>
<p>其他还有 GNU Make，GNU autotools，Apache Ant（主要用于Java），Gradle（主要用于Java）</p>
<h2>4 什么是Premake</h2>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230915142742856-1890848679.png" alt="" /></p>
<p>Premake 是一种命令工具，通过读取项目脚本，来生成各种开发环境的项目文件。主要用于：</p>
<p>生成开发人员喜欢的平台，工具集（协同开发的人员，可以使用不同的平台和开发工具）</p>
<p>通过脚本保持不同平台，工具集下的项目配置同步（比如新建文件夹，引入新的库文件）</p>
<p>通过脚本来快速更新许多不同的大型代码库，并重新生成项目（比如对依赖的多种著名库的版本更新）</p>
<p>快速升级工具集的版本（比如无缝地从VisualStudio 2010升级到VisualStudio 2019）</p>
<p>目前支持：</p>
<p>Microsoft Visual Studio 2005-2019</p>
<p>GNU Make，包括 Cygwin 和 MinGW</p>
<p>XCode</p>
<p>Codelite</p>
<p>Premake 5.0 目前支持：</p>
<p>32 和 64 位平台</p>
<p>Xbox 360（仅支持Visual Studio）</p>
<p>插件模块可以支持其他语言，框架，和工具集</p>
<p>Premake 是存粹的旧版C应用程序，发布为一个单个的，非常小的exe文件。支持完整的Lua脚本环境</p>
<p>开源地址：https://github.com/premake/premake-core</p>
<p>下载地址：https://premake.github.io/</p>
<p>实例地址：<a href="https://github.com/wuguyannian/tutorial_premake"><u>https://github.com/wuguyannian/tutorial_premake</u></a></p>
<h2>5 使用Premake</h2>
<p>使用premake来构建一个使用了glfw, occt和imgui的程序。从配置文件可以看出，配置比CMake要简单：</p>
<pre class="language-lua highlighter-hljs"><code>workspace "OcctImgui"
    configurations {"Debug", "Release"}
    system "Windows"
    platforms {"Win64"}
    architecture "X64"
    language "C++"
project "OcctImgui"
    kind "ConsoleApp"
    language "C++"
    targetdir "build/bin/%{cfg.buildcfg}"
    objdir "build/obj/%{cfg.buildcfg}"
    files { "**.h",  "**.cpp"}
    -- Header files.
    includedirs
    {
        "C:/OpenCASCADE-7.6.0/opencascade-7.6.0/inc", 
        "C:/glfw-3.3.8/include"
    }
    -- Library files.
    links
    {
        "TKernel", "TKMath", "TKG2d", "TKG3d", "TKGeomBase", "TKGeomAlgo", "TKBRep", "TKTopAlgo", "TKPrim", "TKMesh", "TKService", "TKOpenGl", "TKV3d", 
        "glfw3"
    }
    filter "configurations:Debug"
      defines { "DEBUG" }
      symbols "On"
      libdirs
      {
          "C:/OpenCASCADE-7.6.0/opencascade-7.6.0/win64/vc14/libd", 
          "C:/glfw-3.3.8/lib"
      }
      debugenvs
      {
          "path=%path%;C:/OpenCASCADE-7.6.0/opencascade-7.6.0/win64/vc14/bind"
      }
   filter "configurations:Release"
      defines { "NDEBUG" }
      symbols "Off"
      optimize "On"
      libdirs
      {
          "C:/OpenCASCADE-7.6.0/opencascade-7.6.0/win64/vc14/lib", 
          "C:/glfw-3.3.8/lib"
      }
      debugenvs
      {
          "path=%path%;C:/OpenCASCADE-7.6.0/opencascade-7.6.0/win64/vc14/bin"
      }</code></pre>
<p>通过premake生成的Visual Studio解决方案很干净，没有多余的文件。后面再要写一个小的验证程序时，只需要复制premake5.lua修改一下即可，很方便。对于小的验证程序来说，使用premake是理想的构建工具。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230915142947056-1471060502.png" alt="" /></p>
<p id="1694759403678"></p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230091.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-18 20:49 <a href="http://www.cppblog.com/eryar/archive/2023/09/18/occt_premake.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE GLFW IMGUI</title><link>http://www.cppblog.com/eryar/archive/2023/09/18/occt_imgui.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 18 Sep 2023 12:48:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/18/occt_imgui.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230090.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/18/occt_imgui.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230090.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230090.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE GLFW IMGUI</strong></h1>
<p>如果从事过C++ Windows客户端开发，大家对MFC、Qt、DuiLib、WxWidgets等各种DirectUI应该有了解，本篇给大家介绍一个超级轻量级的C++开源跨平台图形界面框架ImGUI. ImGUI主要用于游戏行业，所有的控件都需要手绘实现，当然性能也是满满的，毕竟是直接用dx/opengl来实现。ImGUI仓库：https://github.com/ocornut/imgui</p>
<p>ImGUI又称为Dear ImGui，它是与平台无关的C++轻量级跨平台图形界面库，没有任何第三方依赖，可以将ImGUI的源码直接加到项目中使用，也可以编译成dll, ImGUI使用DX或者OpenGL进行界面渲染，对于画面质量要求较高，例如客户端游戏，4k/8k视频播放时，用ImGUI是很好的选择，当然，你得非常熟悉DirectX或者OpenGL，不然就是宝剑在手，屠龙无力。相对于Qt、MFC、DuiLib、SOUI等，ImGUI的拓展性更好，也更轻量级，当然对于开发者的要求也更高.ImGUI没有类似于Qt/MFC这种，可以拖拽控件进行搭建界面，ImGUI的所有控件都必须手写实现。ImGUI的demo基本提供了所有控件、图表等的实现，源码也有，可以对照的学习。在PC端技术选型时，如果公司有音视频、图形图像、4k/8k视频业务，或者一些简单的UI可以考虑一下使用ImGUI，毕竟是直接使用DX/OpenGL来进行绘制渲染，其它功能就直接使用C++来实现。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230914204541555-1083570386.png" alt="" /></p>
<p>OpenCASCADE提供了一个GLFW的示例程序，将OpenCASCADE与IMGUI集成起来，对于实现一些简单的小的三维应用程序的UI，有满满的科技感。很多游戏相关的小程序都是使用IMGUI来做界面。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230914204603123-161912602.png" alt="" /></p>
<p>其中OpenCASCAE开源的光线追踪程序CADRays的UI就是用IMGUI实现的：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230914204612536-1993428430.png" alt="" /></p>
<p>IMGUI也支持Docking，常见的控件都有，并且也支持跨平台，只依赖OpenGL，生成的程序体积很小。</p>
<p>&nbsp;</p>
<p>使用GLFW配置IMGUI可以实现跨平台的界面开发，对于不复杂的应用程序是个不错的选择。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230090.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-18 20:48 <a href="http://www.cppblog.com/eryar/archive/2023/09/18/occt_imgui.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几何内核与数学</title><link>http://www.cppblog.com/eryar/archive/2023/09/18/occt_math.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 18 Sep 2023 12:47:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/09/18/occt_math.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230089.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/09/18/occt_math.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230089.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230089.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>几何内核与数学</strong></h1>
<h2>1 概述</h2>
<p>从1950年第一台图形显示器（美国麻省理工大学MIT旋风I号Whirlwind I）的诞生，到1962年MIT林肯实验室的Ivan E. Sutherland发表题为&#8220;Sketchpad: 一个人机交互的图形系统&#8221;确定计算机图形学作为独立科学分支。经过70多年的发展，计算机图形学中的几何造型技术成了现在的几何内核。</p>
<p>数学是我们从小学、中学到大学一直都在学习的课程，是现代科技的理论基础，是创新的源泉。几何内核与数学的联系非常紧密，结合开源几何内核opencascade谈谈学习过程和数学的认识。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082718381-749066138.png" alt="" /></p>
<p id="1694478442098"></p>
<h2>2 高中时代</h2>
<p>在刚学习的时候，总是先从简单的开始入手。比如，先看看直线、圆是什么，怎么显示出来。长方体、圆柱体等怎么用BREP进行表示，怎么显示在屏幕上。这个时候考虑问题是常人思维，我认为是高中时代。</p>
<p>比如，怎么计算二维直线与圆的交点呢？上过高中的都学过，联立直线与圆的一元二次方程组，将解方程组的代码固化在代码中。当计算椭圆与圆的交点时，也做同样的处理，就是多写点代码。高中几何学得好的，可能会说我可以用向量，向量的方法会比代数的方法速度要快。</p>
<p>再比如，怎么计算曲线的弧长和曲面的面积呢？计算曲线可以通过对曲线进行采样点，将点连成多段线，再分别计算每小段线段的长度累加就是了。面积咱们也可以类似处理，将曲面离散成三角形或四边形，再将这些三角形或四边形面积累加。这些方法都能实现，但是性能、精度都会成为问题。要计算得精度高，采样就要密，就会带来性能问题。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082736855-663600608.png" alt="" /></p>
<h2>3 大学时代</h2>
<p>到了大学时代学习过高等数学、线性代数等，有了更有力的工具来解决更一般问题。面向对象编程和数学也是相通的，就是将问题抽象的能力。这个时候眼中没有直线、圆、B样条曲线、平面、球面、B样条曲面等等，只有线Curve和面Surface。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082751089-1682592835.png" alt="" /></p>
<p>再进一步抽象，将线Curve与一元函数F(x)对应，将面Surface与多元函数对应F(x,y)，与数学建立了联系。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082759877-1574928138.png" alt="" /></p>
<p>这时就可以使用数学工具对问题进行处理了。如计算曲线弧长，就变成一个对切向量的积分。曲线切向量需要计算一阶导数，《The NURBS Book》等书上计算B样条微分的公式就有用，看待B样条曲线和看待圆一样了。</p>
<p>积分的计算可以使用数值方法，如Newton-Cotes或Gauss积分法，使用更少的迭代获得更高的精度，程序优化的方向也清晰。</p>
<p>&nbsp;</p>
<p>从opencascade的类math_Function和math_MultipleVarFunction可以可以看出，许多几何问题都抽象成了数学问题。有很多人问我，怎样才算入门了opencascade呢？那就是思考问题的方式转换成数学的方式，我觉得就算入门了。再具体点可操作点呢，<strong>首先就是数据结构的入门，掌握BREP边界表示法，如在圆柱面Surface上框出一个小面片Face，能正常显示出来就算理解opencascade中的BREP结构。其次是几何算法入门，就是将从math_Function和math_MultipleVarFunction所有的派生类的数学公式写出来。</strong>这两点动手做完，我觉得可以算入门了。</p>
<p>&nbsp;</p>
<h2>4 研究生时代</h2>
<p>如果大学时代我们掌握了微分、积分、线性代数，到研究生时代应该掌握变分、偏微分方程、最优化理论等等。如偏微分方程用于构造过渡曲面，曲线曲面拟合光顺最后都抽象成带约束的非线性方程组的求解。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082824272-2076450802.png" alt="" /></p>
<p>到研究生时代因为掌握的数学工具，会处理看上去很简单但处理起来更复杂的问题：如上图所示的过渡Blend，以及蒙面Skinning和扫掠Sweep等。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202309/534255-20230912082833044-1553004147.png" alt="" /></p>
<p>这时也可以有一些智能算法，如遗传算法、蚁群，粒子群PSO优化算法，AI等，使用仿生、遗传变异等手段使求解迭代更快收敛。为传统优化算法提供初始解，使传统算法能更快、更准地找到解析解，不至于深陷局部最优解中不能自拔。在opencascade中也有相关实例，如math_PSO。这些智能算法有随机性，不像解析算法那样满足一定约束条件必定会找到相对准确答案。只能作为像曲面求交之网格离散法，作为参数迭代法的预处理。到这个时代，查看相关技术论文毫无压力，面对一般的几何问题都应该可以从容应对。和研究生一样面对的问题更加具体，会钻牛角尖。</p>
<h2>5 总结</h2>
<p>综上所述，几何内核可以看成一个数学库的子集，只是在几何图形上的应用。学习几何内核的过程类比于学生时代掌握的数学工具。在高中时代，看问题很具体，只能case by case的处理，功能能做出来。到大学时代，有了一定的抽象能力（与面向对象编程一样），看问题具有一般性，能使用更高级点的数学工具来处理，有能力来兼顾精度和性能。到了研究生时代，就不怕别人来卡脖子，甚至能超越别人。这个时候不要给自己设限，找准喜欢的方向去钻牛角尖，终会有所成。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230089.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-09-18 20:47 <a href="http://www.cppblog.com/eryar/archive/2023/09/18/occt_math.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面交线分类</title><link>http://www.cppblog.com/eryar/archive/2023/08/21/intpatch_line.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 20 Aug 2023 16:27:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/08/21/intpatch_line.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230019.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/08/21/intpatch_line.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230019.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230019.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>O</strong><strong>penCASCADE</strong><strong>曲面交线分类</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>e</u><u>ryar@163.com</u></a></p>
<p><strong>Abstract.</strong> OpenCascade classify the intersection line between two surfaces. A intersection line may be either geometric: line, circle, ellipse, parabola, hyperbola as defined in the class GLine, or analytic as defined in the class ALine, or defined by a set of points(coming from a walking algorithm) as defined in the class WLine. Or described by a restriction line on one of the surfaces as RLine.</p>
<p><strong>Key Words.</strong> Surface Intersection, Intersection Line</p>
<h2>1 Introduction</h2>
<p>OpenCASCADE中对两个曲面求交得到的交线进行了分类，如下类图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001223218-690205493.png" alt="" /></p>
<p>交线总共分为四类：</p>
<ul>
<li>ALine：Analytic解析曲线，主要为两个二次曲面求交所得，如圆柱面、球面、圆锥面等之间的交线；</li>
<li>GLine：Geometric几何曲线，即交线可以表示成简单的二次曲线，如直线、圆、抛物线等；</li>
<li>WLine：Walking追踪法得到的交线，保存了追踪路线上的两个曲面的交点；</li>
<li>RLine：Restriction受限交线，这条交线可能只在一个面上；</li>
</ul>
<p>下面我们使用Tcl脚本在DRAW中验证一下这四类交线的来源，加深对曲面求交算法的理解。分类带来了麻烦，可以带着问题：为什么要分这几种类型？有什么好处？来看这篇文章。要用好开源的东西，其实要求还是很高的，需要对源码有相对深入的理解。</p>
<h2>2 ALine</h2>
<p>Analytic交线是二次曲面求交所得，二次曲面是因为可以统一使用二次型来表示的解析曲面，也是《解析几何》中研究的主要内容。我们可以在DRAW中构造圆柱面与圆锥面求交验证一下。TCL脚本如下：</p>
<pre class="language-perl highlighter-hljs"><code># Test for IntPatch_ALine.
# Geometry surfaces.
cylinder s1 0 0 0 1 1 1 2
cone s2 0 0 0 0 0 1 1 0 0 30 3
# Topology faces
mkface f1 s1 0 2*pi -8 8
mkface f2 s2 0 2*pi -5 5
# Intersection.
bop f1 f2
bopsection r
# Display result.
vdisplay f1 f2 r</code></pre>
<p>生成结果如下图所示，其中红色为交线：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001336985-696556410.png" alt="" /></p>
<p>DEBUG源码可以看到是使用类IntPatch_ImpImpIntersection&nbsp;计算求交，即两个解析曲面求交算法类。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001348443-1156515472.png" alt="" /></p>
<h2>3 GLine</h2>
<p>Geometric几何曲线形式简单，如果交线用几何曲线来表示，对于后续算法有好处。如平面与圆锥面求交线，圆柱面与圆柱面求交等，都会得到几何曲线。将上面的圆柱面换成平面与圆锥面求交我们可以在DRAW验证经典的圆锥与平面交线：根据平面位置不同，可以得到圆、椭圆、双曲线等几何曲线。TCL脚本如下所示：</p>
<pre class="language-perl highlighter-hljs"><code># Test for IntPatch_GLine.
cone s1 0 0 0 0 0 1 30 3
plane s2 0 0 0 0 0 1 
mkface f1 s1 0 2*pi -5 5
mkface f2 s2 -8 8 -8 8
bop f1 f2
bopsection r
vdisplay f1 f2 r</code></pre>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001438884-1897406853.png" alt="" /></p>
<p>计算交线结果如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001448271-2141213116.png" alt="" /></p>
<p>我们改变平面的法向，使其斜着与圆锥面求交，会得到椭圆：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001457918-1196666057.png" alt="" /></p>
<p>还可以得到双曲线、抛物线等，同学们可以自己尝试一下。</p>
<h2>4 WLine</h2>
<p>对于NURBS曲面求交，一般会使用Marching方法，国内教材翻译为追踪法。在看《地球脉动》时，注意到对于大草原上的水牛、大象等动物成群结队的迁徙使用了这个词，这个词的字面意思有行进、行军，列队行进之意，如果结合opencascade中的walking感觉翻译为<strong>行进法</strong>更贴切，因为在opencascade中对于求交专门有个package名为IntWalk，其中类IntWalk_PWalking来使用marching method对两个参数曲面进行求交。Walk有行走之意，所以对于使用Walk方法得到的交线命名为WLine。对于NURBS曲面求交及二次曲面与NURBS曲面求交，使用了Marching方法，WLine的来源是清晰的。继续使用上面的脚本，只需要将上述两个面转换成NURBS曲面即可触发Marching法进行求交。TCL脚本如下：</p>
<pre class="language-perl highlighter-hljs"><code># Test for IntPatch_WLine.
cone s1 0 0 0 0 0 1 30 3
plane s2 0 0 0 1 1 2 
mkface f1 s1 0 2*pi -5 5
mkface f2 s2 -8 8 -8 8
nurbsconvert f1 f1
nurbsconvert f2 f2
bop f1 f2
bopsection r
vdisplay f1 f2 r</code></pre>
<p>通过IntWalk_PWalking<strong>行进法</strong>配合三参数迭代法，将行进过程中的交点都保存在WLine中。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001538010-1065001893.png" alt="" /></p>
<p>虽然结果与上面看上去一样，内部交线已经不是简单的几何曲线了。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001548231-222441506.png" alt="" /></p>
<h2>5 RLine</h2>
<p>Restriction交线是受限交线，这种类型的交线只会位于一个面上。这里我们构造一个平面及与平面重叠的一个NURBS曲面来求交进行解释。TCL脚本如下：</p>
<pre class="language-perl highlighter-hljs"><code># Test for IntPatch_RLine.
plane s1 0 0 0 0 0 1
plane s2 0 0 0 0 0 1
mkface f1 s1 -5 5 -5 5
mkface f2 s2 -8 8 -8 8
nurbsconvert f1 f1
bop f1 f2
bopsection r
vdisplay f1 f2 r</code></pre>
<p>DEBUG会发现这两个曲面的交线为RLine，并在生成RLine时指定交线属于哪个曲面，是在S1曲面SetArcOnS1还是在S2曲面SetArcOnS2：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001638598-2032902095.png" alt="" /></p>
<p>生成交线如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001650132-2110311711.png" alt="" /></p>
<p>当然可以使用RLine来判断两个曲面是否有重叠，但是在opencascade中两个曲面重叠叫Tangent Face，可以将上述NURBS面不转换，还是使用两个重叠平面来验证：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001700588-916489887.png" alt="" /></p>
<p>求交最后对交线数据归并时的代码有点不敢恭维：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230821001710633-889685715.png" alt="" /></p>
<h2>6 Conclusion</h2>
<p>综上所述，对两个曲面求交得到的交线进行分类，避免交线都是NURBS曲线，可以是简单的二次曲线，提高后续算法性能。在理解源码的基础上，可以根据实际应用场景选择高效的算法。如若只是求两个模型之间的交线，可以直接使用曲面求交算法，一般情况下性能还是不错的。当然理解源码后，可以结合实际应用场景可以对求交算法做进一步优化。</p>
<p>要深入理解opencascade源码，熟练使用DRAW是一个相对容易的路线。因为在DRAW中可以使用Tcl脚本快速验证各种想法，甚至直接DEBUG源码，从表向深入到与源码作者直接对话。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230019.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-08-21 00:27 <a href="http://www.cppblog.com/eryar/archive/2023/08/21/intpatch_line.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>性能提升-BVH层次包围体</title><link>http://www.cppblog.com/eryar/archive/2023/08/16/occt_bvh.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Wed, 16 Aug 2023 15:28:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/08/16/occt_bvh.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230016.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/08/16/occt_bvh.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230016.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230016.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>性能提升-BVH层次包围体</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<p><strong>Abstract.</strong> &nbsp;OpenCASCADE provides BVH to achieve high performance in AIS of visualization module. To understand BVH usage will help us to understand many code of opencascade.</p>
<p><strong>Key Words.</strong> BVH, Bounding Volume Hierarchy, LBVH, SAH Algorithm</p>
<h2>1 Introduction</h2>
<p>层次包围体技术 (BVH) 指的是将所有包围体分层逐次地再次包围，获得一个更大的包围体，直到包围住所有物体。实际上，它是一个树形结构，因此可以仿照树的结构，将两个或三个小的包围体包围成一个更大的包围体，以此类推。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230816231907497-786197437.png" alt="" /></p>
<p>BVH是一种以物体BV为基础进行划分的结构。它由根节点、内部节点和叶子节点组成。其中叶子节点存放物体，每个非叶子节点都有包围体，父节点可以把子节点包围起来。每个非叶子节点的包围体大小，是它所包含的所有物体的包围体的总和，所以它在空间上比较紧凑，非常适用于需要大量求相交测试的应用场景，如光线追踪、碰撞检测、射线相交测试之类的应用场合中。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230816231928196-399409810.png" alt="" height="248" width="550" /></p>
<p>&nbsp;</p>
<p>BVH在OpenCASCADE中也有广泛地应用，如开源版本中的模型快速碰撞检测，使用类BRepExtrema_ShapeProximity. 模型选择操作，光线跟踪等算法中都有应用。在</p>
<p><a href="https://www.cnblogs.com/opencascade/p/6804446.html"><u>https://www.cnblogs.com/opencascade/p/6804446.html</u></a></p>
<p>中介绍如何遍历BVH树，本文主要介绍BVH使用方法。</p>
<h2>2 BVH</h2>
<p>OpenCASCADE中的BVH是相对独立的一个包，是作者根据论文实现的纯C++版本移植过来的。在DRAW中的QA品质保证Bugs中提供了BVH的使用示例。</p>
<h3>2.1 BVH_Set</h3>
<p>首先要定义层次包围盒的集合Set来构造BVH树，从BVH_Set基类派生的集合是可以直接使用的：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230816232017300-2093555063.png" alt="" /></p>
<p>如可以直接使用BVH_Triangulation，也可以直接使用BVH_BoxSet：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230816232024543-1233759109.png" alt="" /></p>
<p>从这些类名中，我们可以看出在求模型间极值距离Extrema，三维可视化Graphic3d及Select3D拾取及布尔操作BOPTools中都有BVH的应用。</p>
<p>将元素通过Add函数添加到BVH集合后，调用BVH()函数就可以构造BVH树。</p>
<h3>2.2 BVH_Traverse</h3>
<p>对于单个BVH的遍历提供类BVH_Traverse，一般的应用场景如求距离一点P最近的模型，或者位于某个空间范围内的所有模型。代码如下所示：</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
//function : ShapeSelector
//purpose : Implement the simplest shape's selector
//=======================================================================
class ShapeSelector :
  public BVH_Traverse &lt;Standard_Real, 3, BVH_BoxSet &lt;Standard_Real, 3, TopoDS_Shape&gt;, Standard_Boolean&gt;
{
public:
  //! Constructor
  ShapeSelector() {}
  //! Sets the Box for selection
  void SetBox (const Bnd_Box&amp; theBox)
  {
    myBox = Bnd_Tools::Bnd2BVH (theBox);
  }
  //! Returns the selected shapes
  const NCollection_List&lt;TopoDS_Shape&gt;&amp; Shapes () const { return myShapes; }
public:
  //! Defines the rules for node rejection by bounding box
  virtual Standard_Boolean RejectNode (const BVH_Vec3d&amp; theCornerMin,
                                       const BVH_Vec3d&amp; theCornerMax,
                                       Standard_Boolean&amp; theIsInside) const Standard_OVERRIDE
  {
    Standard_Boolean hasOverlap;
    theIsInside = myBox.Contains (theCornerMin, theCornerMax, hasOverlap);
    return !hasOverlap;
  }
  //! Defines the rules for leaf acceptance
  virtual Standard_Boolean AcceptMetric (const Standard_Boolean&amp; theIsInside) const Standard_OVERRIDE
  {
    return theIsInside;
  }
  //! Defines the rules for leaf acceptance
  virtual Standard_Boolean Accept (const Standard_Integer theIndex,
                                   const Standard_Boolean&amp; theIsInside) Standard_OVERRIDE
  {
    if (theIsInside || !myBox.IsOut (myBVHSet-&gt;Box (theIndex)))
    {
      myShapes.Append (myBVHSet-&gt;Element (theIndex));
      return Standard_True;
    }
    return Standard_False;
  }
protected:
  BVH_Box &lt;Standard_Real, 3&gt; myBox;         //!&lt; Selection box
  NCollection_List &lt;TopoDS_Shape&gt; myShapes; //!&lt; Selected shapes
};</code></pre>
<p>主要是从类BVH_Traverse派生并重写两个虚函数RejectNode()和Accept()，即在RejectNode()中定义排除结点的规则，在Accept()中处理满足条件的情况。</p>
<h3>2.3 BVH_PairTraverse</h3>
<p>对于两个BVH的遍历提供类BVH_PairTraverse，一般的应用场景有求两个Mesh之间的最近距离，判断两个Mesh之间是否有碰撞等。</p>
<pre class="language-cpp highlighter-hljs"><code>//=======================================================================
//function : MeshMeshDistance
//purpose : Class to compute the distance between two meshes
//=======================================================================
class MeshMeshDistance : public BVH_PairDistance&lt;Standard_Real, 3, BVH_BoxSet&lt;Standard_Real, 3, Triangle&gt;&gt;
{
public:
  //! Constructor
  MeshMeshDistance() {}
public:
  //! Defines the rules for leaf acceptance
  virtual Standard_Boolean Accept (const Standard_Integer theIndex1,
                                   const Standard_Integer theIndex2) Standard_OVERRIDE
  {
    const Triangle&amp; aTri1 = myBVHSet1-&gt;Element (theIndex1);
    const Triangle&amp; aTri2 = myBVHSet2-&gt;Element (theIndex2);
    Standard_Real aDistance = TriangleTriangleSqDistance (aTri1._Node1, aTri1._Node2, aTri1._Node3,
                                                          aTri2._Node1, aTri2._Node2, aTri2._Node3);
    if (aDistance &lt; myDistance)
    {
      myDistance = aDistance;
      return Standard_True;
    }
    return Standard_False;
  }
};</code></pre>
<p>主要也是从BVH_PairTraverse派生并重写两个虚函数RejectNode()和Accept()。</p>
<h3>2.4 BVH_Builder</h3>
<p>关于BVH的构造提供多种Builder，默认是使用基于SAH算法的BVH_BinnedBuilder来构造BVH树，如果要切换不同的构造器，可以在BVH集合的构造函数中传入一个。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230816232215793-1477285548.png" alt="" /></p>
<p>下面给出求两个Mesh之间最近距离的示例代码：</p>
<p>&nbsp;</p>
<pre class="language-cpp highlighter-hljs"><code> // Define BVH Builder
  opencascade::handle &lt;BVH_LinearBuilder &lt;Standard_Real, 3&gt; &gt; aLBuilder =
      new BVH_LinearBuilder &lt;Standard_Real, 3&gt;();
  // Create the ShapeSet
  opencascade::handle &lt;BVH_BoxSet &lt;Standard_Real, 3, Triangle&gt; &gt; aTriangleBoxSet[2];
  for (Standard_Integer i = 0; i &lt; 2; ++i)
  {
    aTriangleBoxSet[i] = new BVH_BoxSet &lt;Standard_Real, 3, Triangle&gt; (aLBuilder);
    TopTools_IndexedMapOfShape aMapShapes;
    TopExp::MapShapes (aShape[i], TopAbs_FACE,   aMapShapes);
    for (Standard_Integer iS = 1; iS &lt;= aMapShapes.Extent(); ++iS)
    {
      const TopoDS_Face&amp; aF = TopoDS::Face (aMapShapes(iS));
      TopLoc_Location aLoc;
      const Handle(Poly_Triangulation)&amp; aTriangulation = BRep_Tool::Triangulation(aF, aLoc);
      const int aNbTriangles = aTriangulation-&gt;NbTriangles();
      for (int iT = 1; iT &lt;= aNbTriangles; ++iT)
      {
        const Poly_Triangle aTriangle = aTriangulation-&gt;Triangle (iT);
        // Nodes indices
        Standard_Integer id1, id2, id3;
        aTriangle.Get (id1, id2, id3);
        const gp_Pnt aP1 = aTriangulation-&gt;Node (id1).Transformed (aLoc.Transformation());
        const gp_Pnt aP2 = aTriangulation-&gt;Node (id2).Transformed (aLoc.Transformation());
        const gp_Pnt aP3 = aTriangulation-&gt;Node (id3).Transformed (aLoc.Transformation());
        BVH_Vec3d aBVHP1 (aP1.X(), aP1.Y(), aP1.Z());
        BVH_Vec3d aBVHP2 (aP2.X(), aP2.Y(), aP2.Z());
        BVH_Vec3d aBVHP3 (aP3.X(), aP3.Y(), aP3.Z());
        BVH_Box&lt;Standard_Real, 3&gt; aBox;
        aBox.Add (aBVHP1);
        aBox.Add (aBVHP2);
        aBox.Add (aBVHP3);
        aTriangleBoxSet[i]-&gt;Add (Triangle (aBVHP1, aBVHP2, aBVHP3), aBox);
      }
    }
    // Build BVH
    aTriangleBoxSet[i]-&gt;Build();
  }
  // Initialize selector
  MeshMeshDistance aDistTool;
  // Select the elements
  aDistTool.SetBVHSets (aTriangleBoxSet[0].get(), aTriangleBoxSet[1].get());
  Standard_Real aSqDist = aDistTool.ComputeDistance();
  if (!aDistTool.IsDone())
    std::cout &lt;&lt; "Not Done" &lt;&lt; std::endl;
  else
    theDI &lt;&lt; "Distance " &lt;&lt; sqrt (aSqDist) &lt;&lt; "\n";</code></pre>
<h2>3 Conclusion</h2>
<p>准确、稳定、高效是高品质几何内核的目标，我们在开发软件时，也要时刻追求这个目标。而BVH层次包围盒技术是提升性能的一种方式，理解BVH的使用，我们可以理解opencascade中快速求极值Extrema，交互选择SelectMgr等很多代码。甚至我们也可以参与贡献，如将</p>
<pre class="language-cpp highlighter-hljs"><code>Standard_Boolean BRepExtrema_Poly::Distance (const TopoDS_Shape&amp; S1, const TopoDS_Shape&amp; S2,
                                             gp_Pnt&amp; P1, gp_Pnt&amp; P2, Standard_Real&amp; dist)</code></pre>
<p>这个O(N^2)的改造成BVH的来对比一下性能。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230016.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-08-16 23:28 <a href="http://www.cppblog.com/eryar/archive/2023/08/16/occt_bvh.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>性能提升-空间二叉查找树</title><link>http://www.cppblog.com/eryar/archive/2023/08/06/NCollection_UBTree.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 06 Aug 2023 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/08/06/NCollection_UBTree.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/230005.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/08/06/NCollection_UBTree.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/230005.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/230005.html</trackback:ping><description><![CDATA[<h2 style="text-align: center;"><strong>性能提升-空间二叉查找树</strong></h2>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<p><strong>Abstract. </strong>&nbsp;OpenCASCADE provides NCollection_UBTree to achieve high performance search overlapped boxes. The algorithm of unbalanced binary tree of overlapped bounding boxes. Once the tree of boxes &nbsp;of geometric objects is constructed, the algorithm is capable of fast geometric selection of objects. &nbsp;The tree can be easily updated by adding to it a new object with bounding box. The time of adding to the tree &nbsp;of one object is O(log(N)), where N is the total number of &nbsp;objects, so the time &nbsp;of building a tree of &nbsp;N objects is O(N(log(N)). The search time of one object is O(log(N)). Defining &nbsp;various classes &nbsp;inheriting NCollection_UBTree::Selector &nbsp;we can perform various kinds of selection over the same b-tree object.</p>
<p><strong>Key Words.</strong> Unbalanced Binary Tree, Binary Search Tree, Binary Sort Tree, Bounding Box</p>
<h2>1 Introduction</h2>
<p>非平衡二叉树（Unbalanced Binary Tree）又叫二叉查找树（Binary Search Tree）或二叉排序树（Binary Sort Tree）。它的定义很简单，就是左子树上所有节点的值都要小于根节点上的值。右子树上所有节点值都要大于根节点上的值。在二叉查找树上执行操作时间与树的高度成正比。对于一棵含有n个结点的完全二叉树，这些操作的最坏情况运行时间为O(lg(n))。但是如果树是含n个结点的线性链，则这些操作的最坏的情况运行时间为O(n)。一棵随机构造的二叉查找树的期望高度为O(lg(n))，从而这种树上操作的平均时间为O(lg(n))。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202308/534255-20230806184735309-333339535.png" alt="" /></p>
<p>几何搜索（geometry searching）大致分两类：一类是区域搜索问题（range searching problem），另一类是点的定位问题（point location problem）。区域搜索问题要回答的是给定一个区域，看有多少模型属于这个区域。当然，我们可以对所有模型进行遍历，这种算法时间复杂度为O(N)，效率不高。常见的高效的区域搜索算法有k-D树，k-D树就是一种多维的平衡二叉树。还有比较常见的KNN问题，这些都是计算几何处理的问题。</p>
<p>OpenCASCADE中提供一种空间查找二叉树算法NCollection_UBTree，字面意思是非平衡二叉树Unbalanced Binary Tree。把上图中的数字换成包围盒，构造二叉查找树。为了解决查找二叉树单链问题，加入随机处理，可以使查找性能达到O(log(N))，相对普通遍历速度而言还是不错的。本文结合示例代码说明如何使用这个非平衡二叉树。</p>
<h2>2 Example</h2>
<p>在OpenCASCADE中有多个函数来实现将很多无序边Edges连接成Wire，需要查询一条边Edge的一个顶点Vertex在一定精度范围内相连的顶点Vertex有哪些？</p>
<p>首先，实现一个选择类，通过选择类来进行过滤：</p>
<pre class="language-cpp highlighter-hljs"><code>typedef NCollection_UBTree&lt;Standard_Integer, Bnd_Box&gt; BoxTree;
typedef NCollection_UBTreeFiller&lt;Standard_Integer, Bnd_Box&gt; BoxTreeFiller;
class BoxSelector : public BoxTree::Selector
{
public:
    BoxSelector(const TColgp_SequenceOfPnt&amp; thePoints, Standard_Real theTolerance)
        : Selector()
        , myPoints(thePoints)
        , myTolerance(theTolerance)
    {
    }
    virtual Standard_Boolean Reject(const Bnd_Box&amp; theBox) const
    {
        return theBox.IsOut(myBox);
    }
    virtual Standard_Boolean Accept(const Standard_Integer&amp; theIndex)
    {
        if (theIndex &gt; myPoints.Size() || theIndex == myIndex)
        {
            return Standard_False;
        }
        const gp_Pnt&amp; aPnt = myPoints.Value(theIndex);
        if (aPnt.SquareDistance(myPnt) &lt; myTolerance)
        {
            myResultIndex.Append(theIndex);
            return Standard_True;
        }
        return Standard_False;
    }
    void SetCurrentPoint(const gp_Pnt&amp; thePnt, Standard_Integer theIndex)
    {
        myPnt = thePnt;
        myBox.Add(thePnt);
        myIndex = theIndex;
    }
    const TColStd_ListOfInteger&amp; GetResultIndex() const
    {
        return myResultIndex;
    }
    void ClearResultIndex()
    {
        myResultIndex.Clear();
    }
protected:
private:
    const TColgp_SequenceOfPnt&amp; myPoints;
    gp_Pnt myPnt;
    Bnd_Box myBox;
    Standard_Integer myIndex;
    Standard_Real myTolerance;
    TColStd_ListOfInteger myResultIndex;
};</code></pre>
<p>主要实现两个抽象函数Reject()和Accept()，以及设置当前选择器的状态。Reject()函数用来判断要查找的Box与当前空间范围的状态，如果在外，则返回True。当两个Box有相交时，会调用Accept()函数，在此函数中判断两个点的距离是否在容差范围内，若在容差范围内，则将点记录起来。主函数main代码如下：</p>
<pre class="language-cpp highlighter-hljs"><code>int main(int argc, char* argv[])
{
    // Fill tree with random points.
    BoxTree aBoxTree;
    BoxTreeFiller aTreeFiler(aBoxTree);
    math_BullardGenerator aRandom;
    TColgp_SequenceOfPnt aPoints;
    for (Standard_Integer i = 1; i &lt;= 100; ++i)
    {
        gp_Pnt aPnt(aRandom.NextReal(), aRandom.NextReal(), aRandom.NextReal());
        aPoints.Append(aPnt);
        Bnd_Box aBox;
        aBox.Add(aPnt);
        aTreeFiler.Add(i, aBox);
    }
    aTreeFiler.Fill();
    // Query points near the given point.
    BoxSelector aSelector(aPoints, 0.1);
    for (Standard_Integer i = aPoints.Lower(); i &lt;= aPoints.Upper(); ++i)
    {
        const gp_Pnt&amp; aPnt = aPoints.Value(i);
        aSelector.SetCurrentPoint(aPnt, i);
        Standard_Integer aSize = aBoxTree.Select(aSelector);
        if (aSize &gt; 0)
        {
            std::cout &lt;&lt; "Search Point : " &lt;&lt; aPnt.X() &lt;&lt; " \t " &lt;&lt; aPnt.Y() &lt;&lt; " \t " &lt;&lt; aPnt.Z() &lt;&lt; std::endl;
            const TColStd_ListOfInteger&amp; aResult = aSelector.GetResultIndex();
            for (TColStd_ListOfInteger::Iterator aIt(aResult); aIt.More(); aIt.Next())
            {
                const gp_Pnt&amp; aPoint = aPoints.Value(aIt.Value());
                std::cout &lt;&lt; "Target Point : " &lt;&lt; aPoint.X() &lt;&lt; " \t " &lt;&lt; aPoint.Y() &lt;&lt; " \t " &lt;&lt; aPoint.Z() &lt;&lt; std::endl;
            }
            std::cout &lt;&lt; "=============================" &lt;&lt; std::endl;
        }
        aSelector.ClearResultIndex();
    }
    return 0;
}</code></pre>
<p>先用随机函数随机生成100个点，并将点通过BoxTreeFiller添加到查找树aBoxTree中，调用Fill函数构造查找树。</p>
<p>再使用类BoxSelector来进行快速查找，查找之前先设置当前点及包围盒。然后调用aBoxTree.Select(aSelector)进行查找。</p>
<h2>3 Conclusion</h2>
<p>类NCollection_UBTree通过构造包围盒的非平衡二叉树来加快区域搜索速度。如何提高搜索速度，是计算几何处理的范畴。在OpenCASCADE中这个类使用场景比较多，如将无序边构造成Wire时都用这个类：BRepLib_MakeWire::Add(const TopTools_ListOfShape&amp; L), ShapeAnalysis_FreeBounds::ConnectEdgesToWires()。包括后面引入的BVH都是为了提高搜索速度，在合适的场景中多使用这些算法，会对程序性能的提升有很大帮助。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/230005.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-08-06 18:53 <a href="http://www.cppblog.com/eryar/archive/2023/08/06/NCollection_UBTree.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之追踪法</title><link>http://www.cppblog.com/eryar/archive/2023/05/30/IntWalk_PWalking.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 29 May 2023 16:39:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/30/IntWalk_PWalking.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229911.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/30/IntWalk_PWalking.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229911.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229911.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之追踪法</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>朱心雄等著《自由曲线曲面造型技术》书中对曲面求交的追踪法（Marching method）有详细介绍，首先曲面求交追踪法的提出是1990年R.E. BARNHILL和S.N. KERSEY的一篇论文：A marching method for parametric surface/surface intersection感兴趣的可以下载来看看原文：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230530003529121-1689691047.png" alt="" /></p>
<p>1990年我才几岁，那时家里有黑白电视机已经算不错的。对于一般NURBS曲面的求交，先用分割离散法求得交线的拓朴结构和交点的估计值，然后再应用迭代法根据估计值求得精确交点。如果认为交点分布不够细密，可以对网格进行加密，再应用迭代法得到新的精确交点，由此可以获得完整、致密的精确交线，而无需应用追踪法。</p>
<h2>2 追踪法的原理</h2>
<p>追踪法的原理：假设两曲面间共有N个交线环，先通过某种求交方法确定各交线环上的一个交点，然后以该交点为初始交点，根据交线的几何性质，按照一定步长计算该条交线上下一交点的近似值，再应用迭代法求得精确交点。沿交线走向不断前进，直到遍历整条交线。追踪法的优点是在求得首交点后搜索交线其余交点的速度非常快，且适用范围广。不论何种参数曲面，只要曲面不存在非正则点，并可以求得曲面上任意点的坐标位置、法矢、切矢等几何信息，就可以用追踪法求交。追踪法的问题是目前尚无非常有效的方法来求得所有交线环的起始点。在有些情况下寻求初始点所花费的时间远大于追踪法过程中所节省的时间，而为了节省寻求初始点的时间，又可能漏掉某些交线，当在孤立交点和比较小的交线环时尤甚。</p>
<h2>3 追踪法的实现</h2>
<p>OpenCASCADE实现曲面求交追踪法的类是IntWalk_PWalking，注意看类注释中的单词marching:</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230530003542464-1141497458.png" alt="" /></p>
<p>追踪法中需要解决两个问题：</p>
<ul>
<li>如何确定追踪方向：目前常用切线法确定追踪方向，即估计真实交线在该点的切线方向，并以此作为追踪方向。当曲面间存在切点时，由于在切点附近两曲面的法矢非常接近，无法确定追踪方向，这也就是所谓的&ldquo;迷向&rdquo;问题（Tangent tracks）。在类IntWalk_PWalking中，追踪方向作为成员变量tgdir，将迭代法计算的交线的切向量保存起来，当有&ldquo;迷向&rdquo;问题时，调用函数RepartirOuDiviser来修复。</li>
<li>如何确定追踪步长：追踪步长的选择通常有两种方法：1）固定步长和2）变步长也称为自适应步长法Adaptive Step。因为迭代法是根据估计点的参数来计算精确交点，为了避免根据点反求参数，直接根据切线方向的X,Y分量来确定参数的步长。虽然有些变化，总体上看使用的是固定步长法。若直接根据交线切线方向tgdir乘以步长得到点，是需要反求点在曲面上的参数。步长主要与精度设置有关，精度设置高，步长越小，则会导致计算速度慢，求出的交点过密，还可能带来不稳定因素。如步长过大，可能 会导致迭代不收敛或者跳到另外 一条交线上。</li>
</ul>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230530003603360-112110383.png" alt="" /></p>
<p>使用默认精度设置，追踪法得到的交点数量就很大。若设置精度低，交点数量会明显减少，提高计算速度。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230530003611967-147958699.png" alt="" /></p>
<p>如上图所示，若两个曲面只有一个交线，使用有追踪法时只需要指定交线的初始交点，即可以得到整个交线。当两曲面有多个交线或有孤立交点时，就需要找出多个交线的起始交点：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230530003623960-427050951.png" alt="" /></p>
<p>上图所示位置交线断开生成两条交线，一个是绿色一个是红色，这种情况就需要分别指出两个交线起始点。</p>
<h2>4 Conclusion</h2>
<p>综上所述，曲面求交追踪法的优点是在求得首交点后搜索交线其余交点的速度非常快，且适用范围广。追踪法的问题是目前尚无非常有效的方法来求得所有交线环的起始点。曲面求交一般会采用通用性较好的网格法-迭代法-追踪法三者相结合的方法。应用网格法求得交点的初始估计值，再用迭代法求得精确交点，并以其为起点进行追踪，直到得到整条精确交线。</p>
<p>IntWalk_PWalking追踪法的步长与精度密切相关，选择合适的精度，可以使交线的交点数量少，提高计算速度。因为精度越高追踪过程中得到的追踪点越多，对于每个追踪点都需要使用迭代法计算精确交点。</p>
<img src ="http://www.cppblog.com/eryar/aggbug/229911.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-30 00:39 <a href="http://www.cppblog.com/eryar/archive/2023/05/30/IntWalk_PWalking.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之迭代法2</title><link>http://www.cppblog.com/eryar/archive/2023/05/28/occt_IntWalk_TheInt2S.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 28 May 2023 05:05:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/28/occt_IntWalk_TheInt2S.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229908.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/28/occt_IntWalk_TheInt2S.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229908.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229908.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之迭代法2</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>朱心雄等著《自由曲线曲面造型技术》书中对曲面求交的迭代法有详细介绍，其中关于曲面迭代求交的原理介绍如下：为求得两个曲面精确的交点，Newton-Raphson迭代法得到广泛应用，该法的优点为</p>
<ul>
<li>计算精度高，速度快，在初值选择比较合理的情况下，一般仅需要迭代二到三次就可以使交点的精度从百分之几提高到万分之几甚至百万分之一的数量级。</li>
<li>适用范围广，只要能获得曲面的几何位置、切矢、法矢等信息，不论什么类型的曲面都可以使用迭代法。</li>
</ul>
<p>其缺点是对初始值要求较严格，初始值选择不当，可能导致迭代不收敛，也就无法得到精确的交点。</p>
<p>在曲面求交等问题中，一般可根据参与变化的参数数量将迭代法分为三参数迭代法和四参数迭代法两种类型。我们知道，一张参数曲面有两个参数，两张参数曲面共有四个参数变量。采用三参数迭代法时，两个曲面的四个参数中只有三个参数参与迭代过程，而保持另一个参数固定不变，这实际上就是计算不变参数的等参数线与另一张曲面的交点。采用四参数迭代法时，两张曲面的四个参数变量都参与迭代过程，四者都可能变化。两种迭代法各有其优缺点。在下述情况下以应用三参数迭代法为宜：</p>
<ul>
<li>要求将交点迭代至某参数线上，以利于后继追踪求交法中滤除无效初值点；</li>
<li>当交线接近于参数边界时，希望将交点迭代至准确的边界上，以便进行裁剪等操作。</li>
</ul>
<p>但对于一般交点，三参数法则未必适用。首先遇到的问题是在四个参数中选择何者作为不变参数，固定参数选择不当可能降低迭代收敛速度以至根本不收敛，或者破坏交线拓朴结构的正确性。迭代法本身不能够成独立的求交方法，主要在追踪法中使用，OpenCASCADE中曲面求交追踪法的类是IntWalk_PWalking。前面的blog已经介绍了曲面交求的离散网格法，和类IntWalk_TheInt2S实现原理，本文主要介绍OpenCASCADE曲面求交迭代法的类IntWalk_TheInt2S的用法结果。</p>
<h2>2 Newton迭代求交</h2>
<p>前文介绍了曲面求交的离散网格法，使用类IntPolyh_Intersection来计算两个曲面网格的交线，计算结果是网格交线的一组交点。把离散网格的交点作为迭代法的输入，来检查一下迭代法的计算结果。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230528130007565-84758748.png" alt="" /></p>
<p>Newton迭代法的输入是初始估计点分别在两个曲面上的四个参数（u1, v1, u2, v2），及两个曲面aS1和aS2，迭代终止精度TolTangency。将计算结果输出，其中第一个距离是迭代的精确值与网格上交点的距离，第二个距离是精确交点的u1, v1, u2,v2分别在两个曲面上点的距离，可以看出经过Newton迭代计算后，根据精确交点的参数u1, v1, u2, v2计算出两个曲面上的点在指定的精度下是重合的。为了便于观察，将计算结果输出到DRAW中查看。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230528130020103-1528568754.png" alt="" /></p>
<p>其中红色的线是两个曲面网格的交线，绿色的线是将网格交线经过Newton迭代后得到精确交线。再将两个交线与实际曲面一起显示来对比：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230528130029390-385320846.png" alt="" /></p>
<p>从图上可以看出，绿色的交线已经能比较准确地表达两个曲面之间的相交情况。比网格交线效果好。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230528130040324-763309969.png" alt="" /></p>
<p id="1685250042459"></p>
<h2>3 Conclusion</h2>
<p>综上所述，将曲面求交的离散网格交线作为Newton迭代法的初始估计点，可以得到较好的交线。后面再分析一下曲面求交的追踪法，看在追踪法中是如何使用离散网格交线数据的。对于一般的NURBS曲面求交，先用离散网格法或分割离散法求得交线和交点的估计值，然后再应用Newton迭代法由估计值求得精确交点。如果认为估计交点分布不够细密，可以对网格加密，由此可以得到完整、致密的精确交线而无需应用追踪法。</p><img src ="http://www.cppblog.com/eryar/aggbug/229908.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-28 13:05 <a href="http://www.cppblog.com/eryar/archive/2023/05/28/occt_IntWalk_TheInt2S.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之迭代法</title><link>http://www.cppblog.com/eryar/archive/2023/05/27/IntWalk_TheInt2S.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sat, 27 May 2023 15:35:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/27/IntWalk_TheInt2S.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229907.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/27/IntWalk_TheInt2S.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229907.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229907.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之迭代法</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>朱心雄等著《自由曲线曲面造型技术》书中对曲面求交的迭代法有详细介绍，其中关于曲面迭代求交的原理介绍如下：为求得两个曲面精确的交点，Newton-Raphson迭代法得到广泛应用，该法的优点为</p>
<ul>
<li>计算精度高，速度快，在初值选择比较合理的情况下，一般仅需要迭代二到三次就可以使交点的精度从百分之几提高到万分之几甚至百万分之一的数量级。</li>
<li>适用范围广，只要能获得曲面的几何位置、切矢、法矢等信息，不论什么类型的曲面都可以使用迭代法。</li>
</ul>
<p>其缺点是对初始值要求较严格，初始值选择不当，可能导致迭代不收敛，也就无法得到精确的交点。</p>
<p>在曲面求交等问题中，一般可根据参与变化的参数数量将迭代法分为三参数迭代法和四参数迭代法两种类型。我们知道，一张参数曲面有两个参数，两张参数曲面共有四个参数变量。采用三参数迭代法时，两个曲面的四个参数中只有三个参数参与迭代过程，而保持另一个参数固定不变，这实际上就是计算不变参数的等参数线与另一张曲面的交点。采用四参数迭代法时，两张曲面的四个参数变量都参与迭代过程，四者都可能变化。两种迭代法各有其优缺点。在下述情况下以应用三参数迭代法为宜：</p>
<ul>
<li>要求将交点迭代至某参数线上，以利于后继追踪求交法中滤除无效初值点；</li>
<li>当交线接近于参数边界时，希望将交点迭代至准确的边界上，以便进行裁剪等操作。</li>
</ul>
<p>但对于一般交点，三参数法则未必适用。首先遇到的问题是在四个参数中选择何者作为不变参数，固定参数选择不当可能降低迭代收敛速度以至根本不收敛，或者破坏交线拓朴结构的正确性。迭代法本身不能够成独立的求交方法，主要在追踪法中使用，OpenCASCADE中曲面求交追踪法的类是IntWalk_PWalking。本文主要介绍OpenCASCADE曲面求交迭代法的类IntWalk_TheInt2S的用法及原理。</p>
<h2>2 Newton迭代求交</h2>
<p>OpenCASCADE中两曲面求交迭代法由类IntWalk_TheInt2S实现，其类中主要函数有：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231602065-1358703950.png" alt="" /></p>
<p>给定两个曲面，和初始估计点在两个曲面上的参数(u1, v1), (u2,v2)，迭代计算出精确交点。固定三参数的方式总共分四种类型IntImp_ConstIsoparametric：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231610155-341268190.png" alt="" /></p>
<p>IntImp_UIsoparametricOnCaro1：是固定估计点在曲面1上的参数u；</p>
<p>IntImp_VIsoparametricOnCaro1：是固定估计点在曲面1上的参数v；</p>
<p>IntImp_UIsoparametricOnCaro2：是固定估计点在曲面2上的参数u；</p>
<p>IntImp_VIsoparametricOnCaro2：是固定估计点在曲面2上的参数v；</p>
<p>为了避免三参数迭代法找不到交点的情况，会在四个方向上分别进行计算，总有一个方向上会找到交点，并将找到交点的参数固定情况返回：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231628726-451599587.png" alt="" /></p>
<p>在函数Perform()中通用求解迭代方程组，得到精确交点：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231639574-1627264024.png" alt="" /></p>
<p>其中迭代方程组为成员变量myZerParFunc，方程组求解使用类math_FunctionSetRoot。由方程组求解类注释可知，需要方程组的一阶偏导数即梯度Gradient，采用的是Newton迭代法。若方程组有解Root且满足精度要求，则保存下精确交点的坐标值及在两个曲面上的参数值等数据。</p>
<p>&nbsp;</p>
<h2>3 三参数迭代方程组</h2>
<p>三参数迭代方程类为GeomInt_TheFunctionOfTheInt2SOfThePrmPrmSvSurfacesOfWLApprox,</p>
<p>是从类math_FunctionSetWithDerivatives派生的，即三参数迭代方程是个方程组(Function Set)。其定义在文件IntImp_ZerParFunc中，先使用函数ComputeParameters()根据固定参数类型来确定估计点的固定参数及另外三个参数变量的初始值：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231657284-682405945.png" alt="" /></p>
<p>设两个参数曲面S1(u1,v1)，S2(u2,v2)，并已知一交点的初估计点P0，P0点在两张曲面上对应的投影点分别为P1=S1(u0,v0)和P2=S2(s0,t0)。由于P0点为一估计点，所以P1和P2并不重合。设以s0作为固定参数，即当固定参数类型为IntImp_UIsoparametricOnCaro1时，则问题转化为求u*,v*,t*，使两曲面片上的点S1(u*,v*)和S2(s0,t*)重合。建立方程组：</p>
<p>R(u,v,t)=S1(u,v) - S2(s0, t)</p>
<p>通过函数计算两个曲面上的点pntsol1和pntsol2，得到三个变量的方程组的值。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231720266-2026368266.png" alt="" /></p>
<p>因为要使用Newton迭代法，需要提供方程组的一阶偏导数，即Jacobian矩阵：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231732649-647381080.png" alt="" /></p>
<p id="1685200655547">函数Derivatives()用来计算一阶偏导数：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230527231810189-213504688.png" alt="" /></p>
<h2>4 Conclusion</h2>
<p>综上所述，迭代法本身不能构成一个独立的求交方法，与所有不动点迭代法一样，应用迭代法求交线之前，首先必须给出交点的初始估计值，而交点的初始值必须通过其他求交方法得到。因此，迭代交交常同其它求交方法结合使用，作为交点精化的一种手段。迭代法的主要过程是根据初始估计点的几何性质（如坐标位置、切矢、法矢、曲率等）运用Newton方法得到一个较原估计点更接近于目标点（即精确交点）的估计点。如此反复进行，直到求得的交点满足所要求的精度。该法的优点是在初值比较好时其收敛速度非常快，而且能应用于任意参数曲面包括Coons曲面和等距曲面，因此应用非常广泛，其主要缺点是对初始值要求比较苛刻，初始值 选择不当有可能导致迭代不收敛。</p>
<p>OpenCASCADE中曲面求交的迭代法也不是独立的方法，与之配合的有离散网格求交得到初值，在追踪中作用迭代法。迭代求交使用的是三参数迭代法，根据三参数迭代法的数学方程可知，需要计算曲面上参数对应的点和切矢。</p><img src ="http://www.cppblog.com/eryar/aggbug/229907.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-27 23:35 <a href="http://www.cppblog.com/eryar/archive/2023/05/27/IntWalk_TheInt2S.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之网格离散法3</title><link>http://www.cppblog.com/eryar/archive/2023/05/23/occt_IntPolyh_Intersection.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Tue, 23 May 2023 15:21:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/23/occt_IntPolyh_Intersection.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229904.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/23/occt_IntPolyh_Intersection.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229904.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229904.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之网格离散法3</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>由朱心雄等著《自由曲线曲面造型技术》书中对曲面求交之网格离散法描述如下：该法的基本思想是先将曲面离散为由小平面片组成的网格，当网格足够密时，可以认为已经非常接近真实曲面，对分别表示不同曲面的两张网格，利用平面片求交法求得的交线，并以此交线近似代表曲面间的交线。这种方法原理简明，便于实现，适用范围广，任意参数曲面均可利用该法求交。但为获取精确地交线，则必须生成非常细密的网格，这将导致占用内存多，计算花费大。因此，实际工作中很少单一使用离散网格法，而常将其与其他方法结合使用。</p>
<p>OpenCASCADE中对于曲面求交也提供离散网格法，其中曲面的离散网格由类IntPatch_Polyhedron表示，两个网格面求交使用类IntPatch_InterferencePolyhedron。在实际计算两个面相交时并没有使用这个类，而是使用类IntPolyh_Intersection，而离散网格使用类IntPolyh_MaillageAffinage。</p>
<h2>2 网格离散</h2>
<p>使用类IntPolyh_MaillageAffinage主要用来生成曲面的网格，其中MaillageAffinage是法语，翻译过来是Mesh Refining网格细化，网格精度主要是通过参数U，V方向上的采样点数量来确定。当不指定采样点数量时，默认是参数U，V方向分别10个，即默认会生成10x10个采样点，即使是平面也是生成100个采样点。通过函数FillArrayOfPnt()生成采样点。通过函数FillArrayOfTriangles()来生成三角形，三角形的数量通过如下图所示公式计算，默认数量 为2x(10-1)x(10-1)=162。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230523225950683-604683071.png" alt="" /></p>
<p>对于简单的平面，如果不指定采样点数量，也会生成100个采样点及162个三角形：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230523225957709-406374580.png" alt="" /></p>
<h2>3 网格求交</h2>
<p>两个网格求交是通过类IntPolyh_Intersection来计算，计算的结果也是两个网格之间的交线。还是将交线显示出来便于观察：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230523230007230-1084041231.png" alt="" /></p>
<p>从生成的交线来看，这个结果要比IntPatch_InterferencePolyhedron要好，没有多余的交线。类IntPolyh_Intersection中使用BVH来过滤不相交的三角形，所以速度也会快很多。</p>
<h2>4 Conclusion</h2>
<p>综上所述，使用类IntPolyh_Intersection来计算两个曲面网格的交线。曲面网格生成直接通过参数U，V上的采样点数量来确定，虽然生成网格速度快，但是精度控制不好，即使是平面也会根据采样数量生成大量采样点和三角形，影响求交速度。网格求交作为曲面求交的预处理步骤，如何用更少的三角形来表示曲面，可提高网格求交性能。</p><img src ="http://www.cppblog.com/eryar/aggbug/229904.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-23 23:21 <a href="http://www.cppblog.com/eryar/archive/2023/05/23/occt_IntPolyh_Intersection.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之网格离散法2</title><link>http://www.cppblog.com/eryar/archive/2023/05/21/occt_intpatch_polyhedron2.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 21 May 2023 12:21:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/21/occt_intpatch_polyhedron2.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229899.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/21/occt_intpatch_polyhedron2.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229899.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229899.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之网格离散法2</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>由朱心雄等著《自由曲线曲面造型技术》书中对曲面求交之网格离散法描述如下：该法的基本思想是先将曲面离散为由小平面片组成的网格，当网格足够密时，可以认为已经非常接近真实曲面，对分别表示不同曲面的两张网格，利用平面片求交法求得的交线，并以此交线近似代表曲面间的交线。这种方法原理简明，便于实现，适用范围广，任意参数曲面均可利用该法求交。但为获取精确地交线，则必须生成非常细密的网格，这将导致占用内存多，计算花费大。因此，实际工作中很少单一使用离散网格法，而常将其与其他方法结合使用。</p>
<p>OpenCASCADE中对于曲面求交也提供离散网格法，其中曲面的离散网格由类IntPatch_Polyhedron表示，两个网格面求交使用类IntPatch_InterferencePolyhedron。本文主要介绍曲面的网格求交类IntPatch_InterferencePolyhedron。</p>
<h2>2 Polyhedron Interference</h2>
<p>OpenCASCADE中计算两个三角网格交线的类是IntPatch_InterferencePolyhedron，这个类还可以用来计算一个网格的自交情况。目前是简单计算两个网格中所有三角形的相交情况，时间复杂度为O(nm)或O(n^2)，对于网格三角形数量大的情况效率很低。为了稍微提高一些性能，引入Bnd_BoundSortBox来加速过滤掉包围盒不相交的三角形，减少两个三角形相交计算。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230521201524901-749379681.png" alt="" /></p>
<p>其中函数Intersect()就是用来计算两个三角形的相交情况。关于两个三角形的快速求交计算，很多网格处理库都使用了Tomas Moller&#8217;s 1997 triangle intersection routine，如</p>
<p><a href="http://geometry-central.net/surface/algorithms/intersection/"><u>http://geometry-central.net/surface/algorithms/intersection/</u></a>&nbsp;中也提供两个网格求交函数：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230521201533961-1746800060.png" alt="" /></p>
<p>在使用较广泛的网格处理库CGAL中也有相关计算函数：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230521201543933-193278585.png" alt="" /></p>
<p>感兴趣的同学可以对比一下这三个库关于两个网格求交的性能，看谁的性能最好，使用了什么技术。这里只是将OpenCASCADE中计算的求交结果输出，首先是面的自相交情况：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230521201553175-1401188965.png" alt="" /></p>
<p>其中红色部分为交线，可以看出在计算自相交时，会生成多余的交线。其中蓝色部分是有重叠三角形的情况。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230521201600832-627429797.png" alt="" /></p>
<p>当计算两个网格交线时，总体上是正确的，不过也会有多余的交线产生。</p>
<h2>3 Conclusion</h2>
<p>综上所述，两个网格相交计算最直接的算法就是两两三角形进行求交计算，但是对于大网格会有性能问题。OpenCASCADE中两个网格求交计算会得到多余的交线，目前网格离散求交只是用于B样条曲面的求交计算的前处理IntPatch_PrmPrmIntersection，从OpenCASCADE计算两个曲面交线结果来看，离散网格计算中多余的交线没有影响最终的计算结果。大家可以带着这个问题&#8220;离散网格计算得到多余的交线对最终结果有影响么？&#8221;来理解IntPatch_PrmPrmIntersection中曲面求交的实现原理。</p><img src ="http://www.cppblog.com/eryar/aggbug/229899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-21 20:21 <a href="http://www.cppblog.com/eryar/archive/2023/05/21/occt_intpatch_polyhedron2.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE曲面求交之网格离散法1</title><link>http://www.cppblog.com/eryar/archive/2023/05/14/occt_intpatch_polyhedron.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 14 May 2023 13:05:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/05/14/occt_intpatch_polyhedron.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229884.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/05/14/occt_intpatch_polyhedron.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229884.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229884.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE曲面求交之网格离散法1</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<p>&nbsp;</p>
<h2>1 Introduction</h2>
<p>由朱心雄等著《自由曲线曲面造型技术》书中对曲面求交之网格离散法描述如下：该法的基本思想是先将曲面离散为由小平面片组成的网格，当网格足够密时，可以认为已经非常接近真实曲面，对分别表示不同曲面的两张网格，利用平面片求交法求得的交线，并以此交线近似代表曲面间的交线。这种方法原理简明，便于实现，适用范围广，任意参数曲面均可利用该法求交。但为获取精确地交线，则必须生成非常细密的网格，这将导致占用内存多，计算花费大。因此，实际工作中很少单一使用离散网格法，而常将其与其他方法结合使用。</p>
<p>OpenCASCADE中对于曲面求交也提供离散网格法，其中曲面的离散网格由类IntPatch_Polyhedron表示，两个网格面求交使用类IntPatch_InterferencePolyhedron。本文主要介绍曲面的网格表示类IntPatch_Polyhedron。</p>
<h2>2 Polyhedron</h2>
<p>OpenCASCADE用于曲面求交的网格离散算法相对BRepMesh中的算法要简单很多，主要思路是根据参数U，V方向上的采样点数量来计算曲面上的点，再根据固定公式将采样点连成三角形。其中生成采样点代码如下所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205644028-497606770.png" alt="" /></p>
<p>成员变量CMyPnts是采样点数组，CMyU和CMyV是采样点在曲面上的参数。将采样点连成三角形函数如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205657740-117057090.png" alt="" /></p>
<p>根据上述生成采样点及三角形函数，对于平面生成的三角形如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205706544-242713112.png" alt="" /></p>
<p>其中Triangle()函数中变量line表示参数u方向上第几条线，代入具体的索引Index来看规律：</p>
<p>当参数索引 Index为1时，line为1，得到的三角形为1-4-5；</p>
<p>当参数索引Index为2时，line为1，得到的三角形为1-5-2；</p>
<p>当参数索引Index为3时，line为1，得到的三角形为2-5-6；</p>
<p>当参数索引Index为4时，line为1，得到的三角形为2-6-3；</p>
<p>当参数索引Index为5时，line为2，得到的三角形为4-7-8；</p>
<p>当参数索引Index为6时，line为2，得到的三角形为4-8-5；</p>
<p>&nbsp;</p>
<p>从上可以得到生成三角形的规律，即根据索引Index计算正在处理的三角形是参数u方向上第几条线line，生成这条线上在参数v方向上的所有的三角形。生成的三角形都是逆时针的。</p>
<p>下面我们看看对于一些基本曲面，这种离散网格算法生成的网格效果：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205721539-2108217031.png" alt="" /></p>
<p>球面的离散网格</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205729847-2135393871.png" alt="" /></p>
<p>圆柱面的离散网格</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205739129-1284939763.png" alt="" /></p>
<p>圆环面的离散网格</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205746534-2081259211.png" alt="" /></p>
<p>B样条曲面</p>
<h2>3 Conclusion</h2>
<p>综上所述，类IntPatch_Polyhedron中生成网格的算法主要依赖曲面在参数U，V上的采样点数量。默认采样点数量是根据函数NbSamplesV()和NbSamplesU()生成。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202305/534255-20230514205756472-327306536.png" alt="" /></p>
<p>也可以指定采样点数量，当采样点数量越多，则生成的三角形越多，网格越密。当然这种方式也可用来生成曲面的显示数据，生成速度很快，唯一的缺陷是生成显示用网格的精度只能通过采样点数量来控制，对于曲率变化大的曲面，若指定多的采样点，则会生成大量三角形，占用大量内存空间。</p>
<p>附上测试代码：</p>
<pre class="language-cpp highlighter-hljs"><code>
#include &lt;TColgp_Array2OfPnt.hxx&gt;
#include &lt;Geom_Plane.hxx&gt;
#include &lt;Geom_CylindricalSurface.hxx&gt;
#include &lt;Geom_ConicalSurface.hxx&gt;
#include &lt;Geom_SphericalSurface.hxx&gt;
#include &lt;Geom_ToroidalSurface.hxx&gt;
#include &lt;Geom_BSplineSurface.hxx&gt;
#include &lt;GeomAdaptor_Surface.hxx&gt;
#include &lt;GeomAPI_PointsToBSplineSurface.hxx&gt;
#include &lt;IntPatch_Polyhedron.hxx&gt;
#include &lt;IntPatch_InterferencePolyhedron.hxx&gt;
#pragma comment(lib, "TKernel.lib")
#pragma comment(lib, "TKMath.lib")
#pragma comment(lib, "TKG2d.lib")
#pragma comment(lib, "TKG3d.lib")
#pragma comment(lib, "TKGeomBase.lib")
#pragma comment(lib, "TKGeomAlgo.lib")
void makeSurface(Handle(Geom_BSplineSurface)&amp; theSurface)
{
    TColgp_Array2OfPnt aPoints(1, 5, 1, 5);
    aPoints.SetValue(1, 1, gp_Pnt(-4, -4, 5));
    aPoints.SetValue(1, 2, gp_Pnt(-4, -2, 5));
    aPoints.SetValue(1, 3, gp_Pnt(-4, 0, 4));
    aPoints.SetValue(1, 4, gp_Pnt(-4, 2, 5));
    aPoints.SetValue(1, 5, gp_Pnt(-4, 4, 5));
    aPoints.SetValue(2, 1, gp_Pnt(-2, -4, 4));
    aPoints.SetValue(2, 2, gp_Pnt(-2, -2, 4));
    aPoints.SetValue(2, 3, gp_Pnt(-2, 0, 4));
    aPoints.SetValue(2, 4, gp_Pnt(-2, 2, 4));
    aPoints.SetValue(2, 5, gp_Pnt(-2, 5, 4));
    aPoints.SetValue(3, 1, gp_Pnt(0, -4, 3.5));
    aPoints.SetValue(3, 2, gp_Pnt(0, -2, 3.5));
    aPoints.SetValue(3, 3, gp_Pnt(0, 0, 3.5));
    aPoints.SetValue(3, 4, gp_Pnt(0, 2, 3.5));
    aPoints.SetValue(3, 5, gp_Pnt(0, 5, 3.5));
    aPoints.SetValue(4, 1, gp_Pnt(2, -4, 4));
    aPoints.SetValue(4, 2, gp_Pnt(2, -2, 4));
    aPoints.SetValue(4, 3, gp_Pnt(2, 0, 3.5));
    aPoints.SetValue(4, 4, gp_Pnt(2, 2, 5));
    aPoints.SetValue(4, 5, gp_Pnt(2, 5, 4));
    aPoints.SetValue(5, 1, gp_Pnt(4, -4, 5));
    aPoints.SetValue(5, 2, gp_Pnt(4, -2, 5));
    aPoints.SetValue(5, 3, gp_Pnt(4, 0, 5));
    aPoints.SetValue(5, 4, gp_Pnt(4, 2, 6));
    aPoints.SetValue(5, 5, gp_Pnt(4, 5, 5));
    theSurface = GeomAPI_PointsToBSplineSurface(aPoints).Surface();
}
void writeStl(const IntPatch_Polyhedron&amp; thePolyhedron, const std::string&amp; theFileName)
{
    // Dump surface polyhedron to STL file.
    std::ofstream aStlFile(theFileName);
    aStlFile &lt;&lt; "solid polyhedron" &lt;&lt; std::endl;
    // Dump triangles.
    for (Standard_Integer t = 1; t &lt;= thePolyhedron.NbTriangles(); ++t)
    {
        Standard_Integer aPi1 = 0;
        Standard_Integer aPi2 = 0;
        Standard_Integer aPi3 = 0;
        thePolyhedron.Triangle(t, aPi1, aPi2, aPi3);
        const gp_Pnt&amp; aP1 = thePolyhedron.Point(aPi1);
        const gp_Pnt&amp; aP2 = thePolyhedron.Point(aPi2);
        const gp_Pnt&amp; aP3 = thePolyhedron.Point(aPi3);
        aStlFile &lt;&lt; "facet" &lt;&lt; std::endl;
        aStlFile &lt;&lt; "outer loop" &lt;&lt; std::endl;
        aStlFile &lt;&lt; "vertex " &lt;&lt; aP1.X() &lt;&lt; " " &lt;&lt; aP1.Y() &lt;&lt; " " &lt;&lt; aP1.Z() &lt;&lt; std::endl;
        aStlFile &lt;&lt; "vertex " &lt;&lt; aP2.X() &lt;&lt; " " &lt;&lt; aP2.Y() &lt;&lt; " " &lt;&lt; aP2.Z() &lt;&lt; std::endl;
        aStlFile &lt;&lt; "vertex " &lt;&lt; aP3.X() &lt;&lt; " " &lt;&lt; aP3.Y() &lt;&lt; " " &lt;&lt; aP3.Z() &lt;&lt; std::endl;
        aStlFile &lt;&lt; "endloop" &lt;&lt; std::endl;
        aStlFile &lt;&lt; "endfacet" &lt;&lt; std::endl;
    }
    aStlFile &lt;&lt; "endsolid polyhedron" &lt;&lt; std::endl;
    aStlFile.close();
}
void testPolyhedron()
{
    // Plane surface polyhedron.
    Handle(Geom_Plane) aPlane = new Geom_Plane(gp::XOY());
    Handle(GeomAdaptor_Surface) aSurfaceAdaptor = new GeomAdaptor_Surface(aPlane, 0.0, 10.0, 0.0, 20.0);
    IntPatch_Polyhedron aPlanePolyhedron(aSurfaceAdaptor);
    writeStl(aPlanePolyhedron, "d:/plane.stl");
    // Spherical surface polyhedron.
    Handle(Geom_SphericalSurface) aSphericalSurface = new Geom_SphericalSurface(gp::XOY(), 3.0);
    aSurfaceAdaptor = new GeomAdaptor_Surface(aSphericalSurface);
    IntPatch_Polyhedron aSphericalPolyhedron(aSurfaceAdaptor);
    writeStl(aSphericalPolyhedron, "d:/spherical.stl");
    // Cylindrical surface polyhedron.
    Handle(Geom_CylindricalSurface) aCylindricalSurface = new Geom_CylindricalSurface(gp::XOY(), 5.0);
    aSurfaceAdaptor = new GeomAdaptor_Surface(aCylindricalSurface, 0.0, M_PI, 0.0, 8.0);
    IntPatch_Polyhedron aCylindricalPolyhedron(aSurfaceAdaptor);
    writeStl(aCylindricalPolyhedron, "d:/cylindrical.stl");
    // Toroidal Surface polyhedron.
    Handle(Geom_ToroidalSurface) aToroidalSurface = new Geom_ToroidalSurface(gp::XOY(), 10.0, 3.0);
    aSurfaceAdaptor = new GeomAdaptor_Surface(aToroidalSurface);
    IntPatch_Polyhedron aToroidalPolyhedron(aSurfaceAdaptor);
    writeStl(aToroidalPolyhedron, "d:/toroidal.stl");
    // BSpline surface polyhedron.
    Handle(Geom_BSplineSurface) aBSplineSurface;
    makeSurface(aBSplineSurface);
    aSurfaceAdaptor = new GeomAdaptor_Surface(aBSplineSurface);
    IntPatch_Polyhedron aPolyhedron(aSurfaceAdaptor);
    writeStl(aPolyhedron, "d:/bspline.stl");
}
int main(int argc, char* argv[])
{
    testPolyhedron();
    return 0;
}</code></pre>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/229884.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-05-14 21:05 <a href="http://www.cppblog.com/eryar/archive/2023/05/14/occt_intpatch_polyhedron.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PlaneGCS-平面几何约束求解器用法</title><link>http://www.cppblog.com/eryar/archive/2023/03/24/planegcs-usage.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Fri, 24 Mar 2023 14:10:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/03/24/planegcs-usage.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229776.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/03/24/planegcs-usage.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229776.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229776.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>PlaneGCS-平面几何约束求解器用法</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<h2>1 Introduction</h2>
<p>在传统的机械设计软件中，一般使用几何约束求解器来画草图，再通过对草图进行拉伸旋转等生成特征实现建模功能。基于参数化历史特征方式来建模的软件绕不开几何约束求解器，目前主流商用软件一般使用西门子D-Cubed DCM及达索的CGM。开源世界也有两款几何约束求解器：SolveSpace和PlaneGCS。</p>
<p>PlaneGCS字面意思是平面几何约束求解器，主要用于绘制二维草图。因为PlaneGCS代码相对清晰，功能简单，只能处理平面几何元素的约束，本文主要结合示例代码介绍PlaneGCS的使用方法，在会用的基础上去理解源码的实现逻辑。</p>
<h2>2 PlaneGCS</h2>
<p>PlaneGCS主要包含三部分：</p>
<ul>
<li>几何元素数据结构文件：h/Geo.cpp</li>
<li>约束条件文件：h/Constraints.cpp</li>
<li>约束求解实现文件：h/GCS.cpp</li>
</ul>
<p>其中几何元素数据结构中定义的几何元素如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230324220305696-222594862.png" alt="" /></p>
<p>从上图可以看到，目前支持的几何元素有点Point，直线Line，圆Circle，椭圆Ellipse，双曲线Hyperbola，抛物线Parabola，圆弧Arc/ArcOfEllipse/ArcOfHyperbola/ArcOfParabola，及B样条曲线BSpline，不过看代码BSpline部分函数没有实现，应该是不支持的。</p>
<p>约束条件文件定义的约束类型如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230324220324299-830063162.png" alt="" /></p>
<p>从约束求解文件中可以看到，其中数学计算主要使用Eigen中非线性方程组求解算法和boost的图graph算法，从中可以推测出实现平面几何约束求解器中需要的关键技术。先掌握PlaneGCS的用法，然后再分析其背后的实现原理细节。</p>
<h2>3 Code Example</h2>
<p>这里给出一个简单的示例程序，先让大家对PlaneGCS有个认识。示例程序中演示了给两条直线加上水平和垂直约束。为了便于查看约束后的结果，在代码中生成Draw Test Harness脚本文件。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230324220408620-1496050053.png" alt="" /></p>
<p>程序代码如下所示：</p>
<pre class="language-cpp highlighter-hljs"><code>/*
Copyright(C) 2023 Shing Liu(eryar@163.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "GCS.h"
#include &lt;fstream&gt;
void test()
{
    double aPx1 = 0.0;
    double aPy1 = 0.0;
    double aPx2 = 3.0;
    double aPy2 = 3.0;
    double aPx3 = 6.0;
    double aPy3 = 9.0;
    GCS::VEC_pD aParameters;
    aParameters.push_back(&amp;aPx1);
    aParameters.push_back(&amp;aPy1);
    aParameters.push_back(&amp;aPx2);
    aParameters.push_back(&amp;aPy2);
    aParameters.push_back(&amp;aPx3);
    aParameters.push_back(&amp;aPy3);
    GCS::Point aP1(&amp;aPx1, &amp;aPy1);
    GCS::Point aP2(&amp;aPx2, &amp;aPy2);
    GCS::Point aP3(&amp;aPx3, &amp;aPy3);
    GCS::Line aLine1;
    GCS::Line aLine2;
    aLine1.p1 = aP1;
    aLine1.p2 = aP2;
    aLine2.p1 = aP2;
    aLine2.p2 = aP3;
    std::ofstream aTclFile("d:/gcs.tcl");
    aTclFile &lt;&lt; "# 2 lines before PlaneGCS solve" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vinit" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vertex aP1 " &lt;&lt; aPx1 &lt;&lt; " " &lt;&lt; aPy1 &lt;&lt; " 0" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vertex aP2 " &lt;&lt; aPx2 &lt;&lt; " " &lt;&lt; aPy2 &lt;&lt; " 0" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vertex aP3 " &lt;&lt; aPx3 &lt;&lt; " " &lt;&lt; aPy3 &lt;&lt; " 0" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "polyvertex aPolyline1 aP1 aP2 aP3" &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vdisplay aPolyline1 " &lt;&lt; std::endl;
    aTclFile &lt;&lt; "vsetcolor aPolyline1 RED" &lt;&lt; std::endl;
    GCS::System aSolver;
    aSolver.addConstraintHorizontal(aLine1);
    aSolver.addConstraintVertical(aLine2);
    if (aSolver.solve(aParameters) == GCS::Success)
    {
        aSolver.applySolution();
        aTclFile &lt;&lt; "# 2 lines after PlaneGCS solve" &lt;&lt; std::endl;
        aTclFile &lt;&lt; "vertex aV1 " &lt;&lt; aPx1 &lt;&lt; " " &lt;&lt; aPy1 &lt;&lt; " 0" &lt;&lt; std::endl;
        aTclFile &lt;&lt; "vertex aV2 " &lt;&lt; aPx2 &lt;&lt; " " &lt;&lt; aPy2 &lt;&lt; " 0" &lt;&lt; std::endl;
        aTclFile &lt;&lt; "vertex aV3 " &lt;&lt; aPx3 &lt;&lt; " " &lt;&lt; aPy3 &lt;&lt; " 0" &lt;&lt; std::endl;
        aTclFile &lt;&lt; "polyvertex aPolyline2 aV1 aV2 aV3" &lt;&lt; std::endl;
        aTclFile &lt;&lt; "vdisplay aPolyline2 " &lt;&lt; std::endl;
        aTclFile &lt;&lt; "vsetcolor aPolyline2 GREEN" &lt;&lt; std::endl;
    }
    aTclFile.close();
}
int main(int argc, char* argv[])
{
    test();
    return 0;
}</code></pre>
<p>从程序代码中可以看出PlaneGCS的使用先要定义需要计算的参数aParameters，这些参数是几何元素中的数据，都是使用的指针。然后将约束加入到GCS::System中，最后代入参数调用solve函数进行求解。求解成功后使用applySolution()函数应用求解结果。求解结果在Draw中显示的绿色的线如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230324220420026-1241877432.png" alt="" /></p>
<p id="1679666661820"></p>
<h2>4 Conclusion</h2>
<p>本文结合示例代码演示如何使用PlaneGCS，主要使用了水平和垂直约束。PlaneGCS中还支持其他约束类型，童鞋们可以自己探索一下。几何造型内核和几何约束求解器常被看作是工业CAD软件的卡脖子技术，开源库一般功能不太完善，但是用来探索背后的实现原理还是有参考借鉴意义的。希望有更多的童鞋去了解背后的原理，共同来提高国内三维CAD软件开发水平。</p><img src ="http://www.cppblog.com/eryar/aggbug/229776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-03-24 22:10 <a href="http://www.cppblog.com/eryar/archive/2023/03/24/planegcs-usage.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE-曲面求交</title><link>http://www.cppblog.com/eryar/archive/2023/03/19/IntTools_FaceFace.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 19 Mar 2023 04:23:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/03/19/IntTools_FaceFace.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229760.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/03/19/IntTools_FaceFace.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229760.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229760.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE-曲面求交</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<p><strong>Abstract</strong>: 曲面求交是几何造型内核最为重要也最为复杂的问题之一，求交算法的质量（稳定、准确、快速）直接影响到几何内核的稳定性和实用程度，故具有十分重要的意义。求交问题包括曲线与曲线求交、曲线与曲面求交和曲面与曲面求交，其中最重要难度最大的当属曲面与曲面求交问题，其他求交问题可以应用曲面与曲面求交的思想予以解决。本文主要介绍opencascade中曲面与曲面求交的实现原理。</p>
<p><strong>Key Words</strong>: Face Face Intersection, Intersection</p>
<h2>1. Introduction</h2>
<p>如果说理解opencascade中面的构造原理（即理解BRepBuilderAPI_MakeFace的源码），我觉得算是理解了BRep表示法的数据结构Modeling Data。如果说理解了曲面与曲面求交的实现原理，我觉得算是对几何内核中的核心算法布尔操作有了一定的认识。曲面与曲面求交过程（Intersection Algorithm）中主要依赖三大工具：拟合（Approximation Algorithm）、投影（Projection Algorithm）和定位（Classification Algorithm）。下面结合布尔操作TKBO中的曲面与曲面求交类IntTools_FaceFace源码实现分别介绍这三大工具的应用场景。opencascade中的算法类一般的使用套路和把大象装冰箱类似总共分三步：</p>
<p>第一步，初始化；通过构造函数或Init()等函数将算法类需要的参数输入进去；IntTools_FaceFace中通过SetParameters()函数设置算法的参数</p>
<p>第二步：计算；使用函数Build()， Perform()函数来执行计算；IntTools_FaceFace中的主要实现逻辑在Perform()中。</p>
<p>第三步：输出；将计算结果输出。IntTools_FaceFace通过Lines()和Points()函数输出计算结果即交线和交点。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319122020669-1506353956.png" alt="" /></p>
<p id="1679199622540"></p>
<h2>2. Approximation Algorithm 拟合</h2>
<p>opencascade中曲面的表示有两种方式，一种是参数方程S(u,v)表示，一种是二次曲面的代数方程f(x,y,z)=0表示。因此也将曲面求交问题分为：</p>
<ul>
<li>代数/代数曲面求交；</li>
<li>代数/参数曲面求交；</li>
<li>参数/参数曲面求交；</li>
</ul>
<p>opencascade中计算曲面求交更底层的类是IntPatch_Intersection，其中也是分这三种类型来处理：</p>
<p>GeomGeomPerfom()对应的是代数/代数曲面求交；</p>
<p>GeomParamPerform()对应的是代数/参数曲面求交；</p>
<p>ParamParamPerform()对应的是参数/参数曲面求交；</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121338171-127654505.png" alt="" /></p>
<p>其中代数/代数曲面求交函数GeomGeomPerform()中使用类IntPatch_ImpImpIntersection来计算两个二次代数曲面的求交，其实这是Imp缩写就是隐式代数方程Implicit Equation的意思。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121355047-106338270.png" alt="" /></p>
<p>二次代数曲面的求交使用包IntAna来实现，这在早期文章中分析了其实现原理，主要思想是将一个二次曲面的参数表示代入隐式方程变成一元方程，然后对这个一元方程进行求解。例如圆柱面与二次代数曲面求交：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121404692-65876191.png" alt="" /></p>
<p>其中代数/参数曲面求交函数GeomParamPerform()中使用类IntPatch_ImpPrmIntersection来计算代数曲面和参数曲面的求交，这里Prm为Parametric equation参数方程的缩写。其代码注释中写到bi-parametrised surface意思双参数曲面S(u, v)。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121415810-1106535849.png" alt="" /></p>
<p>其中代数/参数曲面求交函数ParamParamPerform()中使用类IntPatch_PrmPrmIntersection来计算参数数曲面和参数曲面的求交。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121428379-159377107.png" alt="" /></p>
<p>参数/参数曲面求交的基本方法有以下五种：</p>
<ul>
<li>代数法，也称解析法；</li>
<li>网格离散法；</li>
<li>分割法；</li>
<li>迭代法；</li>
<li>追踪法；</li>
</ul>
<p>相关原理介绍可以参考朱心雄等著《自由曲线曲面造型技术》。大家可以结合源码，看看opencascade中使用了哪些方法。曲面交线的表达涉及三个问题，交点信息表示，交线组织及交线中交点的删除策略。交线的表达使用类IntPatch_Line：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121439383-1344980194.png" alt="" /></p>
<p>在类IntTools_FaceFace中函数SetParameters()中可以指定交线中交点的删除策略。通过参数ApproxCurves和ApproximationTolerance来指定交线中交点是否拟合及拟合精度。求交过程中得到的交点往往非常致密，这样虽然可以保证交线的精度，但保存的数据量太大，在实际应用中需要删除部分交点。Pratt提出使用最小二乘法对交线逼近，以删除不必要的交点。这里就需要使用到拟合算法Approximation Algorithm。</p>
<p>在opencascade中拟合问题被抽象成非线性方程组的求解问题，当然最小二乘法也是其中方法之一。拟合算法是造型内核中最基础最重要的算法，除了简单的点拟合成线以外，还要处理带约束的情况，如加上交点通过曲面的位置约束等。几何约束求解器中核心也是如此。法国、俄罗斯数学厉害，我想应该是已经形成理论+应用的良性循环。数学是创新的基础，是用最简单的语言来精确描述自然中的规律，虽然我们理工科一直学数学，但工作后很大一部分人很少使用高等数学中的工具，感觉数学没什么用。学而不思则罔，思而不学则殆。我总结的学习规律就是要有实践，上学时实践就是通过做题，数学理论的一种实践就是开发出软件程序。</p>
<p>现在国家提倡自主我觉得是大好事，什么时候我们出一个几何内核，出一个PDE偏微方程求解器等，形成理论加实践的良性循环，以我们的人数和勤劳，离科技强国就不会太远。</p>
<h2>3. Projection Algorithm 投影</h2>
<p>投影主要用来计算曲面上的曲线对应到曲面参数空间的曲线PCurve，生成FACE面时如果边EDGE中没有PCurve，得到的面是不正确的。投影算法的实现原理在早期的文章中已经详细介绍过，投影算法依赖拟合算法。投影算法用在生成交线的函数中MakeCurve()：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121507661-1490313908.png" alt="" /></p>
<p>当不对交线进行拟合时，生成交线及PCurve主要使用类GeomInt_SS的静态函数来得到交线：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121515366-1824358895.png" alt="" /></p>
<p>这样生成的交线是B样条曲线且控制顶点数量很大。当通过拟合可以生成更简化的B样条交线。</p>
<p>&nbsp;</p>
<h2>4. Classification Algorithm 定位</h2>
<p>定位工具主要用于判断点和一个区域的状态，是在区域内、外还是上。在早期的文章中有一些介绍：<a href="https://www.cnblogs.com/opencascade/p/Point_Classifier.html"><u>https://www.cnblogs.com/opencascade/p/Point_Classifier.html</u></a></p>
<p>点定位在曲面求交中的应用就是处理拓朴面的情况，对于几何曲面，其实参数域就是其参数S(u,v)的取值范围。对于拓朴面，其参数域是通过边界Wire来限定的，而且还会有面上开孔的情况需要处理。其实IntTools_FaceFace主要是用来计算FACE中几何曲面求交的，没有正确处理定位问题，即生成的交线没有处理面的边界问题。相信看懂源码的同学可以解决这个问题。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230319121533307-1510083468.png" alt="" /></p>
<p>定位问题是个几何问题，在《计算几何及应用》一书中有对点的定位问题有详细算法。一般的处理方法是通过点作一条半射线，计算半射线与多边形交点的个数，若交点数为奇数，则点在内部，否则在外部。还有一种方法是通过计算点与多边形各顶点的角度来判断。这两种方式时间复杂度均为O(n)。opencascade中使用的第一种方法。书中提到几种效率更高的算法，如点定位问题的分层方法，可以将查询时间提高到O(logn)。点定位问题的单调链方法用O(nlogn)时间和O(n)空间作预处理，查询时间可以O((logn)^2)完成。点定位的三角形细分方法(Triangulation refinement method)只用O(n)空间存放预处理结果，用O(logn)时间回答点在哪个区域，完成预处理的时间是O(nlogn)。由此可见，opencascade的定位算法还有一些改进空间。</p>
<h2>5. Conclusion</h2>
<p>几何内核中曲面和曲面求交是最重要最复杂的问题，处理曲面和曲面求交需要拟合Approximation Algorithm、投影Projection Algorithm和定位Classification Algorithm工具。其中拟合和投影主要是数学问题，定位主要是个几何问题，理解问题就能找到相应的解决工具。庆幸有opencascade这个功能相对完备的开源的几何内核，提供了一个理论联系实践的平台。理解数学理论后可以去阅读源码，甚至是参与和贡献，求交、拟合、投影和定位工具都有一些改进空间。</p>
<p>最近关注的朋友说文章发得少了，主要原因是有点忙，不是因为保守不想分享，当然有些朋友也提醒要留一手。对于opencascade的技术分享我是没有保留的，因为opencascade是开源的，如果通过这些文章分享能帮助别人解决一些问题，就是创造价值，是有意义的。时不时收到网友的感谢，收获的感动不是用金钱可以衡量的。</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/229760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-03-19 12:23 <a href="http://www.cppblog.com/eryar/archive/2023/03/19/IntTools_FaceFace.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>McCad - BRep to CSG</title><link>http://www.cppblog.com/eryar/archive/2023/03/12/mccad_brep_csg.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Sun, 12 Mar 2023 10:19:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/03/12/mccad_brep_csg.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229751.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/03/12/mccad_brep_csg.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229751.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229751.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;">McCad - BRep to CSG</h1>
<p style="text-align: center; "><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230312181050519-5633893.jpg" alt="" /></p>
<p>McCad是一个开源工具，能自动将BRep模型转换成CSG模型。随着核动力技术的发展，不断开展新型反应堆的研究，反应堆的燃料形式和堆芯布置都较为复杂，由于蒙特卡罗(MC)方法具有强大的几何处理能力和较高的计算精确度，它是模拟分析这些复杂堆芯的有效手段。通过使用McCad将复杂BRep模型转换成CSG模型，CSG表示的模型可作为核反应堆芯计算蒙特卡罗(MC)方法的输入。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230312181111035-1029858497.png" alt="" /></p>
<p id="1678615874165"></p>
<p>McCad基于OpenCASCADE内核已经持续开发了20年，基于LGPL协议开源，感兴趣的小伙伴可以下载尝试一下：</p>
<p><a href="https://github.com/inr-kit/McCAD-Library">https://github.com/inr-kit/McCAD-Library</a></p>
<p>BRep转CSG个人感觉没有完美的解决方案，但是应用范围很广。如果转换效果好，其实还有一个用途，就是将BRep转换的CSG模型导入到PDMS中，从而解决通过机械设备接口MEI导入STEP/IGES后PDMS模型数据变大的问题。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202303/534255-20230312181121060-1211505435.png" alt="" /></p>
<p>McCad is an open source tool for automatic conversion of B-Rep models into CSG.</p>
<p>McCad has been continuously developed for more than two decades. It provides automatic conversion of Boundary Representation (BREP) CAD models into Constructive Solid Geometry (CSG), the latter of which is an input syntax often used in Monte Carlo (MC) radiation transport codes. The conversion process, from BREP to CSG, is essential for high-fidelity nuclear analysis of complex nuclear facilities. McCad can convert CAD files in STEP format to different input syntaxes used with MC codes such as MCNP, TRIPOLI, Geant4, etc., so that the manual efforts on building a complex simulation model can be avoided. McCad provides an advanced algorithm to decompose complex solids into its constituent convex primitives, and generates the void space description between the solids, which is required by MC codes.</p>
<p>&nbsp;</p>
<p>It relies on OpenCASCADE CAD kernel to perform CAD manipulations and Boolean operations. McCad has been integrated into the SALOME platform, with a detailed manual provided. It is an open-source code released under LGPL license. This code has been currently used for nuclear research institutes crossing continents, for nuclear analyses on fission reactors, fusion facilities and neutron source facilities.</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eryar/aggbug/229751.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-03-12 18:19 <a href="http://www.cppblog.com/eryar/archive/2023/03/12/mccad_brep_csg.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OpenCASCADE Visualization Performance</title><link>http://www.cppblog.com/eryar/archive/2023/01/16/occt_visualization_performance.html</link><dc:creator>eryar</dc:creator><author>eryar</author><pubDate>Mon, 16 Jan 2023 14:58:00 GMT</pubDate><guid>http://www.cppblog.com/eryar/archive/2023/01/16/occt_visualization_performance.html</guid><wfw:comment>http://www.cppblog.com/eryar/comments/229630.html</wfw:comment><comments>http://www.cppblog.com/eryar/archive/2023/01/16/occt_visualization_performance.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eryar/comments/commentRss/229630.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eryar/services/trackbacks/229630.html</trackback:ping><description><![CDATA[<h1 style="text-align: center;"><strong>OpenCASCADE Visualization Performance</strong></h1>
<p style="text-align: center;"><a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#101;&#114;&#121;&#97;&#114;&#64;&#49;&#54;&#51;&#46;&#99;&#111;&#109;"><u>eryar@163.com</u></a></p>
<p>&nbsp;</p>
<h2>1 Introduction</h2>
<p>OpenCASCADE的显示模块的功能性能如何，很多人都很关心。开源社区的FreeCAD目前的显示功能都没有使用OpenCASCADE的显示模块。早在2014年时，我在社区论坛提出过显示模块交互选择的性能问题：</p>
<p><a href="https://dev.opencascade.org/content/selection-convert-2d-not-very-efficient"><u>https://dev.opencascade.org/content/selection-convert-2d-not-very-efficient</u></a></p>
<p>当时的版本应该是6.8.0。在6.7.1的发布信息中还提到了我很开心：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224313002-1873822445.png" alt="" /></p>
<p>当时KGV也正在优化这部分的功能：</p>
<p><a href="https://tracker.dev.opencascade.org/view.php?id=24623"><u>https://tracker.dev.opencascade.org/view.php?id=24623</u></a></p>
<p>通过引入BVH来更高效地处理选择，这个功能集成到6.9.0的版本中了。在6.8.0版本中已经在增强显示模块的性能，如引入culling机制：</p>
<p><a href="https://tracker.dev.opencascade.org/view.php?id=24307"><u>https://tracker.dev.opencascade.org/view.php?id=24307</u></a></p>
<p>到7.4.0版本，culling基本完善，当模型超出视锥体范围就从显存中去除：</p>
<p><a href="https://tracker.dev.opencascade.org/view.php?id=30223"><u>https://tracker.dev.opencascade.org/view.php?id=30223</u></a></p>
<p>到现在最新版本，显示模块的显示和交互功能的性能到底如何，下面给出我的一个测试，测试结果仅供参考。</p>
<p>&nbsp;</p>
<h2>2 硬件信息</h2>
<p>测试电脑的配置信息如下表：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224340102-568798300.png" alt="" /></p>
<p>这台电脑已经是好几年之前的配置了，相对现在的主流配置已经落后了。</p>
<h2>3 测试结果</h2>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224400076-965631313.png" alt="" /></p>
<p>这个测试模型是船的艏部模型，包括船体结构和舾装模型，总共的三角面片数量为5百万，帧数FPS为25.8，帧数大于12应该算流畅。交互选择性能很好，感觉不到延迟，鼠标移动到模型上就可以高亮。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224409301-2036646167.png" alt="" /></p>
<p>这个测试模型是一个渡轮，包括船体结构和舾装的所有模型，总共的三角面片数量为1千2百万，帧数FPS为0.1，视图操作（对视图缩放、旋转、移动）已经有比较严重的延迟，但是交互选择性能还不错，没有延迟，鼠标移动到模型上也是实时高亮。</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224416748-359246980.png" alt="" /></p>
<p>这个是海洋平台模型，包括结构和舾装模型。这个模型量最大，总共有1千7百万三角面片，帧数FPS为2.2，视图操作（对视图缩放、旋转、移动）已经有比较严重的延迟，但是交互选择性能还不错，没有延迟，鼠标移动到模型上也是实时高亮。这个最大的模型占用内存情况如下图所示：</p>
<p><img src="https://img2023.cnblogs.com/blog/534255/202301/534255-20230116224428280-2016376164.png" alt="" /></p>
<p>软件总共占存3.4G内存，这其中还包含左边的设计导航树的数据。当将模型放大，超出视图范围外的模型已经被剔除culling，所以可以从上图可以看出三角面片数量变少了，为6百万。</p>
<h2>4 Conclusion</h2>
<p>通过以上的测试数据，大家可以结合自己行业模型的体量来选择是否使用OpenCASCADE的显示模块。对于接近2千万三角面片的模型来说，模型量已经比较大，在这个电脑配置情况下基本能满足一些大体量的模型显示及交互操作。因为对于大的设计模型，一般在设计过程中，也不是一个人设计，而是多人多专业协同设计，一个人涉及到的模型量一般不会达到2千万这个量级。而当设计完成，只需要浏览时（如模型评审），这时就有很多优化手段。</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/eryar/aggbug/229630.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eryar/" target="_blank">eryar</a> 2023-01-16 22:58 <a href="http://www.cppblog.com/eryar/archive/2023/01/16/occt_visualization_performance.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>