Geometric Modeler

Topology

Overview of the Topological Operators

How to use them to create bodies
Use Case

Abstract

Build on a common scheme, the topological operators are transient objects used to create bodies. The use case illustrates their use in chaining them to create bodies: primitive creation (CATSolidCylinder, CATSolidCuboid), skin body creation (CATTopSkin), prism (CATTopPrism), Boolean operation (CATDynBoolean), filleting (CATDynFillet) and shelling (CATDynShell). The volume of the resulting body is also computed (CATDynMassProperties3D).

The use of the journal, describing the topological modifications from the input bodies to the resulting body, is not described here. See the dedicated use case "Managing the Journal" [1] to have more information on this point.


What You Will Learn With This Use Case

In this use case, the general scheme of the topological operators is explained.

Using topological operators is an easy way to create new consistent topological objects. There are two types of operators:

All these operators follow the smart concept [3]: they never modify the input bodies. They always create new topological objects, which share topological cells to reduce the model size.

The operators can log, under request, the follow-up of the faces and free edges from the input bodies to the resulting body. This data is written, under request, on a topological journal [4] attached to each operator. Hence, the topological journal offers the developer the means to develop procedural applications, such as feature based modeling, but this point in not detailed here. See the dedicated use case [1] to have more information on the use of the journal.

The topological operators are transient objects used to define topological operations, and cannot be streamed.

The AdvancedTopologicalOpe framework provides advanced topological operators in surface design. They follow the general scheme of the topological operators, but are not described here.

[Top]

The General Scheme

All the operators are based on the same scheme as follow that:

  1. Creates an operator
  2. If needed, specifies or modifies additional information such as the definition of a ribbon of a draft or a fillet, the type of trim
  3. Runs the operator: Run
  4. Gets the result: GetResult
  5. Deletes the operator instance.

Unlike the geometric operators, the topological operators do not provide a BASIC and an ADVANCED modes. The topological operators are always set in ADVANCED mode: the run is always mandatory.

[Top]

The CAATopOverview Use Case

CAATopOverview is a use case of the CAATopologicalOperators.edu framework that illustrates TopologicalOperators framework capabilities.

[Top]

What Does CAATopOverview Do

The use case creates the body of Fig.1 by chaining topological operators.

Fig. 1: The Resulting Body
  • A skin is created from a profile and extruded to produce a prism
  • A box primitive is added and a cylinder subtracted
  • The edges of the external loop of the upper face of the prism are filleted. This face is characterized by two holes: one for the path of the cylinder, on for the path of the box
  • A shelling operation is applied with one opening face, the bottom face of the prism.

[Top]

How to Launch CAATopOverview

To launch CAATopOverview, you will need to set up the build time environment, then compile CAATopOverview.m along with its prerequisites, set up the run time environment, and then execute the use case [8].

If you simply type CAATopOverview with no argument, the use case executes, but doesn't save the result in an NCGM file. If you want to save this result, provide the full pathname of the NCGM file to create. For example:

With Windows CAATopOverview e:\Overview.NCGM

With UNIX CAATopOverview /u/Overview.NCGM

This NCGM file can be displayed using the CAAGemBrowser use case.

 

[Top]

Where to Find the CAATopOverview Code

The CAATopOverview use case is made of a main named CAATopOverview.cpp located in the CAATopOverview.m module of the CAATopologicalOperators.edu framework:

Windows InstallRootDirectory\CAATopologicalOperators.edu\CAATopOverview.m\
Unix InstallRootDirectory/CAATopologicalOperators.edu/CAATopOverview.m/

where InstallRootDirectory is the directory where the CAA CD-ROM is installed.

[Top]

Step-by-Step

The main program:

  1. Creates the Geometry Factory
  2. Creates a Skin Body (CATTopSkin)
  3. Creates a Prism (CATTopPrism)
  4. Creates a Box Primitive (CATDynSolidCuboid) and a Cylinder Primitive (CATDynSolidCylinder)
  5. Adds and Subtracts (CATDynBoolean)
  6. Fillets (CATDynFillet)
  7. Shells (CATDynShell)
  8. Computes the Volume (CATDynMassProperties3D)
  9. Writes the Model and Closes the Container

