Reputation: 31
I have been using OpenCASCADE for the past months. My applications is basically receiving commands to create different shapes, creates the shapes, triangulate them and returns the geometry.
Boolean operations works well with basic primitives like cubes, spheres and everything. My problem is, when I use boolean operations on shapes resulting from extrudes or revolutions, things turns out different.
Some faces are missing when I'm doing a difference (cut algo) and the shape is null if I try to do a union (fuse algo) of the two shapes.
Here is the code to create a basic revolution and do a cut operation on it.
// Make revolution shape
gp_Pnt a = gp_Pnt(0.0f, 10.0f, 0.0f);
gp_Pnt b = gp_Pnt(0.0f, 20.0f, 0.0f);
gp_Pnt c = gp_Pnt(10.0f, 20.0f, 0.0f);
gp_Pnt d = gp_Pnt(10.0f, 10.0f, 0.0f);
// Create wire
BRepBuilderAPI_MakeWire wireBuilder;
wireBuilder.Add(BRepBuilderAPI_MakeEdge(a, b).Edge());
wireBuilder.Add(BRepBuilderAPI_MakeEdge(b, c).Edge());
wireBuilder.Add(BRepBuilderAPI_MakeEdge(c, d).Edge());
wireBuilder.Add(BRepBuilderAPI_MakeEdge(d, a).Edge());
Log(wireBuilder.Wire(), "Wire", wireBuilder.Error());
// Make revolved shape
const gp_Ax1 axis(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0));
shape = BRepPrimAPI_MakeRevol(wireBuilder.Wire(), axis).Shape();
Log(shape, "Revolution shape", 0);
// Crete box for difference
const gp_Pnt origin = gp_Pnt(-20 / 2.0f, -10 / 2.0f, -20 / 2.0f);
TopoDS_Shape box = BRepPrimAPI_MakeBox(origin, 20, 10, 20);
gp_Trsf translation;
translation.SetTranslation(gp_Vec(0, 0, 15));
BRepBuilderAPI_Transform transformBox(box, translation);
// Perform difference
shape = BRepAlgoAPI_Cut(shape, transformBox.Shape()).Shape();
The Log()
method :
ExploreShape(const TopoDS_Shape &shape, const int type, const std::string &typeName) const
{
std::cout << typeName << ":" << std::endl;
for (TopExp_Explorer explorer(shape, (TopAbs_ShapeEnum) type); explorer.More(); explorer.Next())
{
explorer.Current().DumpJson(std::cout);
std::cout << std::endl;
}
}
Log(const TopoDS_Shape &shape, const std::string &variableName, const int builderStatus) const
{
std::cout << "--- " << variableName << " | Builder status: " << builderStatus << " ---" << std::endl;
ExploreShape(shape, TopAbs_SHAPE, "shape");
ExploreShape(shape, TopAbs_FACE, "face");
ExploreShape(shape, TopAbs_WIRE, "wire");
ExploreShape(shape, TopAbs_EDGE, "edge");
//ExploreShape(shape, TopAbs_VERTEX, "vertex");
}
Here is my result from the above code
We can clearly see in the pictures above that the faces resulting of the boolean operations are missing.
Face logging (the message would be too long with the whole log) :
[INFO] --- CsgConstructor::CreateGeometry | Builder status: -1 ---
shape:
face:
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fffa0018950", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_Plane", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [0, 0, 0], "Direction": [1, 0, 0], "XDirection": [0, -1, -0], "YDirection": [0, -0, 1]}}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fff84005500", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_CylindricalSurface", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [10, 0, 0], "Direction": [-1, -0, -0], "XDirection": [0, 1, 0], "YDirection": [-0, -0, 1]}, "radius": 10}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fff8c005b90", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_CylindricalSurface", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [0, 0, 0], "Direction": [1, 0, 0], "XDirection": [-0, 1, 0], "YDirection": [0, -0, 1]}, "radius": 20}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fff8c007cb0", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_CylindricalSurface", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [0, 0, 0], "Direction": [1, 0, 0], "XDirection": [-0, 1, 0], "YDirection": [0, -0, 1]}, "radius": 20}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fff84005220", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_CylindricalSurface", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [10, 0, 0], "Direction": [-1, -0, -0], "XDirection": [0, 1, 0], "YDirection": [-0, -0, 1]}, "radius": 10}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
"className": "TopoDS_Shape""TShape": {"className": "BRep_TFace", "className": "TopoDS_TShape", "this": "0xx7fff5c00a090", "ShapeType": 4, "NbChildren": 1, "Flags": 10, "Free": 0, "Free": 0, "Locked": 0, "Modified": 1, "Checked": 0, "Orientable": 1, "Closed": 0, "Infinite": 0, "Convex": 0, "Surface": {"className": "Geom_Plane", "className": "Geom_ElementarySurface", "className": "Geom_Surface", "className": "Geom_Geometry", "pos": {"Location": [10, 0, 0], "Direction": [1, 0, 0], "XDirection": [-0, 1, 0], "YDirection": [0, -0, 1]}}, "Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}, "Tolerance": 1e-07, "NaturalRestriction": 0}"Location": {"className": "TopLoc_Location", "Transformation": {"Location": [0, 0, 0], "Matrix": [1, 0, 0, 0, 1, 0, 0, 0, 1], "shape": 0, "scale": 1}, "IsIdentity": 1}"Orient": 0
I first thought that 6 faces was ok since the result seems to have 6 faces. However, I did some tests by displaying the faces one by one and none of them corresponds to the missing faces. Moreover, some of them do be really strange : what is the purpose of face 2 since face 3 is the only one that will be left at the end ?
I do not use any opencascade viewers display the shape. I compute the triangles of the shape using this algo :
IMeshTools_Parameters parameters = IMeshTools_Parameters();
parameters.MeshAlgo = IMeshTools_MeshAlgoType_Delabella;
parameters.Deflection = triangulationDeflection;
parameters.Relative = false;
parameters.Angle = triangulationAngle;
parameters.MinSize = 1;
parameters.InParallel = true;
BRepMesh_IncrementalMesh(shape, parameters).Perform();
BRepLib::EnsureNormalConsistency(shape, 0.001, true);
std::vector<gp_Pnt> vertices;
// Iterate over the faces of the shape
for (TopExp_Explorer ex(shape, TopAbs_FACE); ex.More(); ex.Next())
{
TopoDS_Face face = TopoDS::Face(ex.Current());
const bool isReversed = face.Orientation() == TopAbs_REVERSED;
LOG_INFO("Face orientation: " << face.Orientation());
TopLoc_Location loc;
Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation(face, loc);
if (triangulation.IsNull()) continue;
triangulation->ComputeNormals();
gp_Trsf transform = face.Location().Transformation();
gp_Trsf normalTransform = transform;
if (isReversed)
{
gp_Trsf normalInverterTransform = gp_Trsf();
normalInverterTransform.SetScaleFactor(-1);
normalTransform = transform * normalInverterTransform;
}
// Iterate over the triangles
for (Standard_Integer i = 1; i <= triangulation->NbTriangles(); i++)
{
Standard_Integer a, b, c;
triangulation->Triangle(i).Get(a, b, c);
if (isReversed) std::swap(a, c);
// Add the indices to the geometry
Face3Msg *triangle = geoMsg->add_triangles();
triangle->set_a(RegisterVertex(vertices, triangulation->Node(a).Transformed(transform)));
triangle->set_b(RegisterVertex(vertices, triangulation->Node(b).Transformed(transform)));
triangle->set_c(RegisterVertex(vertices, triangulation->Node(c).Transformed(transform)));
if (triangulation->HasNormals())
{
RegisterNormal(triangle, triangulation->Normal(a).Transformed(normalTransform));
RegisterNormal(triangle, triangulation->Normal(b).Transformed(normalTransform));
RegisterNormal(triangle, triangulation->Normal(c).Transformed(normalTransform));
}
}
}
for (auto pnt: vertices)
{
// Add the vertex to the geometry
Vector3fMsg *vertex = geoMsg->add_vertices();
vertex->set_x(static_cast<float>(pnt.X()));
vertex->set_y(static_cast<float>(pnt.Y()));
vertex->set_z(static_cast<float>(pnt.Z()));
}
After that, the geometry is sent into a another service of the ecosystem, which will transform the geometry into a GLTF file and display it in a web GLTF viewer. That part seemed to work perfectly fine until now, I don"t think this is where the problem comes from.
Has anyone ever encountered problems like this with boolean operation on extruded or revolved shapes ?
Upvotes: 0
Views: 134