Topology Shapes of OpenCascade BRep
eryar@163.com
摘要Abstract:通過對OpenCascade中的BRep數據的讀寫,理解邊界表示法的概念及實現。理解了拓樸形狀的數據結構,就對ModelingData模塊有了清晰認識,方便OpenCascade其他模塊如ModelingAlgorithms和Visiualization模塊的理解。
關鍵字Key Words:OpenCascade, BRep, Topology, BRep Format
一、引言 Introduction
邊界表示(Boundary Representation)也稱為BRep表示,它是幾何造型中最成熟、無二義的表示法。實體的邊界通常是由面的并集表示,而每個面又由它所在曲面的定義加上其邊界來表示,面的邊界是邊的并集,而邊又是由點來表示。如下圖1.1所示,曲面的匯合處形成曲線,而曲線的匯合處形成點。所以點、線、面是描述一個形狀所需要的基本組成單元。
Figure 1.1 BRep Shape demo
邊界表示的一個重要特點是描述形狀的信息包括幾何信息(geometry)和拓樸(topology)信息兩個方面。拓樸信息描述形狀上的頂點、邊、面的連接關系,它形成物體邊界表示的“骨架”。形狀的幾何信息猶如附著在“骨架”上的肌肉。在OpenCascade中,形狀的幾何信息包含曲線和曲面的參數解析表示Geom_Curve/Geom_Surface。
這樣我們就可以用平面方程和柱面方程來描述曲面,用直線或圓弧方程來描述曲線。這時會出現一個問題,即代數表達式只能定義無邊界的幾何體。除了單個點、圓以及球體,經典的解析幾何僅能表示無限延伸的曲線和曲面。為了解決這個問題,邊界表示法按下述方法明確地定義曲線或曲面的邊界:
l 曲線的邊界由位于曲線上的一對點來確定;
l 曲面的邊界由位于曲面上的一組曲線來確定;
通過這個方法,就可以定義一段曲線或一片曲面。這時,不同幾何元素之間的關系的組織問題就出現了,為此我們將記錄如下信息:
l 哪些點界定哪些曲線;
l 哪些曲線界定哪些曲面;
這些關于誰關聯誰的信息,就是幾何造型系統經常提到的拓樸。在邊界表示法中,理論上表示一個物理模型只需要三個拓樸體(頂點TopoDS_Vertex、邊TopoDS_Edge和面TopoDS_Face),但在實際應用中,為了提高計算機處理的速度或提供高級的操作功能,還要引入其他一些概念,如環TopoDS_Wire、殼TopoDS_Shell、復合體TopoDS_Compound等。
二、邊界表示形狀中的幾何數據 Geometry of BRep shapes
對形狀數據的讀寫主要是由類BRepTools_ShapeSet來完成的,其中在類的函數AddGeometry中對拓樸形狀中的幾何數據進行了處理,代碼如下所示:
//=================================================================
//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());
}
}
根據上述代碼可知,OpenCascade在保存和讀寫BRep表示的形狀時,只保存了頂點、邊和面的信息,因為只有這三個拓樸結構中包含了幾何信息及顯示用的離散點和三角網格數據。有了這些信息,就可以生成一個邊界表示的形狀了。
幾何之間的聯系也保存起來了,這也是拓樸數據的一種形式,在下節詳細說明。
三、邊界表示形狀中的拓樸數據 Topology of BRep shapes
關于拓樸頂點TopoDS_Vertex、邊TopoDS_Edge、面TopoDS_Face更詳細的信息,請參考博客:
l Topology and Geometry in OpenCascade-Vertex;
l Topology and Geometry in OpenCascade-Edge;
l Topology and Geometry in OpenCascade-Face;
l Topology and Geometry in OpenCascade-Topology;
本文只對OpenCascade拓樸結構中的幾何數據的關聯信息進行分析。
3.1 頂點 TopoDS_Vertex
結合《BRep Format Description White Paper》中對<vertex data>的描述,及程序代碼中對頂點數據的讀取,分析OpenCascade的BRep表示中的頂點。
Figure 3.1.1 NBF-like definition of Vertex
詳細說明:
<vertex data representation u parameter>u的使用方法說明如下:
<vertex data representation data 1> 和參數u定義了三維曲線C上的點V的位置。參數u是曲線C上點V對應的參數:C(u)=V。對應的類是:BRep_PointOnCurve;
<vertex data representation data 2>和參數u定義了曲面上的二維曲線C上點V的位置。參數u是曲線C上點V對應的參數:C(u)=V。對應的類是:BRep_PointOnCurveOnSurface;
<vertex data representation data 3>和參數u及<vertex data representation v parameter>v定義了曲面S上的點V:S(u,v)=V。對應的類是:BRep_PointOnSurface;
在這些類中都將頂點對應的曲線、曲面及其上點的參數都保存起來了。有了這些信息就可以判斷與頂點有聯系的邊或面,因為曲線、曲面屬于邊和面。
<vertex data tolerance>t定義如下所示:
下面結合程序示例片段,創建一個頂點并將其輸出為BRep文件,并在OpenCascade中進行顯示。
void TestVertex(void)
{
ofstream dumpFile("vertex.brep");
TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(1.0, 2.0, 3.0));
BRepTools::Dump(aVertex, std::cout);
BRepTools::Write(aVertex, dumpFile);
}
當使用BRepTools::Dump時,顯示更易讀的信息。可將數據dump到屏幕,也可將數據dump到文件;當使用Write時,生成的信息即是BRep文件格式的數據。可以通過Read生成形狀,也可直接讀入到OpenCascade中顯示,如下圖3.1.2所示:
Figure 3.1.2 Import a Vertex from brep file
3.2 邊 TopoDS_Edge
詳細說明:
標志位<edge data same parameter flag>,<edge data same range flag>,<edge data degenerated flag>有特別的用途。
<edge data representation data 1>表示一個三維曲線,對應類:Geom_Curve;
<edge data representation data 2>表示曲面上的一個二維曲線,
對應類Geom_Curve/Geom_Surface;
<curve values for parameter minimal and maximal values>只在2版本中使用;
<edge data representation data 3>表示閉合曲面上的一個二維曲線;
對應類Geom_Curve/Geom_Surface;
<curve values for parameter minimal and maximal values>只在2版本中使用;
<edge data representation data 4>表示Regularity的邊,使用到的類有:
Geom_Curve/Geom_Surface;
<edge data representation data 5>表示一個三維的多段線(3D polyline);
對應的類:Poly_Polygon3D,是邊的近似表示,主要用來顯示;
<edge data representation data 6>表示三角剖分上一條多段線;
對應的類:Poly_PolygonOnTriangulation,也是邊在三角剖分上的近似表示;
<edge data tolerance> t的定義如下所示:
下面的示例程序片段將圓的邊導出為BRep文件,并在OpenCascade中顯示。程序代碼如下所示:
void TestEdge(bool bSubdivision = false)
{
ofstream dumpFile("edge.brep");
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Circ(gp::XOY(), 6.0));
if (bSubdivision)
{
BRepMesh::Mesh(anEdge, 1.0);
}
BRepTools::Dump(anEdge, std::cout);
BRepTools::Write(anEdge, dumpFile);
}
其中參數bSubdivision用來生成顯示用的離散多段線數據,這里會生成<edge data representation data 5>的Poly_Polygon3D,將生成的BRep文件導入進行顯示如下圖所示:
Figure 3.2.1 Import a Face from brep file
3.3 面 TopoDS_Face
詳細說明:
<face data>描述了面F的曲面S和三角剖分T。曲面S可能為空:<surface number>=0.
<face data tolerance> t的定義如下所示:
標志位<face data natural restriction flag>有特別的用途。
面中的數據比較簡單,有參數表示的曲面的索引號。若曲面已經被三角剖分,將會把剖分后的網格數據也保存起來。下面的示例程序片段將一個球面導出為brep文件:
void TestFace(bool bSubdivision = false)
{
ofstream dumpFile("face.brep");
TopoDS_Face aFace = BRepBuilderAPI_MakeFace(gp_Sphere(gp::XOY(), 6.0));
if (bSubdivision)
{
BRepMesh::Mesh(aFace, 1.0);
}
BRepTools::Dump(aFace, std::cout);
BRepTools::Write(aFace, dumpFile);
}
其中參數bSubdivision用來生成顯示用的網格數據,這里會生成Poly_Triangulation,將生成的BRep文件導入進行顯示如下圖所示:
Figure 3.3.1 Import a Face from brep
四、示例程序 Example Code
將上述代碼放在一起,完整的程序代碼如下所示:
/*
* Copyright (c) 2013 eryar All Rights Reserved.
*
* File : Main.cpp
* Author : eryar@163.com
* Date : 2013-12-21 21:18
* Version : 1.0v
*
* Description : Use BRepTools to dump and write BRep files.
*
* Key Words : OpenCascade, BRep, Vertex, Edge, Face
*
*/
// OpenCascade library.
#define WNT
#include <gp_Pnt.hxx>
#include <gp_Circ.hxx>
#include <gp_Sphere.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <BRepMesh.hxx>
#include <BRepTools.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#pragma comment(lib, "TKernel.lib")
#pragma comment(lib, "TKMath.lib")
#pragma comment(lib, "TKBRep.lib")
#pragma comment(lib, "TKMesh.lib")
#pragma comment(lib, "TKTopAlgo.lib")
void TestVertex(void)
{
ofstream dumpFile("vertex.brep");
TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(gp_Pnt(1.0, 2.0, 3.0));
BRepTools::Dump(aVertex, std::cout);
BRepTools::Write(aVertex, dumpFile);
}
void TestEdge(bool bSubdivision = false)
{
ofstream dumpFile("edge.brep");
TopoDS_Edge anEdge = BRepBuilderAPI_MakeEdge(gp_Circ(gp::XOY(), 6.0));
if (bSubdivision)
{
BRepMesh::Mesh(anEdge, 1.0);
}
BRepTools::Dump(anEdge, std::cout);
BRepTools::Write(anEdge, dumpFile);
}
void TestFace(bool bSubdivision = false)
{
ofstream dumpFile("face.brep");
TopoDS_Face aFace = BRepBuilderAPI_MakeFace(gp_Sphere(gp::XOY(), 6.0));
if (bSubdivision)
{
BRepMesh::Mesh(aFace, 1.0);
}
BRepTools::Dump(aFace, std::cout);
BRepTools::Write(aFace, dumpFile);
}
int main(void)
{
TestVertex();
TestEdge(true);
TestFace(true);
return 0;
}
也可以把更易懂的信息dump出來,這樣可以更好地理解brep文件的格式。當將球面導出為brep文件時,會生成很多信息,如下所示:
Shape : 7, FORWARD
Dump of 7 TShapes
-----------------
Flags : Free, Modified, Checked, Orientable, Closed, Infinite, Convex
TShape # 1 : FACE 1101000 01807868
+2
NaturalRestriction
Tolerance : 1e-007
- Surface : 1
TShape # 2 : WIRE 0101100 01807CE8
-5 +4 +5 -3
TShape # 3 : EDGE 0101000 01808C10
+6 -6
Tolerance : 1e-007
same parametrisation of curves
same range on curves
degenerated
- PCurve : 4 on surface 1, range : 0 6.28319
UV Points : 0, 1.5708 6.28319, 1.5708
TShape # 4 : EDGE 0101000 01807AA0
+7 -7
Tolerance : 1e-007
same parametrisation of curves
same range on curves
degenerated
- PCurve : 3 on surface 1, range : 0 6.28319
UV Points : 0, -1.5708 6.28319, -1.5708
TShape # 5 : EDGE 0101000 018078C8
+7 -6
Tolerance : 1e-007
same parametrisation of curves
same range on curves
- Curve 3D : 1, range : -1.5708 1.5708
- PCurve : 1, 2 (C0) on surface 1, range : -1.5708 1.5708
UV Points : 6.28319, -1.5708 6.28319, 1.5708
UV Points : 0, -1.5708 0, 1.5708
TShape # 6 : VERTEX 0101101 018076F0
Tolerance : 1e-007
- Point 3D : 3.67394e-016, 0, 6
TShape # 7 : VERTEX 0101101 01807680
Tolerance : 1e-007
- Point 3D : 3.67394e-016, 0, -6
-------
Dump of 4 Curve2ds
-------
1 : Line
Origin :6.28319, 0
Axis :0, 1
2 : Line
Origin :0, 0
Axis :0, 1
3 : Line
Origin :0, -1.5708
Axis :1, 0
4 : Line
Origin :0, 1.5708
Axis :1, 0
-------
Dump of 1 Curves
-------
1 : Trimmed curve
Parameters : 4.71239 7.85398
Basis curve :
Circle
Center :0, 0, 0
Axis :0, -1, 0
XAxis :1, 0, 0
YAxis :-0, 0, 1
Radius :6
-------
Dump of 0 Polygon3Ds
-------
-------
Dump of 0 PolygonOnTriangulations
-------
-------
Dump of 1 surfaces
-------
1 : SphericalSurface
Center :0, 0, 0
Axis :0, 0, 1
XAxis :1, 0, -0
YAxis :-0, 1, 0
Radius :6
-------
Dump of 0 Triangulations
-------
-------
Dump of 0 Locations
-------
根據上面的數據,可以很好地理解BRep中拓樸形狀的相關數據。大部分數據還是很直觀,便于理解的。其中有個數據可能需要解釋即PCurve(Parametric Curve),它是在參數(u,v)空間的曲面上的參數曲線。可能有些不好理解,結合程序代碼看下PCurve的使用,就會Aha!恍然大悟的:
//=================================================================//function : D0
//purpose :
//=================================================================
void BRep_CurveOnSurface::D0(const Standard_Real U, gp_Pnt& P) const
{
// shoud be D0 NYI
gp_Pnt2d P2d = myPCurve->Value(U);
P = mySurface->Value(P2d.X(),P2d.Y());
P.Transform(myLocation.Transformation());
}
此函數的作用是求PCurve上對應參數u的曲面上的點,即0次微分D0。根據PCurve上的一個參數u,可以求出對應參數u的PCurve上的點,把這個點的x,y分別作為參數曲面的參數u,v,即求出了曲面上的點。
五、結論 Conclusion
通過程序代碼將《BRep Format Description White Paper》中數據進行讀寫,深入理解OpenCascade的邊界表示法的數據結構模塊ModelingData,為理解其他模塊打下基礎。
在邊界表示法中,理論上表示一個物理模型只需要三個拓樸體(頂點TopoDS_Vertex、邊TopoDS_Edge和面TopoDS_Face),所以在對brep文件輸出時,只處理了這三種拓樸體的信息。在生成形狀時,主要也是處理這三種拓樸體,再根據他們生成其他拓樸體。
頂點、邊和面的幾何之間的聯系在brep中也保存起來了,有了這些信息,就可以判斷一個頂點是不是邊上的點等。通過示例程序,來理解參數曲線PCurve。
理解了brep表示的結構后,下一步準備來研究下造型算法模塊ModelingAlgorithms。
六、參考資料 References
1. BRepTools_ShapeSet.cpp of OpenCascade
2. TopTools_ShapeSet.cpp of OpenCascade
3. BRepTools.cpp Of OpenCascade
4. BRep Format Description White Paper of OpenCascade
5. 孫家廣等. 計算機圖形學. 清華大學出版社, 2000
6. 詹海生等, 基于ACIS的幾何造型技術與系統開發, 清華大學出版社, 2002