[Top]

Creating the Geometry Factory

The geometry factory (CATGeoFactory) creates and manages all the CATICGMObject : it creates the points, curves, surfaces and bodies and remove them [7].

The CATGeoFactory creation itself is done by the global function ::CATCreateCGMContainer.

Notice that the factory can be defined by reading a NCGM file that was previously stored. In that case, the global function ::CATLoadCGMContainer must be used.

CATGeoFactory* piGeomFactory = ::CATCreateCGMContainer() ;
if (NULL==piGeomFactory) return (1);

[Top]

Creating a Skin Body

This section illustrates the use of the type of topological operators that derive from CATGeoToTopOperator. There are two ways to create a skin body:

  1. Creating the Surface
      CATPlane * piPlane = piGeomFactory->CreatePlane(CATMathOIJ);  // xy plane
        if (NULL == piPlane)
        {
          ::CATCloseCGMContainer(piGeomFactory);
          return (1);
        }
  2. Creating the Curves On the Surface

    Now, using the characteristics of the plane (CATPlane::GetAxis), the CATPLine and CATPCircle of the contour are created.

    Fig. 2: The Skin Body

    The lines and circles are created with the corresponding CATGeoFactory::CreatePLine and CATGeoFactory::CreatePCircle methods. As these lines and circles are defining on the surface, they can only be created from surface parameters. However, no assumption can be done on the parameterization of the geometric objects. The parameters on the plane are evaluated with the CATSurface::GetParam method, from 3D points that are known to be on the plane. This method can be called because the plane is a canonical object, and the points are already on it. If one of these conditions were not filled, it would be mandatory to call the CATProjectionPtSur geometric operator.

    // ----------- Retrieves the mathematical definition of the geometrical plane
    CATMathPoint mathOrigin;
    CATMathDirection mathU, mathV;
    piPlane->GetAxis(mathOrigin,mathU,mathV);
    	
    // ----------- Defines points on the plane
    // Notice that we do not make any assumption on the plane parameterization.
    // The use of GetParam is allowed here, because the 3D points belong to the plane
    // by construction
    CATSurParam p1, p2, p3, p4, c1, c2;
    piPlane->GetParam(mathOrigin, p1);
    piPlane->GetParam(mathOrigin - 20*mathU                      , p2);
    piPlane->GetParam(mathOrigin - 10*mathU +    10*sqrt(3)  *mathV, p3);
    piPlane->GetParam(mathOrigin            +    10*sqrt(3)  *mathV, p4);
    piPlane->GetParam(mathOrigin - 20*mathU +    10          *mathV, c1);
    piPlane->GetParam(mathOrigin - 10*mathU + (10+10*sqrt(3))*mathV, c2);
    
    // ----------- Defines the curves of the profile
    const int nbPCurves = 5;
    CATPCurve *  aPCurves[nbPCurves];
    CATCrvLimits aLimits[nbPCurves];
    
    aPCurves[0]=  piGeomFactory->CreatePLine   (p1, p2, piPlane );
    aPCurves[0] ->GetLimits(aLimits[0]);
    aPCurves[1]=  piGeomFactory->CreatePCircle (10,        // radius
                                                c1,        // center
                                                CATPI/3,   // first limit  (may be reordered)
                                                3*CATPI/2, // second limit (may be reordered)
                                                piPlane);  // surface
    aPCurves[1] ->GetLimits(aLimits[1]);
    aPCurves[2]=  piGeomFactory->CreatePCircle (10, c2, 4*CATPI/3, 3*CATPI/2, piPlane);
    aPCurves[2] ->GetLimits(aLimits[2]);
    
    aPCurves[3]=  piGeomFactory->CreatePLine   (p3, p4, piPlane );
    aPCurves[3] ->GetLimits(aLimits[3]);
    
    aPCurves[4]=  piGeomFactory->CreatePLine   (p4, p1, piPlane );
    aPCurves[4] ->GetLimits(aLimits[4]);
    for (int i=0; i<nbPCurves; i++)
    {
      if (NULL==aPCurves[i])
      {
        ::CATCloseCGMContainer(piGeomFactory);
        return (1);
      }
    }				
  3. Defining the Orientations Of the Curves In the Profile

    CATTopSkin needs

    // Defines the orientations of the curves
    short aOrientations[nbPCurves];
    aOrientations[0] = 1;
    aOrientations[1] = 1;
    aOrientations[2] = 1;
    aOrientations[3] = 1;
    aOrientations[4] = 1;
    
    CATCrvParam low,high;
    CATMathPoint m1start, m1end, aPoints[2];
    
    // first checks the first two curves
    aLimits[0].GetExtremities(low,high);
    aPCurves[0]->Eval(low , CATCrvEvalCommand::EvalPoint, &m1start);
    aPCurves[0]->Eval(high, CATCrvEvalCommand::EvalPoint, &m1end);
    
    aLimits[1].GetExtremities(low,high);
    aPCurves[1]->Eval(low , CATCrvEvalCommand::EvalPoint, &(aPoints[0]));
    aPCurves[1]->Eval(high, CATCrvEvalCommand::EvalPoint, &(aPoints[1]));
    
    int index1, index2;
    double d1 = m1start.DistanceTo(aPoints, // array of 2 points
                                   2,       // count of points of aPoints
                                   index1); // index (beginning at 0) of a point of aPoints
                                            // closest to this
    double d2 =   m1end.DistanceTo(aPoints,2,index2);
    
    if (d1 < d2 )     // the orientation of the first curve is inverted
    {
      aOrientations[0] = -1; 
      if (1==index1) aOrientations[1] = -1; // inverts the orientation of the second curve 
    }
    else
    {
      if (1==index2) aOrientations[1]= -1;  // inverts the orientation of the second curve
    }
    
    // Checks now the other curves
    for (i=2;i<5;i++)
    { 
      m1end   = aPoints [1];
      if (-1==aOrientations[i-1]) m1end   = aPoints [0];
    
      aLimits[i].GetExtremities(low,high);
      aPCurves[i]->Eval(low , CATCrvEvalCommand::EvalPoint, &(aPoints[0]));
      aPCurves[i]->Eval(high, CATCrvEvalCommand::EvalPoint, &(aPoints[1]));
    
      d2 =   m1end.DistanceTo(aPoints,2,index2);
      if (1==index2) aOrientations[i]= -1;
    }						

    The principle of the algorithm is

    The use of the CATMathPoint::DistanceTo method to compute the minimum distance between a point (this calling DistanceTo ) and an array of points: the method retrieves the index (beginning at 0) in the input array of the point realizing the minimum distance.

  4. Using CATTopSkin

    The geometry being created, the CATTopSkin can now be invoked according to the general scheme that:

    // Creates the operator
    // first defines an open configuration for the operator
    CATSoftwareConfiguration * pConfig = new CATSoftwareConfiguration();
    // defines the data of the operator: configuration + journal
    CATTopData topdata(pConfig,NULL);
    // now creates the operator
    CATTopSkin * pSkinOp = ::CATCreateTopSkin (piGeomFactory,
                                               &topdata,
                                               piPlane,
    		                           nbPCurves, 
    					   aPCurves,
                                               aLimits,
                                               aOrientations);
    if (NULL==pSkinOp)
    {
          ::CATCloseCGMContainer(piGeomFactory);
          return (1);
    }
    
    // Runs
    pSkinOp->Run();
    
    // Gets the resulting body
    CATBody * piSkinBody = pSkinOp->GetResult();
    if (NULL==piSkinBody)
    {
          ::CATCloseCGMContainer(piGeomFactory);
          return (1);
    }
        
    // Deletes the operator
    delete pSkinOp;
    pSkinOp = NULL;								

    The operator configuration is the level of software you want to use to run this operator. By default, define an open configuration as in this use case to run with the current level. Moreover here, the pointer to the journal is set to NULL in the operator data. So that the journal is not filled. 

    The configuration must be released after use. Here, it is released after the call to the last operator.

