天天看點

【第二回】使用OCCT實作對一個瓶子模組化的總結

資料:Open CASCADE Technology : Turorial

1.      模組化一個瓶子的第一步是建立瓶子底部的輪廓profile。

首先是建立半邊輪廓,然後再通過mirror(反射),生成另外一邊輪廓,接着就把這兩個半邊輪廓連接配接起來形成一個閉環。

在建立半邊輪廓時,這個半邊輪廓由這幾段線段組成:直線段-弧線段-直線段,其中這個弧線段并不能由BRepBuilderAPI_MakeXXX直接生成,package GC提供了一個類GC_MakeArcOfCircle,這個類可以生成圓弧。

設瓶子的寬厚高辨別分别為:width,thick, height。

輪廓的尺寸及坐标系見下圖:

【第二回】使用OCCT實作對一個瓶子模組化的總結

先定義直線段-弧線段-直線段的點坐标為:

(-width/2, 0, 0), (-width/2, -thick/4, 0)—

(-width/2, -thick/4, 0), (0, -thick/2, 0), (width/2, -thick/4, 0)—

(width/2, -thick/4, 0), (width/2, 0, 0)

直線段可以直接用BRepBuilderAPI_MakeEdge,也可以先使用GC_MakeSegment,傳回Handle<Geom_TrimmedCurve>,然後再使用BRepBuilderAPI_MakeEdge,将Handle<Geom_TrimmedCurve>代入,傳回TopoDS_Edge。

基本上在所有的模組化過程當中,都需要用到TopoDS_Shape繼承體系下的類,因為TopoDS_Shape繼承體系下的類是描述模型的拓撲資訊,這個拓撲資訊就相當于模型模組化時的骨架,而Geom_Geometry繼承體系下的類則是描述模型的幾何資訊,從結構上來說,幾何資訊是附屬在拓撲資訊上的,幾何資訊是可以更換的。

通過代碼:

Handle<Geom_TrimmedCurve> seg1 = GC_MakeSegment(pnt1, pnt2);
Handle<Geom_TrimmedCurve> arcSeg = GC_MakeArcOfCircle(pnt2,pnt3, pnt4);
Handle<Geom_TrimmedCurve> seg2 = GC_MakeSegment(pnt4, pnt5);
           

建立了這三條線段的幾何資訊。那麼對這些資訊的組裝則需要添加他們的拓撲資訊:

TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(seg1);
TopoDS_Edge arcEdge =BRepBuilderAPI_MakeEdge(arcSeg);
TopoDS_Edge edge2 =BRepBuilderAPI_MakeEdge(seg2);
           

現在需要将這三條拓撲邊組成一個環,環有開閉之分,在這裡這是一個半邊環,是以是開環:

BRepBuilderAPI_MakeWiremkWire(edge1, arcEdge, edge2);
TopoDS_Wire wireHalfProfile =mkWire.Wire();
           

到現在為止,瓶子底部輪廓的半邊已經建好了,這裡需要注意的是:在将邊組成環的過程中,這些邊必須是首位相連的,而且增加邊的前後順序不能打亂!現在就是通過mirror(反射)操作生成輪廓的另一個半邊,首先需要設定mirror的參數,鏡子放在哪個位置,然後放在什麼方向,從上面的示意圖可以看出,鏡子的位置在原點(0, 0, 0),方向為(1, 0, 0):

gp_Pnt mirrorPnt(0, 0, 0);
gp_Dir mirrorDir(1, 0, 0);
gp_Ax1 mirrorAx(mirrorPnt,mirrorDir);
gp_Trsf mirrorTrsf;
mirrorTrsf.SetMirror(mirrorAx);
           

在這段代碼中,設定原點位置為(0, 0, 0),方向為(1, 0, 0)的gp_Ax1其實有一個很快捷的方式可以用,就是gp::OX():

gp_Ax1 mirrorAx = gp::OX();
           

在設定好了鏡像矩陣後,接下來就運用BRepBuilderAPI_Transform這個類來生成鏡像環:

TopoDS_Shape mirrorShape =BRepBuilderAPI_Transform(wireHalfProfile, mirrorTrsf);
TopoDS_ShapewireOtherHalfProfile = TopoDS::Wire(mirrorShape);
           

兩個半邊環都生成後,那麼現在要做的是将這兩個半邊環組成一個環:

BRepBuilderAPI_MakeWiremkWire2;
mkWire2.Add(wireHalfProfile);
mkWire2.Add(wireOtherHalfProfile);
TopoDS_Wire profile =mkWire2.Wire();
           

那麼到目前為止瓶子底部輪廓已經生成出來了。

2.      建立瓶身

建立瓶身是采用拉伸的方式,将瓶子的底部等比拉伸一定的高度後形成的體即是瓶身了。

