Creating ACIS Nurbs Surfaces using B-Rep Builder

Oleksandr Bryzghalin

December 05, 2017

With Teigha you can create ACIS nurbs surfaces using a B-Rep Builder interface. Currently Teigha has two B-Rep Builder implementations: one in Teigha Kernel named AcisBrepBuilder and the other in Teigha BIM named BmBrepBuilder.

Although currently the functionality of AcisBrepBuilder is limited (you can create only one face, and the edge/coedge geometry is set only with OdGeNurbCurve3d/OdGeNurbCurve2d, respectively), the functionality can be expanded, for example, to convert 3D models from one format to another. For example, to load a source file and then use Br classes to get and create entities, you can use AcisBrepBuilder and the Br entities to pass to the input. After calling OdRxObjectPtr OdBrepBuilder :: finish (), you get a new ACIS entity.

How does this work?

Suppose you are working with the ACIS kernel. First, you need to create AcisBrepBuilder. In general, you can use the constructor and then the creation of AcisBrepBuilder looks like this:

OdBrepBuilder brepBuilder;
new AcisBrepBuilder(brepType);

Where brepType is the type of entity being created. Currently it is used only in BmBrepBuilder and can take these values:

  • kOpenShell — If the output geometry is an open shell.
  • kSolid — If the output geometry is a solid.
  • kVoid — If the output geometry is a void (i.e., an inverted solid).

Next, set the tolerance that will be used to test the input data using this method:

void setValidatorParameters(BrepValidator* vparams)

Where BrepValidator* vparams is a pointer to an object that contains the necessary tolerances. For each BrepBuilder, you can specify your own class that contains all the necessary tolerances.

For ease of use, the initialization of BrepBuilder was moved to services, and to initialize AcisBrepBuilder, you just need to call this method:

OdResult OdModelerGeometryCreatorABImpl::initBrepBuilder(OdBrepBuilder& brepBuilder, BrepType, OdGeTol& resTol, OdGeTol& distancetol)

Now you can pass directly to the creation of the ACIS model.

The OdBrepBuilder interface contains many methods, and to create an ACIS model, you must at least define a face, which is a surface and has a trimming-loop on it. To set the face, use this method:

BRepBuilderGeometryId addFace(OdGeSurface* pSurf, EntityDirection faceDirection)

The first parameter is a pointer to the surface; the second parameter is how the face will be oriented relative to the surface (co-directional or not). So for an AcisBrepBuilder surface, there should be an OdGeNurbSurface. For output, you will get the face ID, which will be used later to create the trimming-loop. It is created using this method:

BRepBuilderGeometryId OdBrepBuilder::addLoop(BRepBuilderGeometryId& faceId) 

Here, the input parameter is the face identifier, so the trimming-loop will be on this particular face. In general, one face can have multiple loops, for example, if you need to make a hole in the face so you can create several loops.

Next specify the geometry of the loop. For this purpose, these methods are used:

BRepBuilderGeometryId OdBrepBuilder::addEdge(OdGeCurve3d* pCurveForEdge)
BRepBuilderGeometryId OdBrepBuilder::addCoedge(BRepBuilderGeometryId& loopId, BRepBuilderGeometryId& edgeId, EntityDirection codgeDirection = kForward, OdGeCurve2d* pParCur = NULL)

The first method creates an edge with given geometry, and the second method creates a coedge. First, create an edge. To do this, set the geometry for the edge as a pointer to the 3D curve. In AcisBrepBuilder, the geometry edge is specified using OdGeNubrCurve3d. For output, get the edge ID that you need to create the coedge. Call the OdBrepBuilder::addCoedge method and pass it a loop ID, an edge ID, a direction relative to the edge, and a parametric curve that maps an interval of the real line into a 2D real vector space (parameter space). The following condition must be satisfied: the interval of the parametric curve must be the same as the interval of the 3D curve at the edge. Thus, we set the necessary minimum for creating a face. Now call OdRxObjectPtr OdBrepBuilder::finish () and get the entity.

Consider creating a nurbs surface with AcisBrepBuilder using the example from OdWriteEx. Here are the key parts:

1. Initialization.

/********************************************************************/
/* BrepBuilder initialization                                       */
/********************************************************************/
OdDbHostAppServices *pService = pDb->appServices();
BrepType bbType = kOpenShell;
OdBrepBuilder brep;
pService->brepBuilder(brep, bbType);

2. Create geometry for edge/coedge. Not all code is included below, but the curves can either be created manually or obtained with the help of Br classes.

OdGeNurbSurface nurbSurf;
OdArray<OdArray< OdGeNurbCurve3d*> > arrLoops;    //Array of loops
OdArray<OdArray< OdGeNurbCurve2d*> > arrLoopsPar; //Their parametric curves
OdDbNurbSurfacePtr pSurface = OdDbNurbSurface::createObject();//Resulting acis nurb surface
OdArray< OdGeNurbCurve3d*> currentLoop;    //The edge array of current loop 
OdArray< OdGeNurbCurve2d*> currentParLoop; //The coedges geometry of current loop 

3. Create the topology of the ACIS model. Create a face and a trimming-loop on it. A trimming-loop is created from coedges. After, call brep.finish ().

/********************************************************************/
/* Create NURBS Surface by BrepBuilder                              */
/********************************************************************/
try
{ 
  BRepBuilderGeometryId faceId = brep.addFace(&nurbSurf, OdBrepBuilder::kForward);
  ODA_ASSERT(arrLoops.size() == arrLoopsPar.size());
  for (unsigned int i = 0; i < arrLoops.size(); i++)
  {
    OdArray< OdGeNurbCurve3d*> currentLoop = arrLoops[i];
    OdArray< OdGeNurbCurve2d*> currentParLoop = arrLoopsPar[i];
    ODA_ASSERT(currentLoop.size() == currentParLoop.size());
    BRepBuilderGeometryId LoopId = brep.addLoop(faceId);
    for (unsigned int j = 0; j < currentLoop.length(); j++)
    {
      BRepBuilderGeometryId edgeId = brep.addEdge(currentLoop[j]);
      brep.addCoedge(LoopId, edgeId, OdBrepBuilder::kForward, currentParLoop[j]);
    }
  }
  OdRxObjectPtr pModelGeometry;
  pModelGeometry = brep.finish();
  pSurface->setBody(pModelGeometry);
}
catch (const OdError& e)
{
  odPrintConsoleString(L"\n\nException occurred: %ls\n", e.description().c_str());
}

The complete example can be found in the OdWriteEx sample located in the Examples folder where you installed Teigha.