[Top]

Creating a Prism

The created SkinBody is now extruded to create a prism with CATTopPrism. To use it:

CATMathDirection   zDir(0., 0., 1.);
double startOffset = 10.;
double endOffset   = 30.;
CATTopPrism       *pPrismOp = ::CATTopCreatePrism (piGeomFactory,
                                                   &topdata
                                                   piSkinBody,
                                                   &zDir,
                                                   startOffset,
                                                   endOffset);

if (NULL==pPrismOp)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Runs
pPrismOp->Run();
    
// Gets the resulting body 
CATBody * piMainBody1=NULL;
piMainBody1 = pPrismOp->GetResult();
if (NULL==piMainBody1)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Deletes the operator
delete pPrismOp;
pPrismOp = NULL;

As the body to extrude is a skin body, MainBody1 is a volume body. If the body to extrude were a wire body, the result would be a skin body. Other types of prism operations can be defined, especially "until" operations: the limits of the prism are reached when encountering another body. This case is detailed in the CAATopJournal use case [1].

[Top]

Creating a Box and a Cylinder Primitives

This section illustrates the use of CATSolidPrimitive operators: no run is called to do the operation, that is done at the operator creation.

To create a box, use CATSolidCuboid:

CATMathPoint vO( -2., 2., 28.),  vOI(-2., 15., 28.), 
             vOJ(-15., 2., 28.),  vOK(-2., 2., 35.);
	