那麼首先把瓶子底部輪廓修改成面填充形式,使用BRepBuilderAPI_MakeFace:

TopoDS_FacebottomFace = BRepBuilderAPI_MakeFace(profile);
           

現在運用OCCT提供的拉伸功能對瓶底面進行拉伸,拉伸出瓶身,拉伸類為BRepPrimAPI_MakePrism,它的拉伸規則如下:

【第二回】使用OCCT實作對一個瓶子模組化的總結

它的拉伸隻能沿着一個方向進行等比拉伸。代碼如下:

gp_Vec prismVec(0, 0,height);
TopoDS_Shape prismShape= BRepPrimAPI_MakePrism(bottomFace, prismVec);
           

拉伸出來的瓶身邊都是很鋒利的,現在需要對邊進行圓角化,使用BRepFilletAPI_MakeFillet,需要确定對邊進行圓角化時的圓角半徑,确定為thick/15。BRepFilletAPI_MakeFillet的使用,先為其傳入TopoDS_Shape,然後添加需要進行圓角化的邊和半徑,最後得到結果。這其中對瓶身的TopoDS_Edge邊的周遊可以采用TopExp_Explorer。代碼如下:

BRepOffsetAPI_MakeFilletmkFillet(prismShape);
For (TopExp_ExploreredgeExp(prismShape, TopAbs_EDGE); edgeExp.More(); edgeExp.Next())
{
         const TopoDS_Edge& edge = TopoDS::Edge(edgeExp.Current());
         mkFillet.Add(thick/15,edge);
}
TopoDS_Shape body = mkFillet.Shape();
           

#Q: 這裡有一個問題,在BRepPrimAPI_MakePrism的拉伸規則中Face是拉伸成Solid的,我将經過拉伸之後的通用結構TopoDS_Shape轉換成TopoDS_Solid是可以的,但是在我對其進行圓角之後将其圓角的結果通過結構TopoDS_Shape轉換成TopoDS_Solid時會出現異常,這是為什麼?

3.      建立瓶頸

瓶頸的一個尺寸是半徑為thick/4,高度為height/10,建立瓶頸的一個類是BRepPrimAPI_MakePrism,此類預設建立的瓶頸的位置是在原點建立,我們這個瓶頸的位置應該是在(0, 0, height)。在BRepPrimAPI_MakePrism中要去定圓柱建立的位置需要給它傳遞一個參數gp_Ax2。

gp_Pnt neckPnt(0, 0,height);
gp_Dir neckDir(0, 0,1);
gp_Ax2 neckAx(neckPnt, neckDir);
double neckRadius = thick/4,neckHeight = height/10;
TopoDS_Shape neck = BRepPrimAPI_MakeCylinder(neckAx,neckRadius, neckHeight);
           

現在需要把瓶頸和瓶身粘在一起,那麼這裡就需要用到Boolean Operation,用到其中的交集運算,BRepAlgoAPI_Fuse,代碼:

TopoDS_Shape bottle =BRepAlgoAPI_Fuse(body, neck);
           

在使用BRepAlgoAPI_Fuse進行并集運算時,會去掉body和neck的交集部分。通過渲染線模式可以看到這個結果:

【第二回】使用OCCT實作對一個瓶子模組化的總結

4.      為瓶子建立内膽

現在一個瓶子的雛形已經出來了,現在還需要為這個瓶子建立内膽。同時打開瓶口。打開瓶口則需要找到并去掉瓶子頂部的圓面,打開瓶口并建立内膽這個過程可以使用BRepOffsetAPI_MakeThickSolid來實作,這個類就是為了實作hollowedsolid用的。BRepOffsetAPI_MakeThickSolid需要這些參數:初始化的TopoDS_Shape,需要去掉的面的清單,wall的厚度。那麼首先需要找到瓶子頂部的圓面,在這裡瓶子頂部的圓面的特征有:這個圓面的位置度在(0, 0, height+neckHeight),并且這個面的性質是一個平面而非曲面。可以看出這兩個性質都是屬于幾何資訊,是以這裡需要擷取TopoDS_Face的幾何資訊。首先周遊TopoDS_Shape的工具還是使用TopExp_Explorer,初始化使用TopAbs_FACE辨別,表示周遊其中的面,然後就是從拓撲資訊TopoDS_Face得到幾何資訊的過程,這個過程需要用到類BRep_Tools,使用BRep_Tool::Surface方法。這個方法傳回的是幾何結構面的基類Geom_Surface,在我們這個例子中,需要判斷這個面是曲面還是平面,平面才是我們需要的面,這個時候需要用到它的基類Standard_Transient的功能:資料類型識别,類型強制轉換的功能。代碼如下:

