I want to make the face of half a cylinder, using the boundary edges (two vertical lines and two 180° arcs). I know there are easier ways to do this, but my real problem is much more complex, and the edges I have are mostly splines. So, I tried to make a very simple example in hope someone might help me.
With my real input data, I do not know the order and orientation of the boundary edges. All I have is "cylindrical face" and "bucket of edges, that form a closed loop". So, if my orientation is bad, how can I automatically fix that?
Here's my example code:
// make an ARC from 'start' to 'end', counter-clockwise around 'center'.
TopoDS_Edge mkArc(gp_Pnt start, gp_Pnt center, gp_Pnt end, double normalZ) {
gp_Circ geometricCircle = GC_MakeCircle(center
, gp_Dir(0, 0, normalZ)
, center.Distance(start)
).Value()->Circ();
return BRepBuilderAPI_MakeEdge(geometricCircle, start, end);
}
// Make half-cylinder face by using boundary edges
TopoDS_Face MakeClinderFaceTest() {
// ^Z
// _
// ,´ `.
// a c b
// | _ |
// |,´ `.|
// A C B -->X
// top nodes
gp_Pnt a = gp_Pnt(-1, 0, 0);
gp_Pnt b = gp_Pnt( 1, 0, 0);
gp_Pnt c = gp_Pnt( 0, 0, 0);
// bottom nodes
gp_Pnt A = gp_Pnt(-1, 0, -1);
gp_Pnt B = gp_Pnt( 1, 0, -1);
gp_Pnt C = gp_Pnt( 0, 0, -1);
// boundary wire
std::list<TopoDS_Edge> edges;
if (0) { // 1/0 to reverse the order and direction of edges
edges.push_back(mkArc(a, c, b, -1)); // top arc
edges.push_back(BRepBuilderAPI_MakeEdge(b, B)); // right line
edges.push_back(mkArc(B, C, A, 1)); // bottom arc
edges.push_back(BRepBuilderAPI_MakeEdge(A, a)); // left line
} else {
edges.push_back(mkArc(b, c, a, 1));
edges.push_back(BRepBuilderAPI_MakeEdge(a, A));
edges.push_back(mkArc(A, C, B, -1));
edges.push_back(BRepBuilderAPI_MakeEdge(B, b));
}
BRepBuilderAPI_MakeWire wire;
for (auto& e : edges) {
wire.Add(e);
}
auto cylinder = gp_Cylinder( gp_Ax2( C, gp_Dir(0, 0, 1) ), C.Distance(A) /* =R */ );
#if 0
// surface geometry: infinite length cylinder
BRepBuilderAPI_MakeFace cylface(cylinder, wire);
#else
// cylindrical face with limits in V direction.
TopoDS_Face cylinder_face = BRepBuilderAPI_MakeFace(cylinder, 0, 2 * M_PI, 0, 1.0).Face();
// Limit cylinder by wired edges
BRepBuilderAPI_MakeFace cylface(cylinder_face, wire);
#endif
return cylface;
}
It is always better performing direct modeling - e.g. constructing vertices, edges, wires, faces in reliable way defining a valid topology.
But considering the question:
So, if my orientation is bad, how can I automatically fix that?
The shape healing services are provided by ShapeFix package in Open CASCADE Technology. It's main purpose is solving topology issues on shapes imported from external CAD systems (applying different criteria of validity or just writing an invalid shape into STEP / IGES file).
These tools might be also used for making a 'lazy' building algorithm or for handling user input. Beware, that tools provides a lot of 'fixes' - e.g. algorithms solving particular kind of issue. Enabling all of them is not a good idea as it will dramatically affect performance and result might be unexpected.
In your particular case, you are interested in fixing wire orientation. This can be achieved with help of ShapeFix_Face like this (if I understand your question correctly):
// cylindrical face with limits in V direction.
TopoDS_Face cylinder_face = BRepBuilderAPI_MakeFace(cylinder, 0, 2 * M_PI, 0, 1.0).Face();
Handle(Geom_Surface) aSurf = BRep_Tool::Surface (cylinder_face);
// Limit cylinder by wired edges
//BRepBuilderAPI_MakeFace cylface(cylinder_face, wire);
BRepBuilderAPI_MakeFace cylface(aSurf, wire);
ShapeFix_Face aFaceFixer;
aFaceFixer.FixWireMode() = 1;
aFaceFixer.FixOrientationMode() = 1;
//aFaceFixer.FixSplitFaceMode() = 1;
Handle(ShapeFix_Wire) aWireFixer = aFaceFixer.FixWireTool();
aWireFixer->FixConnectedMode() = 1;
aWireFixer->ClosedWireMode() = Standard_True;
Handle(ShapeBuild_ReShape) aContext = new ShapeBuild_ReShape();
aFaceFixer.SetContext (aContext);
aFaceFixer.Init (cylface);
aFaceFixer.Perform();
TopoDS_Shape aFixResult = aFaceFixer.Result();
if (!aFixResult.IsNull()) { return TopoDS::Face (aFixResult); }