Geometric Modeler

Topology

How to Use the Topological Journal

Reading data and creating the journal of a sequence of topological operations
Use Case

Abstract

The journal describes the topological modifications brought to the input bodies to get the resulting body during a topological operation. The journal is filled under request by the topological operators.

The use case proposes a way to define a topological operator by chaining a sequence of topological operators. In this sequence, data necessary to operations are read in the journal of previous operations. The journal of the global operation is filled.


What You Will Learn With This Use Case

In this use case, you learn how to create a new topological operator (CAATopStiffner) by chaining several CGM topological operators. In the sequence,

Meanwhile, the use of the some topological operators is detailed such as:

See "Overview of the Topological Operators" [3] to have the general scheme of the topological operators and other use examples.

[Top]

The Principle

A topological operator operates on topological objects to create new topological objects. Most of the time, these topological objects are bodies (a body is a set of connected (or not) volumes, faces, edges and vertices [1]). A topological operator does never modify the input bodies: the resulting body is a new one, but it can share cells with the input bodies, if these cells are not touched by the operation. This is called the smart concept [2]. On request, the operator can describe the way to go from the initial objects to the resulting body. This information is then put by each operator into a topological journal.

The topological journal [4] records the creation, modification and deletion of the faces, free edges and free vertices of topological objects. A free edge is an edge bounding at most one face, and a free vertex is a vertex bounding at most one edge. In fact, it is sufficient to follow the modifications of these cells to know how the whole body is modified. The journal is attached to any topological or geometric operator that operates on topological objects.

This journal is transient. You have to create it before its use and delete it when you have finished.

As said, each topological operator is able to write the journal corresponding to its operation. So that the journal of the new operator is the concatenation of the journals of each called CGM operator, as demonstrated in the use case.

[Top]

The CAATopJournal Use Case

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

[Top]

What Does CAATopJournal Do

The use case defines a new topological operator CAATopStiffener, that follows the general scheme of the topological operators:

This operator defines a stiffener between two thin cylinder bodies ("wings") as displayed on Fig.1.

Fig. 1: The Resulting Body
  • A rectangular SkinBody is extruded along the z direction to create a prism until FirstCylinderBody and SecondCylinderBody are reached
  • From the journal of this operation, the large lateral faces of the prim are retrieved. On these faces, holes could be created, that are only sketched here by circles to lighten the presentation
  • From the journal, the edges of the intersection between these faces and FirstCylinderBody are also retrieved
  • These edges are filleted in a single operation
  • The journal corresponding to this sequence of operations is filled.

[Top]

How to Launch CAATopJournal

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

If you simply type CAATopJournal 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 CAATopJournal e:\ExJournal.NCGM

With UNIX CAATopJournal /u/ExJournal.NCGM

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

 

[Top]

Where to Find the CAATeopJournal Code

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

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

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

This main uses the new operator class CAATopStiffener, which header is located in the ProtectedInterfaces directory of the CAATopologicalOperators.edu framework, and which source is located in the CAATopOperator.m module of the CAATopologicalOperators.edu framework:

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

[Top]

Step-by-Step

The CAATopStiffener header declares the new class, the corresponding code implements it and CAATopJournal.cpp is a main to run the new operator.

The use case is divided into the following steps:

[Top]

CAATopStiffener: a New Class

We first look at the header of the new class.

 class ExportedByCAATopOperator CAATopStiffener 
{
public:
// deletes
   virtual ~CAATopStiffener();
  
// constructs
  CAATopStiffener (CATGeoFactory     * iFactory,
                   CATTopData        * iData,
                   CATBody           * iFirstLimitBody,
                   CATBody           * iSecondLimitBody,
                   CATBody           * iSkinBody,
                   CATMathVector       iDirection,
                   CATCGMJournalList * iJournal=NULL);

// runs
  int Run();
 
// gets the result
  CATBody * GetResult() ;

// data
private : 
  CATGeoFactory     * _piGeomFactory;        // the factory
  CATBody           * _piFirstLimitBody;     // the first relimiting surface
  CATBody           * _piSecondLimitBody;    // the second relimiting surface
  CATBody           * _piSkinBody ;          // the profile (containing an open shell)
  CATMathVector       _direction;            // the stiffener direction
  CATTopData        * _pData;                // the journal and configuration
  CATBody           * _piResultingBody ;     // the resulting body 
};