TopTools_ListOfShapefacesRemoved;
for (TopExp_Explorer faceExp(bottle, TopAbs_FACE); faceExp.More(); faceExp.Next())
{
           TopoDS_Faceface = TopoDS::Face(faceExp.Current());
           Handle(Geom_Surface)surface = BRep_Tool::Surface()
           if (surface->DynamicType()== STANDARD_TYPE(Geom_Plane))
           {
                    Handle(Geom_Plane)plane = Handle(Geom_Plane)::DownCast(surface);
                    gp_PntlocPnt = plane->Location();
                    if(locPnt.Z() > height) facesRemoved.Append(face);
           }
}
 
bottle =BRepOffsetAPI_MakeThickSolid(bottle, facesRemoved, -thick/50, 1.3e-3);
           

5.      為瓶口建立螺紋

#TODO(continue)

6.      代碼:

TopoDS_Shape reCreateBottle(const double width, const double thick, const double height)
{
	gp_Pnt pnt1(-width/2, 0, 0), 
			pnt2(-width/2, -thick/4, 0), 
			pnt3(0, -thick/2, 0), 
			pnt4(width/2, -thick/4, 0), 
			pnt5(width/2, 0, 0);

	// create geometry data of segment and arc segment
	Handle(Geom_TrimmedCurve) seg1 = GC_MakeSegment(pnt1, pnt2);
	Handle(Geom_TrimmedCurve) arcSeg = GC_MakeArcOfCircle(pnt2, pnt3, pnt4);
	Handle(Geom_TrimmedCurve) seg2 = GC_MakeSegment(pnt4, pnt5);
	// create topological data with above geometry data
	TopoDS_Edge edge1 = BRepBuilderAPI_MakeEdge(seg1);
	TopoDS_Edge arcEdge = BRepBuilderAPI_MakeEdge(arcSeg);
	TopoDS_Edge edge2 = BRepBuilderAPI_MakeEdge(seg2);

	// make wire composited by above topological data
	TopoDS_Wire wireHalfProfile = BRepBuilderAPI_MakeWire(edge1, arcEdge, edge2);

	// make mirror wire
	gp_Ax1 mirrorAx = gp::OX();
	gp_Trsf mirrorTrsf;
	mirrorTrsf.SetMirror(mirrorAx);
	TopoDS_Shape mirrorShape = BRepBuilderAPI_Transform(wireHalfProfile, mirrorTrsf);
	TopoDS_Wire wireOtherHalfProfile = TopoDS::Wire(mirrorShape);
	// composite two half wire to bottle bottom profile
	BRepBuilderAPI_MakeWire mkWire;
	mkWire.Add(wireHalfProfile);
	mkWire.Add(wireOtherHalfProfile);
	TopoDS_Wire wireProfile = mkWire.Wire();

	// create bottle body
	TopoDS_Face bottomFace = BRepBuilderAPI_MakeFace(wireProfile);
	gp_Vec prismVec(0, 0, height);
	TopoDS_Shape prismShape = BRepPrimAPI_MakePrism(bottomFace, prismVec);
	//----fillet to round edge
	BRepFilletAPI_MakeFillet mkFillet(prismShape);
	for (TopExp_Explorer edgeExp(prismShape, TopAbs_EDGE); edgeExp.More(); edgeExp.Next())
	{
		const TopoDS_Edge& edge = TopoDS::Edge(edgeExp.Current());
		mkFillet.Add(thick/15, edge);;
	}
	TopoDS_Shape body = mkFillet.Shape();

	// create neck
	gp_Pnt neckPnt(0, 0, height);
	gp_Dir neckDir(0, 0, 1);
	gp_Ax2 neckAx(neckPnt, neckDir);
	double neckRadius = thick/4, neckHeight = height/10;
	TopoDS_Shape neck = BRepPrimAPI_MakeCylinder(neckAx, neckRadius, neckHeight);

	// apply boolean operation fuse, union body and neck
	TopoDS_Shape bottle = BRepAlgoAPI_Fuse(body, neck);

	// make thick solid
	TopTools_ListOfShape facesRemoved;
	for (TopExp_Explorer faceExp(bottle, TopAbs_FACE); faceExp.More(); faceExp.Next())
	{
		TopoDS_Face face = TopoDS::Face(faceExp.Current());
		Handle(Geom_Surface) surface = BRep_Tool::Surface(face);
		if (surface->DynamicType() == STANDARD_TYPE(Geom_Plane))
		{
			Handle(Geom_Plane) plane = Handle(Geom_Plane)::DownCast(surface);
			gp_Pnt locPnt = plane->Location();
			if (locPnt.Z() > height)
				facesRemoved.Append(face);
		}
	}
	bottle = BRepOffsetAPI_MakeThickSolid(bottle, facesRemoved, -thick/50, 1.e-3);

	return bottle;
}
           

繼續閱讀