OpenCASCADE BRepTools
Abstract. OpenCASCADE BRepTools provides utilities for BRep data structure. OuterWire method to find the outer wire of a face. Dump method to dump a BRep object. It also can be used as the data exchange for OpenCASCADE native shapes.
Key Words. OpenCASCADE, BRepTools, BRep, Topology
1. Introduction
OpenCASCADE 提供了一个类BRepTools,其中有许多static函数,主要用来对BRep表示的拓朴形状的数据进行读写,也提供了查找一个面中外环(Outer Wire)的函数。因为OpenCASCADE中的边界表示法BRep的数据结构如下图1.1所示:
Figure 1.1 BRep Data Structure of OpenCASCADE
因为OpenCASCADE中拓朴结构采用了包含关系,当需要将TopoDS_Shape数据保存到文件时,如何保持TopoDS_Shape中的关系,以便于从文件读取这些数据时,可以重构出TopoDS_Shape中的各种关系?
参 考opennurbs中的BRep表示时数据的存储方式,可知直接在BRep中保存拓朴及几何数据的索引,这样对数据的存储及读取时重构拓朴结构还是很方 便的。而在OpenCASCADE中拓朴数据是以Handle来保存的,且为组合关系,即一个父结构中有一个列表 (TopoDS_ListOfShape)给包含了子结构数据。对于没有索引的OpenCASCADE的拓朴结构,如何进行读写操作呢?
本文结合类BRepTools中的函数,对OpenCASCADE中TopoDS_Shape数据的保存和读取功能的代码进行分析,从而对ModelingData中的BRep数据做进一步的理解。
2.Topology Shape Serialization
OpenCASCADE的类BRepTools中提供了如下函数,可以TopoDS_Shape中的数据进行导入导出:
v BRepTools::Dump();
v BRepTools::Read();
v BRepTools::Write();
这 几个函数比较常用,因为可以方便地将TopoDS_Shape导出,或导入到OpenCASCADE的Draw Test Harness中,来对程序一些算法进行验证。对于使用了组合关系的TopoDS_Shape如何确保数据的保存及读取后,能够维持这些关系?带着这个问 题去看BRep文件读写的功能,应该更为清晰。
还是看看代码,如下所示为输出TopoDS_Shape的函数,在程序Debug时比较常用:
//======================================================================= //function : Dump //purpose : //======================================================================= void BRepTools::Dump(const TopoDS_Shape& Sh, Standard_OStream& S) { BRepTools_ShapeSet SS; SS.Add(Sh); SS.Dump(Sh,S); SS.Dump(S); }
其中使用了类BRepTools_ShapeSet,这里的Set的意思我理解为集合的意思,其Add函数如下:
//======================================================================= //function : Add //purpose : //======================================================================= Standard_Integer TopTools_ShapeSet::Add(const TopoDS_Shape& S) { if (S.IsNull()) return 0; myLocations.Add(S.Location()); TopoDS_Shape S2 = S; S2.Location(TopLoc_Location()); Standard_Integer index = myShapes.FindIndex(S2); if (index == 0) { AddGeometry(S2); for (TopoDS_Iterator its(S2,Standard_False,Standard_False); its.More(); its.Next()) Add(its.Value()); index = myShapes.Add(S2); } return index; }
这是一个递归函数,通过AddGeometry函数,将TopoDS_Shape中的几何信息都保存到相应的集合Set中,Set中使用了Map,即给每个几何信息一个唯一的编号与之对应。
//======================================================================= //function : AddGeometry //purpose : //======================================================================= void BRepTools_ShapeSet::AddGeometry(const TopoDS_Shape& S) { // Add the geometry if (S.ShapeType() == TopAbs_VERTEX) { Handle(BRep_TVertex) TV = Handle(BRep_TVertex)::DownCast(S.TShape()); BRep_ListIteratorOfListOfPointRepresentation itrp(TV->Points()); while (itrp.More()) { const Handle(BRep_PointRepresentation)& PR = itrp.Value(); if (PR->IsPointOnCurve()) { myCurves.Add(PR->Curve()); } else if (PR->IsPointOnCurveOnSurface()) { myCurves2d.Add(PR->PCurve()); mySurfaces.Add(PR->Surface()); } else if (PR->IsPointOnSurface()) { mySurfaces.Add(PR->Surface()); } ChangeLocations().Add(PR->Location()); itrp.Next(); } } else if (S.ShapeType() == TopAbs_EDGE) { // Add the curve geometry Handle(BRep_TEdge) TE = Handle(BRep_TEdge)::DownCast(S.TShape()); BRep_ListIteratorOfListOfCurveRepresentation itrc(TE->Curves()); while (itrc.More()) { const Handle(BRep_CurveRepresentation)& CR = itrc.Value(); if (CR->IsCurve3D()) { if (!CR->Curve3D().IsNull()) { myCurves.Add(CR->Curve3D()); ChangeLocations().Add(CR->Location()); } } else if (CR->IsCurveOnSurface()) { mySurfaces.Add(CR->Surface()); myCurves2d.Add(CR->PCurve()); ChangeLocations().Add(CR->Location()); if (CR->IsCurveOnClosedSurface()) myCurves2d.Add(CR->PCurve2()); } else if (CR->IsRegularity()) { mySurfaces.Add(CR->Surface()); ChangeLocations().Add(CR->Location()); mySurfaces.Add(CR->Surface2()); ChangeLocations().Add(CR->Location2()); } else if (myWithTriangles) { // for XML Persistence if (CR->IsPolygon3D()) { if (!CR->Polygon3D().IsNull()) { myPolygons3D.Add(CR->Polygon3D()); ChangeLocations().Add(CR->Location()); } } else if (CR->IsPolygonOnTriangulation()) { myTriangulations.Add(CR->Triangulation()); myNodes.Add(CR->PolygonOnTriangulation()); ChangeLocations().Add(CR->Location()); if (CR->IsPolygonOnClosedTriangulation()) myNodes.Add(CR->PolygonOnTriangulation2()); } else if (CR->IsPolygonOnSurface()) { mySurfaces.Add(CR->Surface()); myPolygons2D.Add(CR->Polygon()); ChangeLocations().Add(CR->Location()); if (CR->IsPolygonOnClosedSurface()) myPolygons2D.Add(CR->Polygon2()); } } itrc.Next(); } } else if (S.ShapeType() == TopAbs_FACE) { // Add the surface geometry Handle(BRep_TFace) TF = Handle(BRep_TFace)::DownCast(S.TShape()); if (!TF->Surface().IsNull()) mySurfaces.Add(TF->Surface()); if (myWithTriangles) { // for XML Persistence Handle(Poly_Triangulation) Tr = TF->Triangulation(); if (!Tr.IsNull()) myTriangulations.Add(Tr); } ChangeLocations().Add(TF->Location()); } }
由上述代码可知,Edge中的几何信息较多,Face中的几何信息最少,只是几何曲面或其用于显示的网格数据。在将拓朴数据输出时,拓朴面、边及顶点中包含的几何信息都是前面几何数据的编号,即相当于索引号的形式输出,代码如下所示:
//======================================================================= //function : WriteGeometry //purpose : //======================================================================= void BRepTools_ShapeSet::WriteGeometry(const TopoDS_Shape& S, Standard_OStream& OS)const { // Write the geometry if (S.ShapeType() == TopAbs_VERTEX) { // Write the point geometry TopoDS_Vertex V = TopoDS::Vertex(S); OS << BRep_Tool::Tolerance(V) << \"\\n\"; gp_Pnt p = BRep_Tool::Pnt(V); OS<<p.X()<<\" \"<<p.Y()<<\" \"<<p.Z()<<\"\\n\"; Handle(BRep_TVertex) TV = Handle(BRep_TVertex)::DownCast(S.TShape()); BRep_ListIteratorOfListOfPointRepresentation itrp(TV->Points()); while (itrp.More()) { const Handle(BRep_PointRepresentation)& PR = itrp.Value(); OS << PR->Parameter(); if (PR->IsPointOnCurve()) { OS << \" 1 \" << myCurves.Index(PR->Curve()); } else if (PR->IsPointOnCurveOnSurface()) { OS << \" 2 \" << myCurves2d.Index(PR->PCurve()); OS << \" \" << mySurfaces.Index(PR->Surface()); } else if (PR->IsPointOnSurface()) { OS << \" 3 \" << PR->Parameter2() << \" \"; OS << mySurfaces.Index(PR->Surface()); } OS << \" \" << Locations().Index(PR->Location()); OS << \"\\n\"; itrp.Next(); } OS << \"0 0\\n\"; // end representations } else if (S.ShapeType() == TopAbs_EDGE) { // Write the curve geometry Handle(BRep_TEdge) TE = Handle(BRep_TEdge)::DownCast(S.TShape()); OS << \" \" << TE->Tolerance() << \" \"; OS << ((TE->SameParameter()) ? 1 : 0) << \" \"; OS << ((TE->SameRange()) ? 1 : 0) << \" \"; OS << ((TE->Degenerated()) ? 1 : 0) << \"\\n\"; Standard_Real first, last; BRep_ListIteratorOfListOfCurveRepresentation itrc = TE->Curves(); while (itrc.More()) { const Handle(BRep_CurveRepresentation)& CR = itrc.Value(); if (CR->IsCurve3D()) { if (!CR->Curve3D().IsNull()) { Handle(BRep_GCurve) GC = Handle(BRep_GCurve)::DownCast(itrc.Value()); GC->Range(first, last); OS << \"1 \"; // -1- Curve 3D OS << \" \"<<myCurves.Index(CR->Curve3D()); OS << \" \"<<Locations().Index(CR->Location()); OS << \" \"<<first<<\" \"<<last; OS << \"\\n\"; } } else if (CR->IsCurveOnSurface()) { Handle(BRep_GCurve) GC = Handle(BRep_GCurve)::DownCast(itrc.Value()); GC->Range(first, last); if (!CR->IsCurveOnClosedSurface()) OS << \"2 \"; // -2- Curve on surf else OS << \"3 \"; // -3- Curve on closed surf OS <<\" \"<<myCurves2d.Index(CR->PCurve()); if (CR->IsCurveOnClosedSurface()) { OS <<\" \" << myCurves2d.Index(CR->PCurve2()); PrintRegularity(CR->Continuity(),OS); } OS << \" \" << mySurfaces.Index(CR->Surface()); OS << \" \" << Locations().Index(CR->Location()); OS << \" \"<<first<<\" \"<<last; OS << \"\\n\"; // Write UV Points // for XML Persistence higher performance if (FormatNb() == 2) { gp_Pnt2d Pf,Pl; if (CR->IsCurveOnClosedSurface()) { Handle(BRep_CurveOnClosedSurface) COCS = Handle(BRep_CurveOnClosedSurface)::DownCast(CR); COCS->UVPoints2(Pf,Pl); } else { Handle(BRep_CurveOnSurface) COS = Handle(BRep_CurveOnSurface)::DownCast(CR); COS->UVPoints(Pf,Pl); } OS << Pf.X() << \" \" << Pf.Y() << \" \" << Pl.X() << \" \" << Pl.Y() << \"\\n\"; } } else if (CR->IsRegularity()) { OS << \"4 \"; // -4- Regularity PrintRegularity(CR->Continuity(),OS); OS << \" \"<<mySurfaces.Index(CR->Surface()); OS << \" \"<<Locations().Index(CR->Location()); OS << \" \"<<mySurfaces.Index(CR->Surface2()); OS << \" \"<<Locations().Index(CR->Location2()); OS << \"\\n\"; } else if (myWithTriangles) { // for XML Persistence if (CR->IsPolygon3D()) { Handle(BRep_Polygon3D) GC = Handle(BRep_Polygon3D)::DownCast(itrc.Value()); if (!GC->Polygon3D().IsNull()) { OS << \"5 \"; // -5- Polygon3D OS << \" \"<<myPolygons3D.FindIndex(CR->Polygon3D()); OS << \" \"<<Locations().Index(CR->Location()); OS << \"\\n\"; } } else if (CR->IsPolygonOnTriangulation()) { Handle(BRep_PolygonOnTriangulation) PT = Handle(BRep_PolygonOnTriangulation)::DownCast(itrc.Value()); if (!CR->IsPolygonOnClosedTriangulation()) OS << \"6 \"; // -6- Polygon on triangulation else OS << \"7 \"; // -7- Polygon on closed triangulation OS << \" \" << myNodes.FindIndex(PT->PolygonOnTriangulation()); if (CR->IsPolygonOnClosedTriangulation()) { OS << \" \" << myNodes.FindIndex(PT->PolygonOnTriangulation2()); } OS << \" \" << myTriangulations.FindIndex(PT->Triangulation()); OS << \" \"<<Locations().Index(CR->Location()); OS << \"\\n\"; } } itrc.Next(); } OS << \"0\\n\"; // end of the list of representations } else if (S.ShapeType() == TopAbs_FACE) { Handle(BRep_TFace) TF = Handle(BRep_TFace)::DownCast(S.TShape()); const TopoDS_Face& F = TopoDS::Face(S); if (!(TF->Surface()).IsNull()) { OS << ((BRep_Tool::NaturalRestriction(F)) ? 1 : 0); OS << \" \"; // Write the surface geometry OS << \" \" <<TF->Tolerance(); OS << \" \" <<mySurfaces.Index(TF->Surface()); OS << \" \" <<Locations().Index(TF->Location()); OS << \"\\n\"; } else //For correct reading of null face { OS << 0; OS << \" \"; OS << \" \" <<TF->Tolerance(); OS << \" \" << 0; OS << \" \" << 0; OS << \"\\n\"; } if (myWithTriangles) { // for XML Persistence if (!(TF->Triangulation()).IsNull()) { OS << 2; OS << \" \"; // Write the triangulation OS << \" \" <<myTriangulations.FindIndex(TF->Triangulation()); } } } }
通过先将几何数据收集到相应的集合(映射)中,再在拓朴结构对应的地方以索引号的方式输出,这样就便于从文件读取数据时,以类似的方式来重构BRep边界表示的拓朴Shape的结构。即读取文件重构拓朴结构数据是输出的逆过程。
在实现从文件读取BRep表示的数据时,先将几何信息读取到对应的集合中,再读取拓朴结构数据时,若拓朴结构中包含几何信息,则以索引的方式,找到对应的几何数据即可。详细实现可参考源程序。
3. For Debugging
由 于BRepTools为Toolkit TKBRep中的类,所以依赖的动态库较少,所以在编程时,若要验证一些算法的正确性时,经常需要将TopoDS_Shape的数据导出,甚至可以直接先 在Draw Test Harness中使用相关命令来将导出的数据导入来查看结果。
4. Conclusion
通 过BRepTools中对TopoDS_Shape数据的输出及导入的代码分析可知,对于只有组合关系的数据,若想维持这种关系,就需要引入集合映射的类 来产生索引,进而在读取数据时,可以根据索引来重构拓朴关系。由于opennurbs中的BRep在内存中本来就是索引的方式,所以在数据存取时,实现要 简单很多。
5. References
1. OpenCASCADE Team. BRep Format. 2014.12
2. Shing Liu. Topology and Geometry in OpenCascade-Topology.
http://www.cppblog.com/eryar/archive/2013/09/21/203338.html
3. Shing Liu. Topology and Geometry in OpenCascade-Vertex
http://www.cppblog.com/eryar/archive/2013/08/20/202678.html
4. Shing Liu. Topology and Geometry in OpenCascade-Edge
http://www.cppblog.com/eryar/archive/2013/08/24/202739.html
5. Shing Liu. Topology and Geometry in OpenCascade-Face
暂无评论内容