CATSolidCuboid *pCuboidOp = ::CATCreateSolidCuboid( piGeomFactory, &topdata, vO, vOI, vOJ, vOK);

if (NULL==pCuboidOp)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Gets the result (the operator is run at is creation)
CATBody *piCuboidBody=NULL;
piCuboidBody = pCuboidOp->GetResult();
if (NULL==piCuboidBody)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Deletes the operator
delete pCuboidOp;
pCuboidOp = NULL;

To create a cylinder, use CATSolidCylinder :

CATMathPoint axisStart ( -20,  10,  20 ),  axisEnd( -20,  10, 32 );
double       radius = 4.0; 
	
CATSolidCylinder *pCylinderOp = ::CATCreateSolidCylinder(piGeomFactory, 
                                                         &topdata
                                                         axisStart, 
                                                         axisEnd, 
                                                         radius);
if (NULL==pCylinderOp)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Gets the resulting body (the operator is run at its creation)
CATBody *piCylinderBody = NULL;
piCylinderBody = pCylinderOp->GetResult();
if (NULL==piCylinderBody)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Deletes the operator
delete pCylinderOp;
pCylinderOp = NULL;

See the CAATopJournal use case [1] to see how to create a skin body cylinder.

[Top]

Adding and Subtracting

To use CATDynBoolean:

This code also shows an example of use the CATICGMContainer::Remove method to suppress the no more used bodies: the RemoveDependancies option declares that not only the body, but also its domains, cells and geometry are removed, except if they were used by other CGM entities.

CATDynBoolean* pBoolOp = ::CATCreateDynBoolean (piGeomFactory, 
                                                &topdata,                     
                                                CATBoolUnion, 
                                                piMainBody1, 
                                                piCuboidBody);
if (NULL==pBoolOp)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Runs
pBoolOp->Run();

// Gets the resulting body 
CATBody * piMainBody2 = NULL;
piMainBody2 = pBoolOp->GetResult();
if (NULL==piMainBody2)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Deletes the operator
delete pBoolOp;
pBoolOp = NULL;
// Asks the factory to proceed the deletion (CATBody)
piGeomFactory->Remove(piCuboidBody, CATICGMContainer::RemoveDependancies);  
piCuboidBody = NULL;
piGeomFactory->Remove(piMainBody1, CATICGMContainer::RemoveDependancies);
piMainBody1 = NULL;     

