OpenCascade源码分析之StlAPI(网格离散化后导出stl文件)

您所在的位置:网站首页 boxmaker OpenCascade源码分析之StlAPI(网格离散化后导出stl文件)

OpenCascade源码分析之StlAPI(网格离散化后导出stl文件)

#OpenCascade源码分析之StlAPI(网格离散化后导出stl文件)| 来源: 网络整理| 查看: 265

OpenCascade源码分析之StlAPI(网格离散化后导出stl文件) 奈何调库满足不了项目需求,并且网上没有详细资料啊 大家都偷懒的话只能自己来分析了,那就分析一下源码 看它对曲面的离散的步骤是怎么样的吧 一、StlAPI.hxx|StlAPI.cxx

我们在调用将离散网格数据写入stl的时候,一般会用到如下方法:

//需要离散的拓扑Shape TopoDS_Shape cur; //保存的文件名 const char* name = "a.stl"; StlAPI::Write(cur,name);

那么对于Write方法是如何运作的呢,接下来来到源码部分

//============================================================================= //function : Write //purpose : //============================================================================= Standard_Boolean StlAPI::Write (const TopoDS_Shape& theShape, const Standard_CString theFile, const Standard_Boolean theAsciiMode) { StlAPI_Writer aWriter; aWriter.ASCIIMode() = theAsciiMode; return aWriter.Write (theShape, theFile); }

代码中调用了 StlAPI_Writer.hxx 同时设置了写文件是以文本格式还是二进制模式输出

接下来来到StlAPI_Writer部分查看实现 二、StlAPI_Writer.hxx | StlAPI_Writer.cxx 首先补充几个步骤 方便之后的代码阅读 关于Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (aFace), aLoc); Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (aFace), aLoc);

该句代码主要调用了

const Poly_ListOfTriangulation& BRep_Tool::Triangulations (const TopoDS_Face& theFace, TopLoc_Location& theLocation) { theLocation = theFace.Location(); const BRep_TFace* aTFace = static_cast(theFace.TShape().get()); return aTFace->Triangulations(); } //而aTFace调用了下面的方法 //======================================================================= //function : Triangulation //purpose : //======================================================================= const Handle(Poly_Triangulation)& BRep_TFace::Triangulation (const Poly_MeshPurpose thePurpose) const { //这边thePurpose默认为NONE if (thePurpose == Poly_MeshPurpose_NONE) { return ActiveTriangulation(); } xxxxx //之后的代码 没用到 就不解读了先删除 } //ActiveTrigulation()返回了成员Handle(Poly_Triangulation) myActiveTriangulation; const Handle(Poly_Triangulation)& ActiveTriangulation() const { return myActiveTriangulation; }

此文件中只实现了一个方法,就是Write方法 引用的头文件为:

#include #include #include #include #include #include #include #include #include #include #include #include #include

实现部分:(解读部分写成注释了)