CAATopStiffener uses the general scheme of the topological operators (create, Run, GetResult, delete), but it does not derive from CATTopOperator: remember that you must not derive from any CGM operator, as stated by the U1 status of this class. As for a CGM operator also, the journal must be allocated by the caller in order to be filled by the called operator: in fact, if the corresponding pointer is NULL inside the operator data _pData, the operator does not fill the journal.

The private data contains the necessary data to run the operator such as the direction of the extrusion, the pointer to the limiting bodies, the pointer to the journal or the pointer to the resulting body.

In the use case, the operator does not have any SetXxxx method that tunes it. But one can easily imagine a SetMeanDirection method, that computes the normal to the mean plane of SkinBody to define the extrusion direction for example.

The constructor simply fills in the private data of the class, except the resulting body, that will be created in the Run method.

CAATopStiffener::CAATopStiffener (CATGeoFactory     * iFactory,
                                  CATTopData        * iData,
                                  CATBody           * iFirstLimitBody,
                                  CATBody           * iSecondLimitBody,
                                  CATBody           * iSkinProfile,
                                  CATMathVector       iDirection,
                                  CATCGMJournalList * iJournal)
{
  _piGeomFactory    = iFactory;
  _piFirstLimitBody = iFirstLimitBody;
  _piSecondLimitBody= iSecondLimitBody;
  _piSkinBody       = iSkinProfile;
  _direction        = iDirection;
  _pData            = iData;
  _piResultingBody  = NULL;
}

The GetResult method returns the pointer to the created body.

CATBody * CAATopStiffener::GetResult()  
{
  CATBody * piReturned = _piResultingBody;
  _piResultingBody = NULL;    // GetResult must only be called once
  return (piReturned);
}

Notice that once read, the life cycle of the body is taken into account by the caller: the caller must remove it from the factory (CATICGMContainer::Remove) if it does not want to keep it. As any topological operator, GetResult must be only called once.

The destructor removes the created body, if it is created and never retrieved:

CAATopStiffener::~CAATopStiffener()
{
  // if the resulting body is created, and is never retrieved (GetResult), deletes it
  if(NULL != _piResultingBody) _piGeomFactory->Remove(_piResultingBody, 
                                                      CATICGMContainer::RemoveDependancies);
  _piResultingBody  = NULL;
  _piGeomFactory    = NULL;
  _piFirstLimitBody = NULL;
  _piSecondLimitBody= NULL;
  _piSkinBody       = NULL;
  _pData            = NULL;
}

We can now concentrate on the important part of the operator: the Run method, that performs the following:

  1. Testing the Inputs
  2. Creating a Prism (CATTopPrism) With "Until"Limits
  3. Searching For the Long Side of the Profile
  4. Searching Inside the Journal For the Bottom Face of the Prism
  5. Creating a Circle on the Underlying Surface of the Face
  6. Searching Inside the Journal For the Face of the Upper Wing
  7. Filleting (CATDynFillet)
  8. Returning the Journal Operator