The same is done for a Boolean subtract: the option CATBoolRemoval is used. MainBody3 contains the result of all the operations, while the no-more used bodies (MainBody2 , CylinderBody) are removed.

pBoolOp = ::CATCreateDynBoolean (piGeomFactory,
                                 &topdata, 
                                 CATBoolRemoval, 
			         piMainBody2, 
			         piCylinderBody);
if (NULL==pBoolOp)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Runs
pBoolOp->Run();

// Gets the resulting body
CATBody * piMainBody3 = NULL;
piMainBody3 = pBoolOp->GetResult();
if (NULL==piMainBody3)
{
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
}

// Deletes the operator
delete pBoolOp;
pBoolOp = NULL;

piGeomFactory->Remove(piCylinderBody);
piCylinderBody = NULL;
piGeomFactory->Remove(piMainBody2);
piMainBody2 = NULL;    

[Top]

Filleting

First define the edges to fillet. These edges are the external boundary of the upper face of the prism after the two Boolean operations, in other words in our case, the face with 2 holes (the paths of the cylinder and the box). To retrieve them:

CATLISTP(CATCell) listC;
CATLISTP(CATCell) listFaces;
piMainBody3 ->GetAllCells(listFaces,  // the output list of cells
                          2 );        // the dimension of the retrieved cells (2 for faces)
int nbFaces=listFaces.Size();	
CATCell * piFace = NULL;

// ---------- Recovers the only face with 2 internal loops
for ( i=1;i<=nbFaces;i++)
{
	if ( 3== (listFaces[i]->GetNbDomains()) ) piFace = listFaces[i];
}

// ---------- and the loop with 5 edges
CATDomain * piLoop = NULL;	
CATLISTP(CATEdge) listEdges;	
int numberOfEdges;

if (NULL != piFace)
{
   for (i=1;i<=3;i++)
   {
	piLoop = piFace->GetDomain(i);
        piLoop->GetAllCells(listC, 1);      
	numberOfEdges = listC.Size();
	if (5==listC.Size())
	{
          for (int j=1;j<=numberOfEdges;j++)
		{listEdges.Append((CATEdge *)listC[j]);}
	}
   }
}
else // problem in the definition of the body
{
      ::CATCloseCGMContainer(piGeomFactory);
   return (2);
}

A filleting operation is defined by affecting (possibly variable) radius to edges:

// for a constant radius, only the first argument is useful
CATDynFilletRadius * pRadius = new 
                       CATDynFilletRadius(1.,    // radius value
                       NULL,  // the cell on which the radius is defined
                       NULL,  // The ratio of the edge length defining the point
                       NULL); // must be kept to NULL
if (NULL==pRadius)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}

CATLISTP(CATDynFilletRadius)	listRadius;		
listRadius.Append(pRadius);

// ribbon definition		
CATDynEdgeFilletRibbon * pRibbon = new CATDynEdgeFilletRibbon(listEdges, listRadius);
if (NULL==pRibbon)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}

pRibbon ->SetSegmentationMode(CATDynTrim);

The fillet operation can now be defined and run. To use it

Also deletes the no more used object (radius, ribbon) and removes the old body (MainBody3).

CATDynFillet * pFilletOp = CATCreateDynFillet(piGeomFactory,&topdata,piMainBody3);
if (NULL==pFilletOp)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}


// Appends the ribbon
pFilletOp ->Append(pRibbon);

// Runs
pFilletOp ->Run(); 

// Gets the resulting body
CATBody * piMainBody4 = NULL;
piMainBody4 = pFilletOp->GetResult();

if (NULL==piMainBody4)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}

// Deletes the operator
delete pFilletOp;
pFilletOp = NULL;

if (NULL != pRadius) delete pRadius;
pRadius = NULL;
if (NULL != pRibbon) delete pRibbon;
pRibbon = NULL;

