Surface modeling. Part6

by - 09:33

(continued...)

Plating
This is one of the advanced techniques of surface modeling that Open CASCADE offers. Other names for it are hole filling, or constrained filling. ACIS calls it covering.

It involves creation of a surface that meets several constraints:
- point constraints – the surface must pass through given points;
- point constraints and tangency, and (optionally) curvature – the same as above + extra Gn requirements with respect to another surface;
- curve constrains – the surface must pass through given curves;
- curve with tangency constraint – the same + extra Gn requirements.

The latter type of constraint is used to produce class A surfaces, G1 tangent to adjacent surfaces. This is important, for instance, in auto industry when designing nice-looking car bodies, to ensure correct smooth refraction of light to avoid sharp edges.

Constraints can be mixed (e.g. 3 curve constraints, 1 curve with tangency constraint, 2 point constraints).

The main algorithm to construct plate surface is GeomPlate_BuildPlateSurface. I use it in CAD Exchanger to translate vertex_blend ACIS surfaces as well as net surfaces. The former is supposed to result from vertex blending and producing a surface G1-continous to neighbours. The image below shows one:



Net surface in ACIS is defined through two sets of curves going in approximately perpendicular directions and forming a grid-like structure. So the resulting surface is supposed to cover that grid as if a roof covered roof-timbers. The image below illustrates this:



As usual, here is an excerpt from the CAD Exchanger code:

/*! The objects in \a theBoundaries must be of the type Adaptor3d_HCurveOnSurface or
GeomAdaptor_HCurve indicating type of a constraint. Otherwise an exception
Standard_TypeMismatch is thrown.

If the \a theBoundaries list is empty then Standard_ConstructionError is thrown.

If the algorithm fails returns a null surface.
*/
Handle(Geom_Surface) ACISAlgo::MakeSurface (const TColStd_ListOfTransient& theBoundaries,
const Standard_Real theTol,
const Standard_Integer theNbPnts,
const Standard_Integer theNbIter,
const Standard_Integer theMaxDeg)
{
//constants for algorithm
const Standard_Integer aNbIter = theNbIter; //number of algorithm iterations
const Standard_Integer aNbPnts = theNbPnts; //sample points per each constraint
const Standard_Integer aDeg = 3; //requested surface degree ?
const Standard_Integer aMaxDeg = theMaxDeg;
const Standard_Integer aMaxSeg = 10000;
const Standard_Real aTol3d = 1.e-04;
const Standard_Real aTol2d = 1.e-05;
const Standard_Real anAngTol = 1.e-02; //angular
const Standard_Real aCurvTol = 1.e-01; //curvature

Handle(Geom_Surface) aRes;
GeomPlate_BuildPlateSurface aPlateBuilder (aDeg, aNbPnts, aNbIter, aTol2d, aTol3d,
anAngTol, aCurvTol);

TColStd_ListIteratorOfListOfTransient anIt (theBoundaries);
if (anIt.More()) {
int i = 1;
for (; anIt.More(); anIt.Next(), i++) {
const Handle(Standard_Transient)& aCur = anIt.Value();
if (aCur.IsNull()) {
assert (0);
Standard_ConstructionError::Raise ("ACISAlgo::MakeSurface()");
} else if (aCur->IsKind (STANDARD_TYPE (Adaptor3d_HCurveOnSurface))) {
//G1 constraint
const Handle(Adaptor3d_HCurveOnSurface)& aHCOS =
Handle(Adaptor3d_HCurveOnSurface)::DownCast (aCur);
Handle (GeomPlate_CurveConstraint) aConst =
new GeomPlate_CurveConstraint (aHCOS, 1 /*GeomAbs_G1*/,
aNbPnts, aTol3d, anAngTol, aCurvTol);
aPlateBuilder.Add (aConst);
} else if (aCur->IsKind (STANDARD_TYPE (GeomAdaptor_HCurve))) {
//G0 constraint
const Handle(GeomAdaptor_HCurve)& aHC =
Handle(GeomAdaptor_HCurve)::DownCast (aCur);
Handle (GeomPlate_CurveConstraint) aConst =
new GeomPlate_CurveConstraint (aHC, 0 /*GeomAbs_G0*/, aNbPnts, aTol3d);
aPlateBuilder.Add (aConst);
} else {
Standard_TypeMismatch::Raise ("ACISAlgo::MakeSurface()");
}
}
} else {
Standard_ConstructionError::Raise ("ACISAlgo::MakeSurface()");
}

//construct
aPlateBuilder.Perform();

if (!aPlateBuilder.IsDone()) {
return aRes;
}

const Handle(GeomPlate_Surface)& aPlate = aPlateBuilder.Surface();
//approximation (see BRepFill_Filling - when no initial surface was given)
Standard_Real aDMax = aPlateBuilder.G0Error();
TColgp_SequenceOfXY aS2d;
TColgp_SequenceOfXYZ aS3d;
aPlateBuilder.Disc2dContour (4, aS2d);
aPlateBuilder.Disc3dContour (4, 0, aS3d);
Standard_Real aMax = Max (aTol3d, 10. * aDMax);
GeomPlate_PlateG0Criterion aCriterion (aS2d, aS3d, aMax);
{
//data races in AdvApp2Var used by GeomApprox_Surface, use global mutex
Standard_Mutex::Sentry aSentry (theBSMutex);
GeomPlate_MakeApprox aMakeApprox (aPlate, aCriterion, aTol3d, aMaxSeg, aMaxDeg);
aRes = aMakeApprox.Surface();
}
return aRes;
}