[Top]

  1. Testing the Inputs

    These inputs have been set by the constructor. One just checks that

      // ---------- Avoids to run twice
      CATBody * piResultingBody = _piResultingBody;
      if (NULL != piResultingBody) return(2);
      
      // ---------- Tests the null pointers
      //
      CATGeoFactory * piGeomFactory = _piGeomFactory;
      if (NULL == piGeomFactory ) return (1);
    
      // ---------- First limit <> Second Limit
      //
      CATBody *   piFirstLimitBody =_piFirstLimitBody;
      if (NULL == piFirstLimitBody) return (1);
      CATBody *   piSecondLimitBody =_piSecondLimitBody;
      if (NULL == piSecondLimitBody) return (1);
      if (piFirstLimitBody == piSecondLimitBody) return (3); 
      
      // ---------- Tests whether the input profile body has one domain, containing one face
      //   
      // The skin body to extrude 
      CATBody * piSkinBody = _piSkinBody; 
      if (NULL == piSkinBody ) return (1);
      if (1!= (piSkinBody->GetNbDomains() ))  return (1) //one domain in the body
      CATDomain* piShell=piSkinBody->GetDomain(1);       //returns the domain
      if(NULL==piShell) return(1);
      if (2 != piShell->GetLowDimension()) return(4);    //the domain is made of faces (dim=2) 
      long nbCells = piShell->GetNbCellUses();           //count of faces
      if (1!=nbCells) { return(4);} 
      CATFace * piFace = (CATFace *) (piShell->GetCell(1)); // returns the face
      if (NULL == piFace) return(4);
  2. [Top]

  3. Creating a Prism With "Until" Limits

    A prism operator is first created with the ::CATCreateTopPrism global function.

     
      // --------- Creates the operator
      //
      double offset = 0.;
      CATMathDirection direction (_direction);
    
      // Journal and configuration
           // Constructs a topdata from the input
      CATTopData internalTopdata(*_pData);  
           // Gets the associated configuration              
      CATSoftwareConfiguration * pConfig = internalTopdata.GetSoftwareConfiguration(); 
           // To use it to create a journal that will be embedded in the created internalTopdata
      CATCGMJournalList* pJournal = new CATCGMJournalList(pConfig,NULL);
           // sets the journal for the internal operations
      internalTopdata.SetJournal(pJournal); 
                     
      // and now creates the operator
      CATTopPrism  *pPrismOp = ::CATCreateTopPrism (piGeomFactory,
                                                    &internalTopdata, 
                                                    piSkinBody,
                                                    &direction,
                                                    offset, // non significative: the limits are defined later
                                                    offset); // non significative: the limits are defined later
      if (NULL==pPrismOp) return (1);

    A specific journal is created inside the operator: in fact, this journal is needed by the algorithm of CAATopStiffener, as seen later, but not necessarily asked for by the caller. Moreover, this allows the operator to modify the input journal (if asked for) only when all its algorithm is done. The specific journal is allocated and passed to the ::CATCreateTopPrism global function within the data internalTopData. It is independent on the general input journal of the operator, which is stored in _pData at the CATopStiffener creation. In fact, if the journal in _pData is not null, pJournal will be copied inside it to report all the orders of the operators chain.

    Notice that the journal is always versioned [6] by a software configuration, retrieved from the input CATTopData.

    The geometry factory, the skin body to extrude and the extrusion direction are set at the CAATopStiffener creation. In case of "until" limits, the start and end offset are not significative: the limits are in fact tuned by the SetLimit method.

      // --------- Sets options
      //
      // Sets the relimiting body
      pPrismOp->SetTrim(piFirstLimitBody);
    
      // Asks for the Boolean union with the relimiting body
      pPrismOp->SetOperation(CatBoolUnion);
    
      // Asks to also retrieve the result of the Booleean operation
      pPrismOp->SetResultMode(TRUE);
    
      // Sets the until limits: first limit
      pPrismOp->SetLimit(CatLimStart,           // first limit
                         CatLimUntil,           // until option
                         TRUE ,                 // same orientation as the direction
                         offset,                // non significative (until limits)
                         piFirstLimitBody,      // the limiting geometry: here a body
                         piFirstLimitBody,      // must be the same as the previous one
                         CatPropagSingle);      // keep to this value
      
      // Sets the until limits: second limit
      pPrismOp->SetLimit(CatLimEnd,
                         CatLimUntil, 
                         TRUE , 
                         offset, 
                         piSecondLimitBody, 
                         piSecondLimitBody, 
                         CatPropagSingle);

    The prism must be delimited on one of the limiting bodies (SetTrim), and there must be a Boolean union operation between the delimiting body and the computed prism (SetOperation). Moreover, we want to recover the result of this Boolean operation (SetResultMode set to TRUE). SetLimit must be called for each limit (CatLimStart, CATLimEnd), to ask an "until" limit (CatLimUntil) on each side. Notice that each limit can have a different behavior: one limit "until", the other defined by an offset from the profile. The prism operator can now be run.

      // --------- Runs
      CATTry
      {
        pPrismOp ->Run(); 
      }
      CATCatch(CATError,error)
      {
        cout << (error->GetNLSMessage()).ConvertToChar() << endl;
        rc = 20; 
      }
      CATEndTry
    
      if (rc!=0) CAAErrorTopStif1(rc,pJournal)
        
      // --------- Gets the resulting body 
      //
      CATBody * piMainBody1=NULL;
      piMainBody1 = pPrismOp->GetBooleanResult();
    
      // gets the prism before the union
      CATBody * piWithoutOperation = pPrismOp->GetResult();
    
      // gets the journal of the boolean operation
      CATCGMJournalList * pBooleanJournal = pPrismOp->GetBooleanJournal(); 
      
      if (NULL==piMainBody1 || NULL==pBooleanJournal || NULL==piWithoutOperation)
      {
        rc = 20;
        CAAErrorTopStif2(rc,pJournal,piGeomFactory,pPrismOp,piMainBody1,piWithoutOperation)
      }

    As the Run method can throw errors, these are caught by the macros CATTry, CATCatch, CATEndTry. The CAAErrorTopStifx macros are defined in the use case to clean the model in case of return: they free the allocations and delete the intermediate created bodies and geometry, but are not detailed in this article.

    The GetResult method returns the prism before its union with the limiting bodies, while the GetBooleanResult returns the body corresponding to the result after the union. In the same way, pJournal contains the modifications corresponding to the prism creation, whereas GetBooleanJournal returns a new created journal containing the modifications relative to the Boolean operation.

  4. [Top]

  5. Searching for the Long Side of the Profile

    In order to recover the faces on which circles have been drawn on Fig. 1, we first search the longest edge of the face of SkinBody.

    // Creates the boundary iterator on the edge of the initial face (of the skin to extrude)
      CATBoundaryIterator  *  pBoundaryIt =  piFace->CreateBoundaryIterator();
      if (NULL==pBoundaryIt)
      {
    	rc =1;
        CAAErrorTopStif2(...)  
      }
      // Computes the length of an edge
      CATSide side;
      CATCell*  piE1 = pBoundaryIt->Next(&side,NULL);
      if (NULL==piE1)
      {
        rc =1;
        CAAErrorTopStif3(...)
      }
      double l1= ((CATEdge * )piE1 )->CalcLength();
    
      // Computes the length of the next  edge
      CATCell*  piE2 = pBoundaryIt->Next(&side,NULL);
      if (NULL==piE2)
      {
        rc =1;
        CAAErrorTopStif3(...)
      }
      double l2=((CATEdge * )piE2)->CalcLength();
    
      // Defines the width and the height according to l1 and l2 values.  
      double height=0;
      double width=0;  
    
      CATCell * piHeight1 = NULL, *piHeight2 = NULL, *piWidth1 = NULL, *piWidth2 = NULL; 
      if ( l1 < l2 )
      {
        height    = l2;
        piHeight1 = piE2;
        piWidth1  = piE1;
        width     = l1;
        piWidth2  = pBoundaryIt->Next(&side,NULL);
        piHeight2 = pBoundaryIt->Next(&side,NULL);
    
      }
      else
      {
        height    = l1;
        piHeight1 = piE1;
        piWidth1  = piE2;
        width     = l2;
        piHeight2 = pBoundaryIt->Next(&side,NULL); 
        piWidth2  = pBoundaryIt->Next(&side,NULL);
      }
    
      delete pBoundaryIt;
      pBoundaryIt=NULL;

    This edge could also be put as an input argument, or with a SetXxx method to the operator! Here, this gives us the opportunity to use a CATBoundaryIterator class to retrieve the edges of a face.

    The iterator is created by the CATCell::CreateBoundaryIterator and skips from one boundary cell to the other one with the CATBoundaryIterator::Next method. The approximate length of an edge is computed with the CATEdge::CalcLength method. After comparing the lengths of the first two edges, we can easily deduce the two long sides, as the profile is rectangular.

    The written code is not generic: by assumption, the face is rectangular.

  6. [Top]

  7. Searching Inside the Journal For the Bottom Face of the Prism

    The topological journal is made of CATCGMJournalItem (unitary order) and CATCGMJournalList (list of items). Each item has a type such as

    To explore the topological journal, high level methods are provided, such as FindFirsts and FindLasts, that recursively scan the journal to retrieve:

    These methods can scan along a type of item, or several types (see the ThroughCreateAndModify value)

    Example: Let the following journal sequence: F1 -> F2 -> F3 -> F4 -> F5

    FindFirsts from F3 gives F1, and FindLasts from F3 gives F5.

      //  Retrieves all the objects created or modified from piHeight1
      //  first, in pJournal
      CATLISTP(CATGeometry) pFaces; 
      pJournal->FindLasts (piHeight1,pFaces,ThroughCreateAndModify);
    
      CATFace * piFromHeight1=NULL;
      int nbresult = pFaces.Size();
    
      // Retrieves the object that is a face.
      for (int i=1 ; (i <= nbresult) && (piFromHeight1 == NULL) ; i++) 
      {
    	 if (pFaces[i]->IsATypeOf(CATFaceType)) { piFromHeight1=(CATFace *)pFaces[i];}
      }
    
      //  now, in pBooleanJournal
      pFaces.RemoveAll();                       // voids the list before a new use
      pBooleanJournal->FindLasts (piFromHeight1,pFaces,ThroughModify);
      CATFace * piBooleanFromHeight1=NULL;
      nbresult = pFaces.Size();
    
      // Retrieves the object that is a face.
      for (i=1 ; (i <= nbresult) && (piBooleanFromHeight1 == NULL) ; i++) 
      {
    	 if (pFaces[i]->IsATypeOf(CATFaceType))	 { piBooleanFromHeight1=(CATFace *)pFaces[i];}
      }
    
      if (NULL==piBooleanFromHeight1) 
      {
        rc =21;
        CAAErrorTopStif3(...)
      }
    Fig. 2: The Journal and Boolean Journal of CATTopPrism
    • In the journal relative to the prism creation, FindLasts finds the cell FromHeight1 resulting from Height1. The ThroughCreateAndModify option indicates that the search is done among the creation and modification items. At this stage, the cells are already delimited on the limiting body, but not glued.
    • In the journal relative to the Boolean union, FindLasts finds the cell BooleanFromHeight1 from FromHeight1. This cell is the cell on which CAATopStifferner creates a circle. The ThroughModify option indicates that the search is done among the modification items only. After the Boolean operation, the prism and the limiting bodies are glued.
  8. [Top]

  9. Creating a Circle on the Underlying Surface of the Face
      // Gets the surface of the face  
      CATOrientation orientation;
      CATSurface * piSurfaceFromHeight = piBooleanFromHeight1->GetSurface(&orientation);
      if (NULL==piSurfaceFromHeight) 
      {
        rc =1;
        CAAErrorTopStif3(...)
      }
    
      // Estimates the center of the face  
      CATSurParam centerParam;
      piFromHeight1->EstimateCenterParam (centerParam);
    
      // Creates a circle on the surface
      CATPCircle * piPCircle1 = piGeomFactory -> CreatePCircle( height/3.,
                                                                centerParam, 
                                                                piSurfaceFromHeight);
      if (NULL==piPCircle1) 
      {
        rc =1;
        CAAErrorTopStif3(...)
      }

    The surface is retrieved with the CATFace::GetSurface method. The center of the circle is put at the "center" of the face, which is only an approximate point. The created circle is a CATPCircle, because it is a circle in the space of the surface.

    The way to define a circle on the other face is similar and not detailed here.

  10. [Top]

  11. Searching Inside the Journal For the Face of the Upper Wing

    On must first define one journal of the two operations: the prism creation (pJournal) and the Boolean operation (pBooleanJournal).

      // Copies in a single journal and deletes the unused body
      pBooleanJournal-> Duplicate(pJournal);
      piGeomFactory->Remove(piWithoutOperation,pJournal);
      piWithoutOperation=NULL;

    pBooleanJournal is duplicated in pJournal. pBooleanJournal will be directly deleted at the CATTopPrism deletion, while Journal now contains all the items of both operations. Then, the prism before union is removed with the Remove method of CATICGMContainer, with the journal as input: in this case all deletion items will be logged if necessary.

    Now, the face of the wing is searched for: this face has been modified by the Boolean operation: a hole is created. The word "modified" is a shorter way to tell that in the resulting body, a new face is created with a hole corresponding to the trace of the prism.

      CATLISTP(CATCell) listCells;
      piFirstLimitBody->GetAllCells(listCells,2);  // gets all the faces of FirstlimitBody
      nbCells = listCells.Size();
      
      CATFace * piFromBody1=NULL;
      int iok=0;
      for (i=1;(i <= nbCells)  ;i++)
      {    
        pFaces.RemoveAll();                          // voids the list
        pBooleanJournal -> FindLasts (listCells[i],pFaces,ThroughModify);
        nbresult = pFaces.Size();
        for (int j=1; (j <= nbresult) && (piFromBody1 == NULL) ; j++)
          
        {
          if (pFaces[j]->IsATypeOf(CATFaceType)     // searches for a face
              && pFaces[j] != listCells[i] )        // different from the initial one
          { 
            piFromBody1=(CATFace *)pFaces[j];
            iok = iok + 1;
          }
        } 
      }
      if (1!=iok) 
      {
        rc=30;
        CAAErrorTopStif5(...) 
      }
    
      // ---------- Deletes the operator
      delete pPrismOp;
      pPrismOp = NULL;
    
    Fig. 3: Use Of the Boolean Journal to Recover the Upper Wing
    • All the faces of FirstLimitBody are first got. For each face listCells[i], FindLasts searches for a cell resulting from a modification of listCells[i]. The resulting cells are put in the list pFaces, that is first cleaned (RemoveAll). FindLasts does never return a void pFaces list. If it does not find any solution, it returns the initial face listCells[i]. It is the reason why the returned solutions must be compared to the initial face.
  12. [Top]

  13. Filleting

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

      CATDynFilletRadius * pRadius = new CATDynFilletRadius(
              5.,    // radius value
              NULL,  // the cell on which the radius is defined (for variable radius)
              NULL,  // The ratio of the edge length defining the point (for variable radius)
              NULL); // must be kept to NULL
      if (NULL==pRadius)
      {
        rc=1;
        CAAErrorTopStif5(...) 
      }
       
      CATLISTP(CATDynFilletRadius)	listRadius;		
      listRadius.Append(pRadius);

    Now, the ribbon is defined.

      //---- first edge to fillet
      listCells.RemoveAll();
      piFromBody1->GetCommonBorderCells( piBooleanFromHeight1,     // the other face
                                         1,                        // must be put to 1
                                         listCells,                // the common cells
                                         1);                       // edge (dimension 1)     
      if (1!=listCells.Size() )
      {  
        rc=10;
        CAAErrorTopStif6(...) 
      }
      CATLISTP(CATEdge) listEdges;
      listEdges.Append((CATEdge *)(listCells[1]));
    
      //---- second edge to fillet
      listCells.RemoveAll();
      piFromBody1->GetCommonBorderCells( piBooleanFromHeight2,     // the other face
                                         1,                        // must be put to 1
                                         listCells,                // the common cells
                                         1);                       // edge (dimension 1)    
      if (1!=listCells.Size() )
      {  
        rc=10;
        CAAErrorTopStif6(...) 
      }
      listEdges.Append((CATEdge *)(listCells[1]));
    
      //---- the ribbon
      CATDynEdgeFilletRibbon * pRibbon = new CATDynEdgeFilletRibbon(listEdges, listRadius);
      if (NULL==pRibbon)
      {  
        rc=1;
        CAAErrorTopStif6(...) 
      }
    
      //---- trim option
      pRibbon ->SetSegmentationMode(CATDynTrim);

    The edges to fillet are common (GetCommonBorderCells) to the face with hole FromBody1 and the faces of the prism BooleanFromHeight1 and BooleanFromHeight2. These non connected edges are appended to the list used to define the ribbon. The CATDynFilletRibbon::SetSegmentationMode option indicates that the computed ribbon must be delimited on the main body.

    The CATDynFillet operator can now be created.

      // ----------- Creates the operator
      //
      CATDynFillet * pFilletOp = ::CATCreateDynFillet(piGeomFactory,&internalTopdata,piMainBody1,pJournal);
      if (NULL==pFilletOp)
      {  
        rc=1;
        CAAErrorTopStif7(...) 
      }
    
      //---- Appends the ribbon
      pFilletOp ->Append(pRibbon);
    
      //---- Runs
      CATTry
      {
        pFilletOp ->Run(); 
      }
      CATCatch(CATError,error)
      {
        cout << (error->GetNLSMessage()).ConvertToChar() << endl; 
        rc=20;
        CAAErrorTopStif7(...) 
      }
      CATEndTry
    
      //---- Gets the resulting body
      CATBody * piMainBody2  = pFilletOp->GetResult();
    
      if (NULL==piMainBody2)
      {  
        rc=1;
        CAAErrorTopStif7(...) 
      }
    
      //---- Deletes the operator
      delete pFilletOp;
      pFilletOp = NULL;
    
      if (NULL != pRadius) delete pRadius;
      pRadius = NULL;
      if (NULL != pRibbon) delete pRibbon;
      pRibbon = NULL;
    
      //---- Deletes the unused body
       piGeomFactory->Remove(piMainBody1,pJournal);
      _piResultingBody = piMainBody2;

    Notice the general scheme of the operator. To use it:

    pJournal is re-used here, so that the filleting operator directly puts its items inside it: at the end of the operation, pJournal contains the items of the prism creation, the Boolean operation and the filleting operation. In the same way, the GetResult method retrieves MainBody2, the body representing the result of the three operations. MainBody1 is now useless, and is removed by the factory: the items corresponding to this deletion are put in pJournal, as argument of the Remove method.

  14. [Top]

  15. Returning the Journal of the Operator
    // Fills the output journal if needed
      CATCGMJournalList * pDataJournal = NULL;
      pDataJournal=_pData->GetJournal();
      if (NULL!= pDataJournal)
      {
        pJournal->Duplicate(pDataJournal);  // duplicates the internal journal inside the input journal
      }
      delete pJournal;                      // deletes the internal journal

    As seen in step 2, pJournal was internally allocated to contain the items of the prism, Boolean union and filleting operations. If the caller of CAATopStiffener operator asks for the report of the modifications, the items must be copied inside the journal allocated by the caller, which address is stored in _pData. pJournal can then be deallocated.

