之前使用一篇博文记录了如何利用vtk的轮廓提取技术生成一个函数模型,里面提到使用vtk生成的模型数据,需要转换为OCC数据类型用于CAD建模使用。
查询了许多资料,IVTK是可以做到将OCC的数据直接转换为VTK的数据,但是似乎没有反向转换的途径,最后只能自己动手,丰衣足食。
整个数据转换流程大概是这样。
获取vtkPolyData中的点集与面集
vtkDataSet 提供了GetPoint()和GetCell()两个函数获取点与单元,只要传入单元的ID即可。
生成TopoDS_ComPound
这里需要用到BRep_Builder,这个类用于构建一些OCC的拓扑数据结构。
void MakeWire (TopoDS_Wire &W) const; void MakeShell (TopoDS_Shell &S) const; void MakeSolid (TopoDS_Solid &S) const; void MakeCompSolid (TopoDS_CompSolid &C) const; void MakeCompound (TopoDS_Compound &C) const ; void Add (TopoDS_Shape &S, const TopoDS_Shape &C) const; void Remove (TopoDS_Shape &S, const TopoDS_Shape &C) const;
为此提供了以上函数,MakeXXX函数需要传入一个数据对象作为构建对象,然后Add和Remove函数则是为数据对象中添加和删除数据。
需要注意的是,当Make的类型不同的时候,Add参数中的数据类型也有着严格的限制。
- 任何 shape 类型都可以 添加到 Compound。
- 只有solid类型可以添加到compSolid类型里。
- 只有shell、edge、vertex可以添加到solid。
- 只有face可以添加到shell中。
- 只有wire、vertex可以添加到face中
- 只有edge可以添加到wire中
- 只有vertex可以添加到edge中
- 任何数据不能添加到vertex中.
在帮助文档上看到这些信息时,我的第一个想法是通过添加shell和vertex直接生成solid,但是尝试之后发现shell也要使用这个方法生成,再次尝试后发现这样生成的solid不能正常的参加布尔运算(也许是过程中出现纰漏),我怀疑是在添加数据时,生成shell的面之间没有关联,vtkPolyData的面之间会共用一个点集,通过这些点的ID来访问,OCC中的数据对象似乎不是这样,Face对象所用的到点都是独立的三个Double类型,众所周知Double类型不能判等,所以我推断添加到shell中的面彼此对立,没有共同点和共同边,所以生成的solid会有这样的问题。
于是有了目前这个思路,先使用Make_ComPound生成一个Compound对象,然后使用缝合算法,解决上面说道的面与面之间相互独立的问题。
TopoDS_Compound aComp; BRep_Builder BuildTool; BuildTool.MakeCompound(aComp); double p[3]; for (int i = 0; i < faceCount; i++) { auto idlist = polydata->GetCell(i)->GetPointIds(); pointData->GetPoint(idlist->GetId(0), p); p1.SetCoord(p[0], p[1], p[2]); pointData->GetPoint(idlist->GetId(1), p); p2.SetCoord(p[0], p[1], p[2]); pointData->GetPoint(idlist->GetId(2), p); p3.SetCoord(p[0], p[1], p[2]); if ((!(p1.IsEqual(p2, 0.0))) && (!(p1.IsEqual(p3, 0.0)))) { Vertex1 = BRepBuilderAPI_MakeVertex(p1); Vertex2 = BRepBuilderAPI_MakeVertex(p2); Vertex3 = BRepBuilderAPI_MakeVertex(p3); newWire = BRepBuilderAPI_MakePolygon(Vertex1, Vertex2, Vertex3, Standard_True); if (!newWire.IsNull()) { newFace = BRepBuilderAPI_MakeFace(newWire); if (!newFace.IsNull()) BuildTool.Add(aComp, newFace); } } }
以上代码就是生成Compound的全过程。
缝合三角面
BRepBuilderAPI_Sewingshi是OCC提供的一个面缝合工具,他可以将相邻的面缝合在一起,建立面与面之间的关系。
使用步骤:
- 创建一个BRepBuilderAPI_Sewingshi 对象。
- 调用init()初始化设置,参数分别为公差(默认1.E-06)、面分析(默认开启),缝合操作(默认开启),是否切割(默认开启),所有参数均有默认值,可以直接调用。
- add() 或者load()要缝合的数据,暂时不清楚两者之间的区别。
- 调用Perfom()执行缝合。
- 输出对象
BRepBuilderAPI_Sewing aSewingTool; aSewingTool.Init(); aSewingTool.Load(aComp); aSewingTool.Perform(); auto shape = aSewingTool.SewedShape();
生成solid
TopExp_Explorer 可以设定一个搜索条件,然后遍历一个shape中所有满足条件的topo对象。
TopExp_Explorer (const TopoDS_Shape &S, const TopAbs_ShapeEnum ToFind, const TopAbs_ShapeEnum ToAvoid=TopAbs_SHAPE)
这是它的构造函数,可以传入两个参数,第一个是查找类型,第二个是防止查找到的类型。
TopAbs_COMPOUND
TopAbs_COMPSOLID
TopAbs_SOLID
TopAbs_SHELL
TopAbs_FACE
TopAbs_WIRE
TopAbs_EDGE
TopAbs_VERTEX
TopAbs_SHAPE
然后是BRepBuilderAPI_MakeSolid 这个是一个生成solid类型的工具,它需要传入一个shell类型数据,所以只用使用TopExp_Explorer 找到shape中的shell数据,就能构造出solid了。
TopoDS_Solid FS::FunctionShape::shapeToSolid(TopoDS_Shape shape) { TopExp_Explorer anExp(shape, TopAbs_SHELL); BRepBuilderAPI_MakeSolid mkSolid; int count = 0; for (; anExp.More(); anExp.Next()) { ++count; mkSolid.Add(TopoDS::Shell(anExp.Current())); } TopoDS_Solid solid = mkSolid.Solid(); BRepLib::OrientClosedSolid(solid); return solid; }
BRepLib::OrientClosedSolid() 是为了给solid计算实体的方向,如果solid不连续或者不封闭都会返回false。
总结
转换的整个过程以上,个人觉得确实十分麻烦,而且也很消耗性能,我尝试过缩减其中的步骤来节省转换的时间,不过都多多少少会有一些问题。
虽然繁琐,运行速度慢,不过还在勉强能用,如果有更好的解决方案,欢迎告知。
文章评论