Standard_Boolean StlAPI_Writer::Write (const TopoDS_Shape& theShape, const Standard_CString theFileName, const Message_ProgressRange& theProgress) { Standard_Integer aNbNodes = 0; Standard_Integer aNbTriangles = 0; // 计算离散之后点的总数目和面的总数目 //例如:一个圆柱体由三个TopoDS_Face组成,顶面、底面、侧面,对每个面进行离散化后 // 每个面上都会产生三角面和顶点,计算总数即可。 for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) { TopLoc_Location aLoc; Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (anExpSF.Current()), aLoc); if (! aTriangulation.IsNull()) { aNbNodes += aTriangulation->NbNodes (); aNbTriangles += aTriangulation->NbTriangles (); } } //这里如果没有执行过BRepMesh_IncrementalMesh的离散的话 //TopoDS_Face是没有三角信息的 if (aNbTriangles == 0) { // No triangulation on the shape // 这个Shape上没有面 return Standard_False; } // 建立临时的Poly_Triangulation //在OpenCascade中曲面的三角剖分的网格数据都保存在类Poly_Triangulation中 //TopoDS_Shape是不存储实际数据的 //实际上是初始化了如下成员变量 //Poly_ArrayOfNodes myNodes; //存放点 //Poly_Array1OfTriangle myTriangles; //存放面 Handle(Poly_Triangulation) aMesh = new Poly_Triangulation (aNbNodes, aNbTriangles, Standard_False); // 记录没有进行离散的TopoDS_Face数量 Standard_Integer aNbFacesNoTri = 0; // 为填充数据做准备 Standard_Integer aNodeOffset = 0; Standard_Integer aTriangleOffet = 0; //遍历Shape上所有的面 for (TopExp_Explorer anExpSF (theShape, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) { const TopoDS_Shape& aFace = anExpSF.Current(); TopLoc_Location aLoc; //获取了一个用于实际存储数据的句柄进行操作 Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation (TopoDS::Face (aFace), aLoc); //如果句柄为NULL说明该面没有被离散 if (aTriangulation.IsNull()) { //记录没有离散面的数量 ++aNbFacesNoTri; continue; } //如果面被离散过了 并且有了三角形数据则进行下面的操作 // 复制顶点 //gp_Trsf是用于将2D点到3D的转换 gp_Trsf aTrsf = aLoc.Transformation(); //遍历所有的点,下标从1开始 for (Standard_Integer aNodeIter = 1; aNodeIter NbNodes(); ++aNodeIter) { //gp_Pnt用于存储点的实体数据 gp_Pnt aPnt = aTriangulation->Node (aNodeIter); //坐标转换 aPnt.Transform (aTrsf); //SetNode(theIndex,thePnt)调用了myNodes.SetValue (theIndex - 1, thePnt); //将aMesh中存放点的数组增加点进去 //aNodeOffset是防止点覆盖到相同的下标上去 因为每个面的aNodeIter都是从1开始计算的 aMesh->SetNode (aNodeIter + aNodeOffset, aPnt); } // 存储三角网格的数据 和上面差不多 // 获得三角形的朝向,对这个不理解的可以去阅读一下TopoDS的文档,这是拓扑定义 const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation(); for (Standard_Integer aTriIter = 1; aTriIter NbTriangles(); ++aTriIter) { Poly_Triangle aTri = aTriangulation->Triangle (aTriIter); //获得顶点的下标 在离散化后会自动生成index->pnt 一个下标对应一个点 //Standard_Integer就是int类型 只不过被typedef了 写成int也可以 Standard_Integer anId[3]; aTri.Get (anId[0], anId[1], anId[2]); //如果面翻转了,则翻回来 if (anOrientation == TopAbs_REVERSED) { // Swap 1, 2. Standard_Integer aTmpIdx = anId[1]; anId[1] = anId[2]; anId[2] = aTmpIdx; } // 防止遍历不同面的时候产生相同的id,加上偏移量重新生成三角形 anId[0] += aNodeOffset; anId[1] += aNodeOffset; anId[2] += aNodeOffset; // 重新设置面上点的id值 aTri.Set (anId[0], anId[1], anId[2]); aMesh->SetTriangle (aTriIter + aTriangleOffet, aTri); } //偏移量增加 aNodeOffset += aTriangulation->NbNodes(); aTriangleOffet += aTriangulation->NbTriangles(); } OSD_Path aPath (theFileName); Standard_Boolean isDone = (myASCIIMode ? RWStl::WriteAscii(aMesh, aPath, theProgress) : RWStl::WriteBinary(aMesh, aPath, theProgress)); return isDone; } OK 现在我们已经理解了如何设置Poly_Triangulation来保存一个被离散过的TopoDS_Shape的实体数据 接下来就是RWStl::WriteAscii(aMesh, aPath, theProgress); 让我们来看看它是如何通过Poly_Triangulation实现写入操作 三、写入操作 RWStl.hxx | RWStl.cxx 话不多说,直接上源码 stl文件的写入还是简单的 //============================================================================= //function : writeASCII //purpose : //============================================================================= Standard_Boolean RWStl::writeASCII (const Handle(Poly_Triangulation)& theMesh, FILE* theFile, const Message_ProgressRange& theProgress) { // note that space after 'solid' is necessary for many systems // 写入文件中 if (fwrite ("solid \n", 1, 7, theFile) != 7) { return Standard_False; } //这边都是基础知识 应该很好理解,也可以改写为iostream的方式读写 char aBuffer[512]; memset (aBuffer, 0, sizeof(aBuffer)); //获取三角面的个数 const Standard_Integer NBTriangles = theMesh->NbTriangles(); //这玩意是个进度条,没什么用 不用管,除非有进度可视化需求 Message_ProgressScope aPS (theProgress, "Triangles", NBTriangles); //创建了一个保存顶点id的数组 Standard_Integer anElem[3] = {0, 0, 0}; for (Standard_Integer aTriIter = 1; aTriIter aVNorm.Normalize(); } else { aVNorm.SetCoord (0.0, 0.0, 0.0); } //写入面的数据,stl的格式可以自行搜索一下 stl的格式非常的简单 Sprintf (aBuffer, " facet normal % 12e % 12e % 12e\n" " outer loop\n" " vertex % 12e % 12e % 12e\n" " vertex % 12e % 12e % 12e\n" " vertex % 12e % 12e % 12e\n" " endloop\n" " endfacet\n", aVNorm.X(), aVNorm.Y(), aVNorm.Z(), aP1.X(), aP1.Y(), aP1.Z(), aP2.X(), aP2.Y(), aP2.Z(), aP3.X(), aP3.Y(), aP3.Z()); if (fprintf (theFile, "%s", aBuffer) if (!aPS.More()) return Standard_False; aPS.Next(IND_THRESHOLD); } } //stl文件的结束符 if (fwrite ("endsolid\n", 1, 9, theFile) != 9) { return Standard_False; } return Standard_True; }

好啦,至此整体的源码就已经读完了,如果还有不懂的可以评论区评论,或者自己读读源码咯。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3