[Top]

CAATopJournal: Use of the New Class

To use the new operator, one must go through the following steps:

[Top]

  1. Creating the Geometry Factory

    The geometry factory (CATGeoFactory) creates and manages all the CATICGMObject: it creates the points, curves, surfaces, and bodies, and removes 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);
  2. [Top]

  3. Creating the Limiting Bodies

    These bodies are defined as a cylinder skin body extruded along a direction. To create them, one must

    CATMathDirection z(0.,0.,1.);
    CATMathAxis axis1(CATMathPoint(0.,0.,-120.),
                      CATMathVector(0.,1.,0.),
                      z,
                      CATMathVector(1.,0.,0.));
    double      radius = 140.;
    double      axisStart= -30.;
    double      axisEnd  = 30.;
    double      angleStart = CATPIBY2-0.3;
    double      angleEnd = CATPIBY2+0.3;
    CATCylinder * piCylinder1 = piGeomFactory->CreateCylinder
                         (axis1,radius,axisStart,axisEnd,angleStart,angleEnd);
    	
    if (NULL == piCylinder1)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }

    A geometric object as a cylinder is created by the CATGeoFactory. axisStart and axisEnd define the limitation of the surface along the cylinder axis, angleStart and angleEnd define the limitation around the axis cylinder. The angle are measured in radians, CATPI and other related values are defined in CATMathConstant.h.

    // Creates a skin body
    // first defines an open configuration for the operator
    CATSoftwareConfiguration * pConfig = new CATSoftwareConfiguration();
    // defines the data of the operator: configuration + journal
    CATTopData topdata(pConfig,NULL);  // an open configuration and a NULL journal
    // defines the limits to take into account
    CATSurLimits limits;
    piCylinder1->GetLimits(limits);
    // now creates the operator
    CATTopSkin * pSkinOp = ::CATCreateTopSkin(piGeomFactory,&topdata,piCylinder1,&limits);
    if (NULL==pSkinOp)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    
    // Runs
    pSkinOp->Run();
    
    // Gets the resulting body
    CATBody * piFirstCylinderBody = pSkinOp->GetResult();
    if (NULL==piFirstCylinderBody)
    {
      ::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.

    CATTopSkin can create a skin body from a list a curves on surface, or directly on the boundaries of a surface. Here the surface is the limited cylinder. CATTopSkin is invoked according to the general scheme, that:

    The created SkinBody is now extruded to create a prism with CATTopPrism.

    CATCGMJournalList * pJournal = NULL;
    
    CATTopPrism  *pPrismOp = ::CATCreateTopPrism (piGeomFactory,
                                                  &topdata,
                                                  piFirstCylinderBody,
                                                  &z,
                                                  0.,               // limit1
                                                  2.,               // limit2
                                                  pJournal);
    if (NULL==pPrismOp)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    
    pPrismOp->Run();
    CATBody* piFirstLimitBody = pPrismOp->GetResult();
    if (NULL==piFirstLimitBody)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    delete pPrismOp;
    pPrismOp=NULL;

    Once again, the same steps are used, that:

    As the body to extrude is a skin body, FirstLimitBody 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 described, especially "until" operations: the limits of the prism are reached when encountering another body. This is detailed in the CAATopJournal section.

    The other limiting body is created in the same way, and this is not detailed here.

  4. [Top]

  5. Creating the Skin Body to Extrude

    The cylinder skin body was created using a surface, here the skin body is defined by giving a list of four segments on a geometric plane.

    CATPlane * piPlane = piGeomFactory->CreatePlane(CATMathOIJ);	// Geometrical plan
    if (NULL == piPlane)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    
    CATMathPoint mathOrigin;
    CATMathDirection mathU, mathV;
    
    // ----------- Retrieves the mathematical definition of the geometrical plane
    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;
    
    piPlane->GetParam(mathOrigin - 20*mathU - 5*mathV , p1);
    piPlane->GetParam(mathOrigin + 20*mathU - 5*mathV , p2);
    piPlane->GetParam(mathOrigin + 20*mathU + 5*mathV , p3);
    piPlane->GetParam(mathOrigin - 20*mathU + 5*mathV , p4);
    	
    // ----------- Defines the curves of the profile
    const int nbPCurves = 4;
    CATPCurve *  aPCurves[nbPCurves];
    CATCrvLimits aLimits[nbPCurves];
    short        aOrientations[nbPCurves];
    aPCurves[0]=  piGeomFactory->CreatePLine (p1, p2, piPlane );
    aPCurves[0] ->GetLimits(aLimits[0]);
    aPCurves[1]=  piGeomFactory->CreatePLine (p2, p3, piPlane);
    aPCurves[1] ->GetLimits(aLimits[1]);
    
    aPCurves[2]=  piGeomFactory->CreatePLine (p3, p4, piPlane);
    aPCurves[2] ->GetLimits(aLimits[2]);
    aPCurves[3]=  piGeomFactory->CreatePLine (p4, p1, piPlane );
    aPCurves[3] ->GetLimits(aLimits[3]);
    
    for (int i=0; i<nbPCurves; i++)
    {
      if (NULL==aPCurves[i])
      {
        ::CATCloseCGMContainer(piGeomFactory);
        return (1);
      }
    }
    
    // Defines the orientations of the curves
    // This is needed by the CATTopSkin
    // Notice that in a more general case (use of circle for example), 
    // you must test the start and end as in CAATopOverview.
    	
    aOrientations[0] = 1;
    aOrientations[1] = 1;
    aOrientations[2] = 1;
    aOrientations[3] = 1;

    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.

    CATTopSkin needs

    // Creates the operator
    pSkinOp = CATCreateTopSkin (piGeomFactory, 
                                &topdata,
                                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;	
  6. [Top]

  7. Running the New Operator
    //--- Creates the operator
    CAATopStiffener *pStiffOp = new CAATopStiffener (piGeomFactory,
                                                     &topdata,
                                                     piFirstLimitBody,
                                                     piSecondLimitBody,
                                                     piSkinBody,
                                                     z,
                                                     pJournal);
    if (NULL==pStiffOp)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    
    //--- Runs
    rc = pStiffOp->Run();
    if (NUL!=rc)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (rc);
    }
    
    //--- Gets the resulting body 
    CATBody * piMainBody1=NULL;
    piMainBody1 = pStiffOp->GetResult();
    if (NULL==piMainBody1)
    {
      ::CATCloseCGMContainer(piGeomFactory);
      return (1);
    }
    
    //--- Deletes the operator
    delete pStiffOp;
    pStiffOp = NULL;   
    // Releases the configuration
        pConfig->Release();  

    The new operator is used as a CGM operator with the steps that creates, runs, gets the result, and deletes.

    The software configuration is also released, because it is no more used.

  8. [Top]

  9. Writing the Model and Closing the Factory

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

    The sample 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

The journal follows the topological modification from the input bodies (that are never modified) to the output body. This journal is read to recover topological entities, that can be later used in other topological operations.

New operator classes can be developed, by chaining several topological operations. In this case, the corresponding journal is the concatenation of the journal of each operator. If an intermediate body is removed, this must be declared in the journal.

[Top]


References

[1] Topology Concepts
[2] The CGM Topological Model
[3] Overview of the Topological Operators
[4] The CGM Journal
[5] Building and Launching a CAA V5 Use Case
[6] The versioning of the 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.