piGeomFactory->Remove(piMainBody3);
piMainBody3 = NULL;

[Top]

Shelling

Take two offset bodies of one initial body. The shelling operation digs a volume by removing one offset body (internal) from the other one (external). Some faces can also be not offset: these faces are called openings. In the use case, the opening face is the bottom face of the prism: it is the unique face with five edges and one domain. The way to retrieve it is similar to the way used in the section Fillets. First void the list (RemoveAll), and remember that the list begins at 1!

listEdges.RemoveAll();
piFace = NULL;
for (i=1;i<=nbFaces;i++)
{
 if ( 1== (listFaces[i]->GetNbDomains()) ) 
 {
   piLoop = listFaces[i]->GetDomain(1);
   piLoop ->GetAllCells(listC, 1);      
   numberOfEdges = listC.Size();
   if (5==listC.Size())
   {
     piFace=listFaces[i];
   }
 }
}

if (NULL == piFace) return (3);

The shelling operation can now be defined and run. To use it:

Also removes the old body (MainBody4).

CATDynShell* pShellOp = CATCreateDynShell (piGeomFactory,
                                           &topdata,      // the configuration and the journal
                                           piMainBody4,   // the body to shell
                                           -1.,   // first offset value (inside the body)
                                           0.);   // second offset value (initial body)
if (NULL==pShellOp)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}	
// Sets the opening faces
CATLISTP(CATFace) openings;
openings.Append((CATFace*)piFace);
pShellOp-> Append(openings); 
    
// Runs
pShellOp->Run();

// Gets the resulting body
CATBody * piMainBody5 = NULL;
piMainBody5 = pShellOp->GetResult();
if (NULL==piMainBody5)
{
  ::CATCloseCGMContainer(piGeomFactory);
  return (1);
}

// Deletes the operator
delete pShellOp;
pShellOp = NULL;

piGeomFactory->Remove(piMainBody4,CATICGMContainer::RemoveDependancies);
piMainBody4 = NULL;

[Top]

Computing the Volume

CATDynMassProperties3D is an operator to analyze a body. Here we ask for the computation of the volume of the body, result of all the operations. To use it:

CATDynMassProperties3D *pPropOp = CATDynCreateMassProperties3D (piMainBody5);
if (NULL != pPropOp)
{
   cout << "Volume of the final object" << pPropOp->GetVolume() << endl;
   delete pPropOp;
   pPropOp = NULL;
}

[Top]

Writing the Model and Closing the Factory

Before ending, we must first release the software configuration.

// Releases the configuration
    pConfig->Release();

 

To save the model in a file, the ::CATSaveCGMContainer global function is used. Notice that in the use case, the save is conditioned by an input parameter representing the file inside which the model must be saved.

The use case ends with the closure of the geometry factory, done by the ::CATCloseCGMContainer global function.

 if(1==toStore)
 {
#ifdef _WINDOWS_SOURCE
   ofstream filetowrite(pfileName, ios::binary ) ;
#else
   ofstream filetowrite(pfileName,ios::out,filebuf::openprot) ;
#endif

   ::CATSaveCGMContainer(piGeomFactory,filetowrite);
   filetowrite.close();
 }	

 //
 // Closes the container
 //
	
 ::CATCloseCGMContainer(piGeomFactory);

[Top]


In Short

This use case creates a body by chaining several types of topological operations, such Boolean, Filleting or Shelling, and primitive creation. The journal is not detailed.

[Top]


References

[1] How to Use the Topological Journal
[2] Topology Concepts
[3] The CGM Topological Model
[4] The CGM Journal
[5] The Geometric Operators
[6] The Boolean Operators
[7] The Objects of CATIA Geometric Modeler
[8] Building and Launching a CAA V5 Use Case
[9] Basic Topological Operators
[Top]

History

Version: 1.1 [Oct 2000] Operator configuration
Version: 1 [May 2000] Document created
[Top]

Copyright © 2000, Dassault Systèmes. All rights reserved.