The code above deals with curve or curve-with-tangency constraints only but you could add point constraints in the same way.

The algorithm starts with creation of initial approximation which you can either specify or it will create a plane otherwise. I have not come across a need of pre-specifying an initial surface but perhaps that would give some hints to the algorithm in complex cases. If there is anyone with such experience, it would be interesting to hear.

To be continued...

P.S. Writing this post in a hotel on the US west coast, Hillsboro, Oregon. The nature here is the greatest I have ever seen. Always enjoying when returning to Oregon...

You May Also Like

18 comments

  1. Hi Roman,

    Would you be able to say something on the relation between BRepFill_Filling and Geom_Plate? The two seem related but I do not know how exactly. Thanks!

    ReplyDelete
  2. Hi Jelle,
    As usual there are two levels - geometry and topology. BRepFill_Filling works on the latter and uses Geom_Plate underneath.

    ReplyDelete
  3. I see, though I do not understand why this seemingly duplication?
    Also the renaming of the concepts ( one would expect BRepFill_Plate, right? ). Anyways. More interesting is the following. The surfacing operation described in your post is called NetworkSrf in Rhino. So, I took a bunch of curves from Rhino, ran GeomPlate_BuildPlateSurface. All seems good, except that Rhino trims the surface that lies outside of the bounding edges.
    See the results from PythonOCC and Rhino

    ReplyDelete
  4. Have you noticed that importing a step file in cm units isn't done correctly? OCC assumes it to be mm. This seems to be a common problem with CAD software but it's still a pain in the backside.

    Also, can you point to the commands to output a brep please? I just can't find it among the masses of files.

    ReplyDelete
  5. Scratch that question, I found it:
    BRepTools::Write(...

    ReplyDelete
  6. (Was heading back home from US, hence delay).

    @Jelle:
    First, it's not really a duplication. GeomPlate is a common underlying engine which produces the surface (from geometrical constraints).
    BRepFill provides additional API which accepts edges (and optionally faces) and creates a *face* bounded by those edges. Compare the following two pictures - Surface and Face, which correspond to the face from the original post. The former results from using GeomPlate, the latter - from BRepOffsetAPI_MakeFilling (internally use BRepFill_Filling). Thus, BRepFill adds extra job by creating a real face not just an underlying surface. In CAD Exchanger, for instance, I only need a surface, as the face boundaries are not its constraints. In some algorithms, working at BRep level can be more convenient to free developer from extra need to reconstruct all face boundaries.

    Given this above, the difference from Rhino should not be an issue, right ? GeomPlate algorithm itself is supposed to only satisfy constraint requirements, they do not necessarily become natural surface boundaries. The strong impact, I believe, has the initial approximation surface which can be fed into the algorithm (plane if none is provided). And let's keep in mind that a surface only have 4 natural boundaries. I'm curious what will be Rhino behavior if you specify 5 or more curve constrains which you expect to be boundaries. Can you try that ?
    Anyway, boundaries in Open CASCADE come for face, not surface. That's why what you are really interested in eventually, is a face.

    As for naming consistency, well, this is not the strongest part of Open CASCADE ;-). Too many components, too many owners, lack of synchronization, etc. It's always a challenge to stay consistent in a large development team (be it Open CASCADE or any other) and it takes extra efforts to preserve it long term.

    Hope this helps understand this stuff a bit better ;-)

    ReplyDelete
  7. @jgdes: Can you provide an example of what you believe is an incorrect behavior ?
    I've just imported a step file with cm and then edited it to be mm and imported again. As expected, the shape has 1/10 of length dimension.

    ReplyDelete
  8. I'm happy it doesn't affect you. I'm creating a netgen mesh from a step file and while the step file is in cm the mesh vertices are in mm so i need to rescale. The step file was internally converted to a brep. As far as i can see netgen ignores units completely. Here is the code that gets the step file

    /* Special STEP File load function including the ability to extract individual surface colours via the extended OpenCascade XDE and XCAF Feature set. */
    OCCGeometry * LoadOCC_STEP (const char * filename)
    {
    OCCGeometry * occgeo;
    occgeo = new OCCGeometry;

    // Initiate a dummy XCAF Application to handle the STEP XCAF Document
    static Handle_XCAFApp_Application dummy_app = XCAFApp_Application::GetApplication();

    // Create an XCAF Document to contain the STEP file itself
    Handle_TDocStd_Document step_doc;

    // Check if a STEP File is already open under this handle, if so, close it to prevent
    // Segmentation Faults when trying to create a new document
    if(dummy_app->NbDocuments() > 0)
    {
    dummy_app->GetDocument(1,step_doc);
    dummy_app->Close(step_doc);
    }
    dummy_app->NewDocument ("STEP-XCAF",step_doc);

    STEPCAFControl_Reader reader;

    Standard_Integer stat = reader.ReadFile((char*)filename);

    // Enable transfer of colours
    reader.SetColorMode(Standard_True);

    reader.Transfer(step_doc);

    // Read in the shape(s) and the colours present in the STEP File
    Handle_XCAFDoc_ShapeTool step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main());
    Handle_XCAFDoc_ColorTool step_colour_contents = XCAFDoc_DocumentTool::ColorTool(step_doc->Main());

    TDF_LabelSequence step_shapes;
    step_shape_contents->GetShapes(step_shapes);


    // List out the available colours in the STEP File as Colour Names
    TDF_LabelSequence all_colours;
    step_colour_contents->GetColors(all_colours);
    PrintMessage(4,"Number of colours in STEP File: ",all_colours.Length());
    for(int i = 1; i <= all_colours.Length(); i++)
    {
    Quantity_Color col;
    step_colour_contents->GetColor(all_colours.Value(i),col);
    PrintMessage(4, "Colour [", i, "] = ",col.StringName(col.Name()));
    }


    // For the STEP File Reader in OCC, the 1st Shape contains the entire
    // compound geometry as one shape
    occgeo->shape = step_shape_contents->GetShape(step_shapes.Value(1));
    occgeo->face_colours = step_colour_contents;
    occgeo->changed = 1;
    occgeo->BuildFMap();

    // occgeo->BuildVisualizationMesh();
    occgeo->CalcBoundingBox();
    PrintContents (occgeo);

    return occgeo;
    }

    ReplyDelete
  9. I'll try that again - 1st time didn't show:
    -----
    I'm happy it doesn't affect you. I'm creating a netgen mesh from a step file and while the step file is in cm the mesh vertices are in mm so i need to rescale. The step file was internally converted to a brep. As far as i can see netgen ignores units completely. Here is the code that gets the step file

    /* Special STEP File load function including the ability to extract individual surface colours via the extended OpenCascade XDE and XCAF Feature set. */
    OCCGeometry * LoadOCC_STEP (const char * filename)
    {
    OCCGeometry * occgeo;
    occgeo = new OCCGeometry;

    // Initiate a dummy XCAF Application to handle the STEP XCAF Document
    static Handle_XCAFApp_Application dummy_app = XCAFApp_Application::GetApplication();

    // Create an XCAF Document to contain the STEP file itself
    Handle_TDocStd_Document step_doc;

    // Check if a STEP File is already open under this handle, if so, close it to prevent
    // Segmentation Faults when trying to create a new document
    if(dummy_app->NbDocuments() > 0)
    {
    dummy_app->GetDocument(1,step_doc);
    dummy_app->Close(step_doc);
    }
    dummy_app->NewDocument ("STEP-XCAF",step_doc);

    STEPCAFControl_Reader reader;

    Standard_Integer stat = reader.ReadFile((char*)filename);

    // Enable transfer of colours
    reader.SetColorMode(Standard_True);

    reader.Transfer(step_doc);

    // Read in the shape(s) and the colours present in the STEP File
    Handle_XCAFDoc_ShapeTool step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main());
    Handle_XCAFDoc_ColorTool step_colour_contents = XCAFDoc_DocumentTool::ColorTool(step_doc->Main());

    TDF_LabelSequence step_shapes;
    step_shape_contents->GetShapes(step_shapes);


    // List out the available colours in the STEP File as Colour Names
    TDF_LabelSequence all_colours;
    step_colour_contents->GetColors(all_colours);
    PrintMessage(4,"Number of colours in STEP File: ",all_colours.Length());
    for(int i = 1; i <= all_colours.Length(); i++)
    {
    Quantity_Color col;
    step_colour_contents->GetColor(all_colours.Value(i),col);
    PrintMessage(4, "Colour [", i, "] = ",col.StringName(col.Name()));
    }


    // For the STEP File Reader in OCC, the 1st Shape contains the entire
    // compound geometry as one shape
    occgeo->shape = step_shape_contents->GetShape(step_shapes.Value(1));
    occgeo->face_colours = step_colour_contents;
    occgeo->changed = 1;
    occgeo->BuildFMap();

    // occgeo->BuildVisualizationMesh();
    occgeo->CalcBoundingBox();
    PrintContents (occgeo);

    return occgeo;
    }

    ReplyDelete
  10. Sorry, jgdes, still not get it :-\. Do you mean to say your shape (and hence final mesh, as Netgen has to operate on the OCC TopoDS_Shape model you feed into it, which is units-agnostic) is the same regardless units in the STEP file ? Can you try the same STEP file and manually editing it to replace CM with MM and see if the mesh changes ?

    ReplyDelete
  11. No It's just a problem with cm. Inches and mm are ok, but cm become mm. If I changed the STEP file to mm, I'd get a mesh in mm. If i leave it as cm i get mm. If i have inches, i get inches. It's weird! But the same probem happens with several CAD programs apparently, presumably because few people use cm, so it's only a problem because the Alibre STEP output is annoyingly always in cm regardless of what unit you thought you were working in. Is there a command for reporting the units of the OCC model?

    ReplyDelete
  12. I want to open a STEP file in Visula C++ using opencascade libraries and reconstruct the shapes and do some analysis. I don't know coding much so please tell how to start or provide a sample code.

    ReplyDelete
  13. Raj, asking someone to do your homework is a little rude. There's plenty of examples that do just what you're after... so get going with the examples that come with OCC. Enjoy.

    ReplyDelete
  14. Raj,
    Jelle is just right - spend your time to learn the product basics and get yourself up to speed. That just works everywhere in life ;-).

    ReplyDelete
  15. Hi

    About filling/plating, I posted a case right now here http://www.opencascade.org/org/forum/thread_21490/
    I wanted to be G1 on just some of the involved edges/faces. Is it possible?

    About "Class A", can we be smoother than G1?

    thanks

    davide

    ReplyDelete
  16. This comment has been removed by a blog administrator.

    ReplyDelete
  17. Hi. I'm trying create GeomPlate_BuildPlateSurface by curves which I can get from edges.
    But I have one issue. I can't convert Geom_Curve to the Adaptor3d_HCurveOnSurface. How to convert Geom_Curve to the Adaptor3d_HCurveOnSurface?

    ReplyDelete
  18. Hi Volodymyr,
    Thanks for the question. Apparently it has already been answered on the dev forum.

    ReplyDelete