2009-03-06

Topology and Geometry in Open CASCADE. Part 6

Continued...

Back references
As you likely noticed using OpenCASCADE or analyzing the diagram in Part1, shapes refer to their sub-shapes and not the other way round. This is understandable as the same (sub-)shape can belong to multiple parent shapes. For instance, any shared edge will belong to at least two faces.
However it is sometimes needed to trace parent shape back from a child. To do this use TopExp::MapShapesAndAncestors().

TopTools_IndexedDataMapOfShapeListOfShape anEFsMap;
TopExp::MapShapesAndAncestors (myShape, TopAbs_EDGE, TopAbs_FACE, anEFsMap);

The code above fills out a map of parent faces for each edge in myShape. If myShape is a solid box, each edge will map to 2 faces. If you explore the same box into faces and try to fill out edge's ancestors in context of each face, then obviously the map will contain a single face for each edge – that very face you are currently in.

Adaptors
Some Open CASCADE algorithms can work on objects representing a curve. However they provide an API that does not accept Geom_Curve but rather Adaptor3d_Curve. For instance, Extrema does so what enables its use in intersection, projection and other algorithms both on geometrical curves (Geom_Curve) and topological edges (TopoDS_Edge). Other examples – calculation of lengths, or surface areas. This approach is known as Adapter pattern.
GeomAdaptor3d_Curve subclasses Adaptor3d_Curve to ‘adapt' Geom_Curve, BRepAdaptor_Curve to TopoDS_Edge, BRepAdaptor_CompCurve to TopoDS_Wire. There are similar classes for 2D curves and surfaces.

So you could write the following to measure lengths of a curve and an edge:

Handle(Geom_Curve) aCurve = ...;
Standard_Real aCurveLen = GCPoints_AbscissaPoints::Length (GeomAdaptor_Curve (aCurve));

TopoDS_Edge anEdge = ...;
Standard_Real anEdgeLen = GCPoints_AbscissaPoints::Length (BRepAdaptor_Curve (anEdge));

Conclusion
So, this has been a long story about fundamental concepts of Open CASCADE. Hope you are now more familiar with them and understand what geometry and topology are. As a first step, make yourself correctly use the terms – curve or edge, surface or face, point or vertex. This will help you and people reading your questions clearly distinguish if you mean geometry or topology. Once you have started using correct definitions and keeping in mind their distinctions, part of your problems may simply go away.

That's it! Full up with this elephant ;-) ?

