从vtkPolyData中生成一个open cascade 中的TopoDS_Solid(实体对象)

2022年3月22日 891点热度 1人点赞 0条评论

之前使用一篇博文记录了如何利用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参数中的数据类型也有着严格的限制。

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提供的一个面缝合工具,他可以将相邻的面缝合在一起,建立面与面之间的关系。

使用步骤:

  1. 创建一个BRepBuilderAPI_Sewingshi 对象。
  2. 调用init()初始化设置,参数分别为公差(默认1.E-06)、面分析(默认开启),缝合操作(默认开启),是否切割(默认开启),所有参数均有默认值,可以直接调用。
  3. add() 或者load()要缝合的数据,暂时不清楚两者之间的区别。
  4. 调用Perfom()执行缝合。
  5. 输出对象
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_ShapeEnum

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。

总结

转换的整个过程以上,个人觉得确实十分麻烦,而且也很消耗性能,我尝试过缩减其中的步骤来节省转换的时间,不过都多多少少会有一些问题。

虽然繁琐,运行速度慢,不过还在勉强能用,如果有更好的解决方案,欢迎告知。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论