Topology and Geometry in Open CASCADE. Part 4


Though the next topology type after the edge considered in the previous post is a wire, let's jump to the face which is the last topology entity that binds with geometry. We'll consider a wire as well as the rest of topology types in the future posts. Frankly, I was not originally going to speak of them but several folks on the blog asked to.

A face is a topology entity that describes a boundary unit of the 3D body. A face is described with its underlying surface and one or more wires. For instance a solid cylinder consists of 3 faces – bottom and top, and lateral. Each of respective underlying surfaces is infinite (Geom_Plane and Geom_CylindricalSurface), while each face bounds its surface with a wire – two of them consists of a single edge lying on Geom_Circle and the lateral face consists of 4 edges – 2 are shared with top and bottom faces, and remaining two represent a seam edge (see previous post on edges), i.e. the face contains it twice in its wire (with different orientations).

Let's briefly recall what a surface is. If you had a course of mathematical analysis in your higher school you likely remember this by heart, if not you might want to read some articles to educate yourself. First part of this one on wikipedia give simple examples of parametric surfaces.

A surface maps its 2D parametric space {U,V} into a 3D space object (though still two-dimensional). Compare it with molding when a planar steel sheet is transformed into something curved. Parametric space can be bounded or unbounded, or (un)bounded in one direction only. For instance, a plane's parametric space is unbounded, while NURBS is bounded, and a cylindrical surface is bounded in U (from 0 to 2*PI) and is unbounded in V (-infinity, + infinity).
Geom_Surface::Value() returns a 3D point (X, Y, Z) from a point in a parametric space (U, V). For instance any location on Earth is described by latitude (V) and longitude (U), but can be viewed as 3D point in the Universe (if Earth center had some origin defined).

Let's recall that an edge must have both a 3D curve and a pcurve in its face surface space. Open CASCADE requires that face boundaries (wires) represent closed contours in 3D and 2D space. Therefore a face cylinder's lateral face is described as we considered above.

Not all modeling kernels impose such restrictions. I remember a cylinder coming from Unigraphics (via STEP) which lateral face was described with 2 wires, each consisting of a circular edge. We had a few discussions with my colleagues from the Data Exchange team on how to recognize such cases and how to convert them into Open CASCADE valid wire. As a result, Shape Healing's ShapeFix_Face has been extended with FixMissingSeam().

Face orientation shows how face normal is aligned with its surface normal. If orientation is TopAbs_FORWARD then normals match, if TopAbs_REVERSED then they are opposite to each other.
Face normal shows where body material is – it lies ‘behind' the face. In a correct solid body all face normals go ‘outward' (see below):

Material on the face is defined by orientation of its edges. The side is defined by a cross product of a surface (not face!) normal and edge derivative. Edge derivative equals its 3D curve derivative if edge is forward and is opposite, if reversed. Perhaps easier understanding can be got if to consider edge pcurves: if an edge is forward then material is on the left, if reversed, material is on the right. This may sound complicated but once you get it, it will be simpler ;-). We'll speak more of orientation in a dedicated chapter.

Geometric meaning of a face tolerance is a thickness of a pie surrounding a surface.

A face tolerance is used by modeling algorithms significantly more rarely than edge's or vertex' and is often retained at default level (Precision::Confusion()).
By convention, Open CASCADE requires that the following condition is respected:
Face tolerance <= Edge tolerance <= Vertex tolerance, where an Edge lies on a Face, and a Vertex – on an Edge.

In addition to underlying surface, a face may contains a tessellated representation (composed of triangles). It is computed, for example, when a face is displayed in shading mode. Visualization algorithms internally call BRepMesh::Mesh() which calculates and adds triangulation for every face.

Additional location
Unlike an edge or a vertex, a face has an additional location (TopLoc_Location) which is a member field of BRep_TFace. So, do not forget to take it into account when using underlying surface or triangulation. Stephane has recently pointed this out on a forum.

Creating a face bottom-up and accessing its data
Like in the case of a vertex and an edge, BRep_Builder and BRep_Tool is what you need.
BRep_Builder aBuilder;
TopoDS_Face aFace;
aBuilder.MakeFace (aFace, aSurface, Precision::Confusion());

TopLoc_Location aLocation;
Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aFace, aLocation);
gp_Pnt aPnt = aSurf->Value (aU, aV).Transformed (aLocation.Transformation());
//or to transform a surface at once
//Handle(Geom_Surface) aSurf = BRep_Tool::Surface (aFace);
//gp_Pnt aPnt = aSurf->Value (aU, aV);

Handle(Poly_Triangulation) aTri = BRep_Tool::Triangulation (aFace, aLocation);
aPnt = aTri->Nodes.Value (i).Transformed (aLocation.Transformation());

Hope this was not too boring ;-). Just bookmark it and get back when you need it!

To be continued...


  1. It explains so clearly. Thank you so much for your great job.

  2. One question about the location.

    If the face is a part of a TopoDS_Shape can the TopoDS::Location::Transformation be different from the one saved in BRep_TFace?


  3. BRep_TFace location (returned by BRep_Tool::xxx (const TopoDS_Face&, TopLoc_Location&) is an independent instance of one stored in TopoDS_Shape. Final location is a product of both:
    TopLoc_Location L = F.Location() * TF->Location();
    I don’t remember why it was introduced into BRep_TFace (if OCC folks have a clue, please post here). My possible guess is to enable explicit sharing (and thus to save some memory) but I am not sure.

    1. Note the difference in returned Location for BRep_Tool::Tolerance and BRepTool::Surface:

      const Handle(Poly_Triangulation)&
      BRep_Tool::Triangulation(const TopoDS_Face& F, TopLoc_Location& L)
      L = F.Location();
      return (*((Handle(BRep_TFace)*)&F.TShape()))->Triangulation();

      const Handle(Geom_Surface)& BRep_Tool::Surface(const TopoDS_Face& F, TopLoc_Location& L)
      Handle(BRep_TFace)& TF = *((Handle(BRep_TFace)*) &F.TShape());
      L = F.Location() * TF->Location();
      return TF->Surface();

  4. Hi, When i use boolean fuse two adjacent boxes(adjacent faces) , then why the common edge is visible. Using the old BRepAlgo_Fuse API , this is not the case. are there any flag/settings to remove this common edge ?
    thanks -PG

  5. PG, maybe BRepFeat_Gluer is the tool you are looking for. You can use it to glue solids on common faces. LocOpe_FindEdges will help you find common edges, that you can then bind from your gluer.
    You can check for pythonOCC examples, under Level1/TopologyLocalOperations/topology_local_operations.py

  6. hi,
    thanks very much for posts.
    i understood that surfaces can be parabolic just like the first picture bottom-up.
    is it true?
    did i understand right?
    and parabolic surfaces should be represented with mathmatics functions.
    then which member of geom_surface in http://opencascade.sourcearchive.com/documentation/6.3.0.dfsg.1-1/classGeom__Surface-members.html
    holds the function data?

  7. hi again,
    i think value() function do the job.