Hi,
I have done a "generic" code to address any kind of surface I give to my program as Geom_Surface and to tessellate it (using its boundaries first to create a TopoDS_Face). Most of the surfaces seem to be tessellated a good way, but some of them are doing all the process (from Geom_Surface to TopoDS_Face) but triangulation fails (the result is empy - isNull() is true). It is difficult for me to know where I should look to solve that issue since no error message is given by Triangulation. Have you got any idea ? (modifying the parameters of tessellation ?)
Sometimes it fails while creating the projected boundary : BuildCurve3D raises an access violation on NULL address but everything goes well till BRepBuilderAPI_MakeEdge. What kind of test should I do on BRepBuilderAPI_MakeEdge to check Edge validity before asking for BuildCurve3D ? Am I doing the process the right way ?
Here is my source code to process the Geom_Surface with its boundaries (array of Geom_Curve) into vertices & faces. It is a bit "raw" but should illustrate well the full process.
bool CBoundary::getWire(Handle(Geom_Surface) &p_surf,Handle(TopTools_HSequenceOfShape) & p_wire, bool p_projection)
{
if(m_curves.size() == 0) // m_curves is a vector of curves describing a boundary (curves have "CCurve*" type : a CCurve can be a BSpline, a segment, a polyline, an ellipse, group of CCurve, ...)
{
printf("Boundary is not valid\n");
return false;
}
// Get all curves that compound the boundary and create a vector of edges
TopoDS_Face f;
if(p_projection) // we only need "f" if we have to project the curves on surface "m_surf" which is a Geom_Surface
{
try{
BRep_Builder aBuilder;
aBuilder.MakeFace (f, m_surf, Precision::Confusion() );
}catch(Standard_Failure sf)
{
printf("Error while creating TopoDS_Face from surface\n");
return false;
}
}
Handle(TopTools_HSequenceOfShape) Edges = new TopTools_HSequenceOfShape();
for(unsigned int i = 0; i < m_curves.size(); i++)
{
CCurve * curve = dynamic_cast<CCurve*>(m_curves[i]);
if(curve)
{
Handle(Geom_Curve) c = curve->getCurve();
if(c.IsNull())
continue;
// DO we need to project the curves on the surface before (for non plane surfaces) ?
if(p_projection)
{
Handle(Geom2d_Curve) c2 = NULL;
// project the curve on surface (get a Geom2D_Curve as result)
try{
Standard_Real tol = 1.0;
BOPTools_AlgoTools2D::MakePCurveOnFace(f, c, c->FirstParameter(), c->LastParameter(), c2,tol);
}catch(Standard_Failure sf)
{
printf("Error while projecting boundary's curve on surface\n");
continue;
}
if(c2.IsNull())
{
printf("Error with projected curve on surface\n");
continue;
}
// build the edges from the Geom2D_Curve (result of the projection of the 3D curve on the surface)
BRepBuilderAPI_MakeEdge me(c2, p_surf);
try{
TopoDS_Edge e = me.Edge();
if(!e.IsNull())
{
if(BRepLib::BuildCurve3d(e))
Edges->Append(e);
}
else
{
printf("Error while building 3D curve (Edges) from projected boundary's curve\n");
continue;
}
}catch(Standard_Failure sf)
{
printf("Error while creating edge from projected boundary's curve\n");
continue;
}
}
else
{
// use the curve as if, it is mostly used for planes
BRepBuilderAPI_MakeEdge me(c, c->FirstParameter(),c->LastParameter());
try{
TopoDS_Edge e = me.Edge();
if(BRepLib::BuildCurve3d(e))
Edges->Append(e);
else
{
printf("Error while building 3D curve (Edges) from boundary's curve\n");
}
}catch(Standard_Failure sf)
{
printf("Error while creating edge from boundary's curve\n");
continue;
}
}
}
}
// weld the edges to have only 1 wire for the boundary
Handle(TopTools_HSequenceOfShape) Wires = new TopTools_HSequenceOfShape();
// Connect all the edges to create a single wire
try{
ShapeAnalysis_FreeBounds::ConnectEdgesToWires(Edges, WELD_TOLERANCE, false, Wires); // if shared = true , the connection is done only if the vertices are the same
}catch(Standard_Failure sf)
{
printf("Error while creating wires from Edges\n");
return false;
}
if(Wires.IsNull() || Wires->Length()==0)
return false;
return true;
}
bool CSurfaceWithBoundaries::getFace(TopoDS_Face & p_face)
{
if(!m_surf)
return false;
// Get inner boundaries in a list
Handle(TopTools_HSequenceOfShape) outerBoundary;
CPlane * p = dynamic_cast<CPlane*>(this);
if(!m_boundaries[0]->getWire( m_surf , outerBoundary , p?false:true)) // m_boundaries is a vector of boundaries (the first one is the outer one, all the others are inner)
{
printf("No boundary for the surface \n");
return false;
}
BRepBuilderAPI_MakeFace mf(m_surf, TopoDS::Wire(outerBoundary->Value(1)) );
for(int k = 1; k < outerBoundary->Length(); k++)
mf.Add( TopoDS::Wire(outerBoundary->Value(k+1)) ); // Not sure about that, the wires shouldn't be added while constructing the face (BRepBuilderAPI_MakeFace
Handle(TopTools_HSequenceOfShape) innerBoundary;
for(unsigned int i = 1; i < m_boundaries.size(); i++)
{
if(m_boundaries[i]->getWire(m_surf, innerBoundary))
{
for(int k = 0; k < innerBoundary->Length(); k++)
mf.Add( TopoDS::Wire(innerBoundary->Value(k+1)) );
}
}
try{
p_face = mf.Face();
}catch(Standard_Failure sf)
{
printf("Error while constructing face from Geom_Surface & boundaries\n");
return false;
}
return true;
}
bool CSurfaceWithBoundaries::getMesh(double p_lineDeflection, bool p_isRelative, double p_angleDeflection, bool p_isInParallel, bool p_adaptiveMin)
{
if(p_vertices)
{
free(p_vertices);
p_vertices = NULL;
}
if(p_faces)
{
free(p_faces);
p_faces = NULL;
}
p_nbVertices = p_nbFaces = 0;
TopoDS_Face f;
if(!getFace(f))
return false;
BRepMesh_IncrementalMesh im(f,p_lineDeflection, p_isRelative,p_angleDeflection,p_isInParallel,p_adaptiveMin);
im.Perform();
if(!im.IsDone())
{
printf("No way to create a mesh from the surface [%s]\n",m_name.c_str());
return false;
}
TopLoc_Location faceLocation;
Handle(Poly_Triangulation) tri;
try{
tri = BRep_Tool::Triangulation(f, faceLocation);
}catch(Standard_Failure sf)
{
printf("No triangulation available [%s]\n",m_name.c_str());
}
bool reversed = ( f.Orientation() == TopAbs_Orientation::TopAbs_REVERSED );
if (!tri.IsNull())
{
Poly_Array1OfTriangle triangles = tri->Triangles();
TColgp_Array1OfPnt nodes = tri->Nodes();
// Store the processed mesh there
}
else
{
printf("No triangulation available for this surface [%s]\n", this->m_name.c_str());
return false;
}
return true;
}