23 comments:

  1. Thankyou very much Roman...
    This is very useful... Its of great help..
    I was able to find the lenght of the curve...

    ReplyDelete
  2. Roman can you please help me finding the solution to this problem?
    I have used a Geom_Curve to build a curve and used Geom_OffsetCurve and found the offset of the Basis curve. But the problem is that the offset curve obtained is self-intersecting at different locations. So,I want to trim the unnecessary curve and want to display only the exact offset curve (in which every point on the offset curve should be D distance away from the Basis curve) and also the resultant offset curve obtained should be C0 continuous.

    ReplyDelete
  3. Thank you very much for explaining what seemed to be an alphabet soup of class names when I was starting off a couple of weeks or so ago... I still have a lot to learn, but at least I now know where to find things...

    Now, if I can just figure out the visualization parts...

    ReplyDelete
  4. Hi Roman,

    once again thanks for the series! Really great!

    However, what I think would be very helpful is a sample code with some more advanced examples especially for creating geometry.

    I could offer mine if it was working... ;) I was trying to create a solid cylinder bottom-up using this code:

    Handle(Geom_Plane) aSurf = new Geom_Plane (gp::XOY());
    Handle(Geom_Plane) aSurf1 = new Geom_Plane (gp_Pnt(0., 0., 20.), gp_Dir(0., 0., 1.));

    Handle(Geom_Curve) anExtC = new Geom_Circle (gp::XOY(), 10.);
    gce_MakeCirc makeCirc(gp_Pnt(0., 0., 20.), gp_Pnt(0., 0., 40.), 10.);
    Handle(Geom_Curve) anExtC1 = new Geom_Circle (makeCirc.Value());
    TopoDS_Edge anExtE = BRepBuilderAPI_MakeEdge (anExtC);
    TopoDS_Edge anExtE1 = BRepBuilderAPI_MakeEdge (anExtC1);
    TopoDS_Wire anExtW = BRepBuilderAPI_MakeWire (anExtE);
    TopoDS_Wire anExtW1 = BRepBuilderAPI_MakeWire (anExtE1);

    Handle(Geom_CylindricalSurface) aCylinder = new Geom_CylindricalSurface(gp_Ax3(gp_Pnt(0.,0.,0.), gp_Dir(0.,0.,1.), gp_Dir(1.,0.,0.)), 10.);

    Handle(Geom2d_Line) line1 = new Geom2d_Line(gp_Pnt2d(0., 0.), gp_Dir2d(0.,1.));
    Handle(Geom2d_TrimmedCurve) line1Trimmed = new Geom2d_TrimmedCurve(line1, 0., 20.);
    TopoDS_Edge aEdge3OnSurf1 = BRepBuilderAPI_MakeEdge(line1Trimmed , aCylinder);

    Handle(Geom2d_Line) line2 = new Geom2d_Line(gp_Pnt2d(2. * PI, 0.), gp_Dir2d(0.,1.));
    Handle(Geom2d_TrimmedCurve) line2Trimmed = new Geom2d_TrimmedCurve(line2, 0., 20.);
    TopoDS_Edge aEdge6OnSurf1 = BRepBuilderAPI_MakeEdge(line2Trimmed , aCylinder);

    Handle(Geom2d_Line) linec1 = new Geom2d_Line(gp_Pnt2d(0., 0.), gp_Dir2d(1., 0.));
    Handle(Geom2d_TrimmedCurve) linec1Trimmed = new Geom2d_TrimmedCurve(linec1, 0., 2. * PI);
    TopoDS_Edge aEdge4OnSurf1 = BRepBuilderAPI_MakeEdge(linec1Trimmed , aCylinder);

    Handle(Geom2d_Line) linec2 = new Geom2d_Line(gp_Pnt2d(0., 20.), gp_Dir2d(1., 0.));
    Handle(Geom2d_TrimmedCurve) linec2Trimmed = new Geom2d_TrimmedCurve(linec2, 0., 2. * PI);
    TopoDS_Edge aEdge5OnSurf1 = BRepBuilderAPI_MakeEdge(linec2Trimmed , aCylinder);

    BRepLib::BuildCurves3d(aEdge3OnSurf1);
    BRepLib::BuildCurves3d(aEdge4OnSurf1);
    BRepLib::BuildCurves3d(aEdge5OnSurf1);
    BRepLib::BuildCurves3d(aEdge6OnSurf1);

    BRepBuilderAPI_MakeWire cylindricalWire;

    cylindricalWire.Add(TopoDS::Edge(aEdge6OnSurf1.Reversed()));
    cylindricalWire.Add(aEdge4OnSurf1);
    cylindricalWire.Add(TopoDS::Edge(aEdge5OnSurf1.Reversed()));
    cylindricalWire.Add(aEdge3OnSurf1);

    BRep_Builder aB;
    BRep_Builder aB1;
    BRep_Builder aB2;
    TopoDS_Face aFace;
    TopoDS_Face aFace1;
    TopoDS_Face aFaceCylindrical;

    aB.MakeFace (aFace, aSurf, Precision::Confusion());
    aB.Add(aFace, anExtW);

    aB1.MakeFace (aFace1, aSurf1, Precision::Confusion());
    aB1.Add(aFace1, anExtW1);

    aB2.MakeFace (aFaceCylindrical, aCylinder, Precision::Confusion());
    aB2.Add(aFaceCylindrical, cylindricalWire);

    ShapeFix_Shape fixer(aFaceCylindrical);
    fixer.Perform();
    aFaceCylindrical = TopoDS::Face(fixer.Shape());

    BRep_Builder aBShell;
    TopoDS_Shell shellF;
    aBShell.MakeShell(shellF);
    aBShell.Add(shellF, aFaceCylindrical);
    aBShell.Add(shellF, aFace);
    aBShell.Add(shellF, aFace1);

    BRep_Builder aBSolid;
    TopoDS_Solid solid;
    aBSolid.MakeSolid(solid);
    aBSolid.Add(solid, shellF);

    //ShapeFix_Solid fixer1(solid);
    //fixer1.Perform();
    //TopoDS_Shape s = fixer1.Shape();

    And I'm obviously missing something as I need to use ShapeFix a lot: the first fixer is necessary to correctly create aFaceCylindrical. Unfortunately, the created solid is not correct. It behaves just like a shell. I hoped fixer1 would create a solid of the resulting shell but it doesn't work - that's why it is commented out.
    There seems to be a problem with creating the edges on the cylindrical surface (linec1, linec2). But I couldn't figure out why. So I woudl appreciate any hints.

    Maybe you continue with part 7 or start a new article... ;)

    Pawel

    ReplyDelete
  5. Swathi,

    Regarding self-intersection, as advised on the forum, the best is to use 2d intersection algorithm provided that your original curve is also planar (even though in 3D).
    Offset curve is by default C0 or higher. It requires that basis curve is C1 or higher and Geom_OffsetCurve::Continuity() is basic curve continuity - 1.

    ReplyDelete
  6. Pawel,
    Below is an example of creating a solid cylinder bottom-up. A few general recommendations:
    - don't abuse ShapeFix;
    - don't use Geom(2d)_TrimmedCurve for edges, rather simply use parameters of edge;
    - beware BRepBuilderAPI_Make* (as it may have side effects such as reorienting or reconstructing subshapes inside), and prefer BRep_Builder for any non-trivial case

    // creates a solid cylinder along Z, with bottom at z=0, top at z = 20, radius 10
    static TopoDS_Solid TestCreateCylinder ()
    {
    const Standard_Real aR = 10.;
    const Standard_Real aZ = 20;

    //axis placements
    gp_Ax2 aBAx (gp::XOY());
    gp_Ax2 aTAx (gp_Pnt (0, 0, aZ), gp::DZ(), gp::DX());

    //surfaces
    Handle(Geom_Plane) aBPlane = new Geom_Plane (aBAx);
    Handle(Geom_Plane) aTPlane = new Geom_Plane (aTAx);
    Handle(Geom_CylindricalSurface) aCSurf = new Geom_CylindricalSurface (aBAx, aR);

    //3D curves
    Handle(Geom_Circle) aBCirc = new Geom_Circle (aBAx, aR);
    Handle(Geom_Circle) aTCirc = new Geom_Circle (aTAx, aR);
    gp_Pnt aBPnt (aR, 0, 0);
    Handle(Geom_Line) aLine = new Geom_Line (aBPnt, gp::DZ());

    BRep_Builder aBuilder;

    //vertices
    TopoDS_Vertex aBVertex;
    aBuilder.MakeVertex (aBVertex, aBPnt, Precision::Confusion());
    TopoDS_Vertex aTVertex;
    aBuilder.MakeVertex (aTVertex, gp_Pnt (aR, 0, aZ), Precision::Confusion());

    //edges (casting using BRepBuilderAPI_MakeEdge::operator TopoDS_Edge())
    TopoDS_Edge aBEdge = BRepBuilderAPI_MakeEdge (aBCirc, aBVertex,
    TopoDS::Vertex(aBVertex.Reversed())); //will be parameterized at full range [0, 2*PI]
    TopoDS_Edge aTEdge = BRepBuilderAPI_MakeEdge (aTCirc, aTVertex,
    TopoDS::Vertex (aTVertex.Reversed()));
    TopoDS_Edge aLEdge = BRepBuilderAPI_MakeEdge (aLine, aBVertex,
    TopoDS::Vertex (aTVertex.Reversed()));//[0, aZ] range

    //wires
    TopoDS_Wire aBWire = BRepBuilderAPI_MakeWire (aBEdge);
    TopoDS_Wire aTWire = BRepBuilderAPI_MakeWire (aTEdge);
    TopoDS_Wire aCWire;
    aBuilder.MakeWire (aCWire);
    aBuilder.Add (aCWire, aBEdge);
    aBuilder.Add (aCWire, aLEdge);
    aBuilder.Add (aCWire, aTEdge.Reversed());
    aBuilder.Add (aCWire, aLEdge.Reversed());

    //faces
    TopoDS_Face aBFace = BRepBuilderAPI_MakeFace (aBPlane, aBWire);
    TopoDS_Face aTFace = BRepBuilderAPI_MakeFace (aTPlane, aTWire);
    TopoDS_Face aCFace;
    aBuilder.MakeFace (aCFace, aCSurf, Precision::Confusion());
    aBuilder.Add (aCFace, aCWire);

    //add pcurves into each edge on each face
    //pcurves on planes are optional, create pcurve on bottom face only (for demo)
    Handle(Geom2d_Circle) aBCirc2d = new Geom2d_Circle (gp::OX2d(), aR);
    aBuilder.UpdateEdge (aBEdge, aBCirc2d, aBFace, BRep_Tool::Tolerance (aBEdge));

    Handle(Geom2d_Line) aCLineB = new Geom2d_Line (gp::Origin2d(), gp::DX2d());
    aBuilder.UpdateEdge (aBEdge, aCLineB, aCFace, BRep_Tool::Tolerance (aBEdge));

    Handle(Geom2d_Line) aCLineT = new Geom2d_Line (gp_Pnt2d (0, aZ), gp::DX2d());
    aBuilder.UpdateEdge (aTEdge, aCLineT, aCFace, BRep_Tool::Tolerance (aTEdge));

    Handle(Geom2d_Curve) aLCurve2dR = new Geom2d_Line (gp::Origin2d(), gp::DY2d()); //for seam-edge when reversed
    Handle(Geom2d_Curve) aLCurve2dF = new Geom2d_Line (gp_Pnt2d (2 * PI, 0), gp::DY2d()); //for seam-edge when forward
    aBuilder.UpdateEdge (aLEdge, aLCurve2dF, aLCurve2dR, aCFace, BRep_Tool::Tolerance (aLEdge));

    //shell
    TopoDS_Shell aShell;
    aBuilder.MakeShell (aShell);
    aBuilder.Add (aShell, aBFace.Reversed());
    aBuilder.Add (aShell, aCFace);
    aBuilder.Add (aShell, aTFace);

    //solid
    TopoDS_Solid aSolid = BRepBuilderAPI_MakeSolid (aShell);
    return aSolid;
    }

    ReplyDelete
  7. Hi Roman

    Can you please help me in finding a function which is used to create a polygon?

    I want to create a polygon with N points where N=3,4..etc. I could find only Geom_Line which is used to draw a line passing through two points. Geom_Curve has different functions,but none of the functions can make a polygon when given N points.

    ReplyDelete
  8. Hi Swathi,
    Polygon is a wire (i.e. topology) not a curve (geometry).
    Check BRepBuilderAPI_MakePolygon.

    ReplyDelete
  9. Thanks a lot Roman,

    I have another doubt. Now I have created a Polygon. It is a TopoDS_Wire. Now I want to convert this TopoDS_Wire to a Geom_Curve. How can I convert?

    ReplyDelete
  10. No way. Make sure you well comprehend the concepts of a wire and an edge (might want to re-read respective articles). Wire is a container of edges. Edge contains 3D curve. Wire does NOT have any relationship to curves.

    If for any good reason you can't deal with edges and their curves one by one, then you have to deal with a single edge and its (single) curve. To encode a polygon you might want to use a B-Spline curve of degree 1.

    ReplyDelete
  11. I want to offset a curve. So, I have used Geom_Offestcurve function to do that. Here this function takes Geom_Curve as the parameter. So, I tried making closed curves using b-spline and tried to offset them. As the degree of the b-spline increased the curve obtained is very complicated and has many self-intersections. So, I thought of creating a polygon and then offset that polygon.
    How can I do this please guide me.
    If the curve has self-intersections I tried to make the curve to 2d and then find the intersection points but for some curves it gives wrong result. What does the Degmin, Degmax parameters of the function
    GeomAPI_PointsToBSplineSurface (const TColgp_Array2OfPnt &Points, const Standard_Integer DegMin=3, const Standard_Integer DegMax=8, const GeomAbs_Shape Continuity=GeomAbs_C2, const Standard_Real Tol3D=1.0e-3)
    mean...

    ReplyDelete
  12. Hi Roman,

    thanks a lot for the hints!

    I was oscillating somewhere between my original solution and your code but there was one thing I could not figure out so let me just point it out here once again for people attempting to construct their own shapes.

    Make sure you set two pcurves for seam-edges with this (or similar) function:
    aBuilder.UpdateEdge (aLEdge, aLCurve2dF, aLCurve2dR, aCFace, BRep_Tool::Tolerance (aLEdge));

    Pawel

    ReplyDelete
  13. Hi,

    just a cross-post based on: http://www.opencascade.org/org/forum/thread_15635/

    I've started implementing a topology browser for OCC as a part of Total Engineer. Maybe somebody's interested.

    http://www.tes-cax.de/index-en/download.html

    Pawel

    ReplyDelete
  14. Hi Pawel,

    This is an excellent idea and the tool can likely be of great help for any Open CASCADE developer. I thought to develop it and to include one into my QOLib but did not come up to it yet.

    Here are some ideas I had in mind for it:
    - tree-like widget
    - back references (e.g. by clicking on an edge you could go up to faces that share it)
    - filtering capabilities (by topology and geometry type), e.g. to show only faces on B-Spline surfaces)
    - visualization – global 3D view where current subshape is highlighted and individual 3D view where you could zoom a subshape + 2D view for face pcurves
    - underlying geometry visualization (e.g. like B-Splines display the grid of their poles in Draw)
    - advanced filtering (e.g. C0 B-Splines, Rational B-Splines, Circles with radii > 5, etc)
    - using icons for faster type recognition (e.g. elementary vs B-Spline geometry)
    - number of subshapes at any given level (e.g. highlighting a face you could see how many edges it has)

    Hope this helps.
    Roman

    ReplyDelete
  15. Hi Roman,

    thanks for your insights and hints!

    First of all, a question: The topology viewer is actually based on a TreeView and a PropertyGrid control. What do you exactly mean by the 'tree-like widget'?

    So, some of the proposed functionalities can be easily added and so they are likely to come soon:
    - icons,
    - back references are actually implemented also. They allow checking some properties of edges (e.g. IsSeam). However, at the moment they are either used internally or can be read in the properties in the '2D Curve' section ('ParentFace' property reveals the id of the parent face). Though, highlighting the parent face either in the viewer or in the TopologyExplorer doesn't seem to be very time-consuming.

    If needed, some basic filtering can be achieved through extracting of specific subshapes (context menu in the Topology Browser) but advanced filtering possibilities are surely very interesting and I'll put it on my TODO list.

    To tell the truth, separate viewers 2D and 3D + highlighting of corresponding subshapes could be really useful but due to implementation issues it is rather a second-priority task.

    There are also a couple of things that I will add to the TopologyExplorer in the future:
    - processing of the remaining Geom_Curves and Surfaces (ellipses, cones etc.)
    - reshaping/topology editing (possible for floating/belonging to linear edges vertices at the moment).

    Best regards
    Pawel

    ReplyDelete
  16. Hi Roman,
    Thanks again for your time and effort. Your notes have been very helpful in shinning some light on very complex library. I have one question, I am trying to learn OpenCASCADE on my own, to further my career. Do you recommend me to take e-learning course? I am not sure how else can I get a grip of the libraries that I can successfully use in my development. Pl comment.
    Thanks,
    Venu

    ReplyDelete
  17. Hi Venu,
    Glad these posts have been helpful. Regarding e-learning I don't have a solid opinion and moreover I did not see the materials for about 5 years, so my opinion would be less grounded. You might want to ask or search on the Open CASCADE forum - there were some feedbacks on this subject.
    Open CASCADE can be challenging to learn and to use in the beginning so be ready to allocate some time into your projects. If this is for your company make sure you clarify expectations with your management upfront.
    Good luck !
    Roman

    ReplyDelete
  18. Thanks Roman, appreciate your inputs. I know may be it is mere foolishness on my part to try to learn such a sophisticated software on my own. But I want to give a genuine try...I waited so many years for something like that...I can't resist to learn it when an opportunity is provided.

    Thanks you very much for helping out people like me and others.
    Regards,
    Venu

    ReplyDelete
  19. Hi Roman,

    Really impressed with the incredible work that you have been doing by writing these posts. I have a question and I did do my homework before deciding to munch on your precious time :). Is it possible to find all the Faces that point to a given surface, so basically you have a surface pointer and you want to know all the topological faces that contain (point to) it? One can do it by iterating through the face list but is there a single API or something that one can call on the surface and get all the faces?

    Thanks in advance.

    ReplyDelete
  20. Hi Kapil,

    Surfaces do not have any references to the owning topological objects (faces). So unless you store this mapping elsewhere, indeed, the only way is to explore the model:
    for (TopExp_Explorer anExp(aShape, TopAbs_FACE);...)

    Same works for curves-edges.

    However, it is not recommended to share geometry by topology entities. OCC modeling algorithms do not do that, and neither should you in your own. Perhaps, Modeling Data User's Guide describe this convention, though not sure. The main driver for that is to avoid impact of other sharing topological objects if one of them needs to adjust this geometry (e.g. improve continuity of a B-Spline surface).
    Hope this helps.
    Roman

    P.S. Thanks for doing your homework ;-)

    ReplyDelete
  21. Hi Roman,

    Thanks for the reply.

    I don't intend to share the geometry among different topological entities. The thing is I am providing a brep wrapper over opencascade to a third party and the way I communicate is through entity (topo or geom) ids (integers). I have to support an api which given a surface get the face that uses that surface.

    As of now the wrapper interface supports only read only operations (and I guess that is how it is going to be) so I am not expecting the underlying geometrical pointer to change for a given topological entity (I hope to be right on this one). Just started wondering would the pointer remain intact even if I start supporting model modifying operations ???.

    One again thanks for your reply and really appreciate the efforts you take not just to answer the question but to also foresee any problems one might face.

    ReplyDelete
  22. Hi Roman,

    Does BRep_Tool::Triangulation always returns a triangulation with normal information or is there no such guarantee?? I see that for pretty simply cases (cube) I receive triangulations with no normal information.

    Thanks in advance.

    ReplyDelete
  23. Hi Roman,

    Does BRep_Tool::Triangulation always returns a triangulation with normal information or is there no such guarantee?? I see that for pretty simply cases (cube) I receive triangulations with no normal information.

    Thanks in advance.

    ReplyDelete