Operating on Parasolid Entities

When performing operations that modify Parasolid entities, it is necessary to keep the HOOPS representation of the model synchronized with its Parasolid representation. Because such operations are typically initiated by selecting (hit-testing) HOOPS geometric objects, it is necessary to determine what Parasolid entity(s) correspond with the selected HOOPS entities, and vice versa. The HOOPS/Parasolid integration includes routines which support this requirement.

HOOPS geometric entities are identified by HOOPS ‘keys’. Keys are renumbered within the integration code to have Parasolid-specific values after being created as a result of the Parasolid PK_TOPOL_render_XXX routines. Recall that Parasolid entities are referred to by a Parasolid ‘tagID’. Given a HOOPS key, the HP_Compute_TagIDfunction will return the Parasolid tagID for the Parasolid entity that the specified HOOPS geometric entity is associated with. Conversely, given a Parasolid tagID, the HP_Compute_Geometry_Keys function will return all the HOOPS keys for the tessellated geometry or the HOOPS segment associated with the Parasolid entity. (Recall that a Parasolid entity might be represented by more than one HOOPS geometric entity.)

Accessing a Parasolid Entity

Selection on the HOOPS scene returns the key(s) of HOOPS geometry, requiring us to find the Parasolid entity that the selected HOOPS geometry is associated with. If we wanted to access the Parasolid ‘body’ entity given the key of a selected HOOPS geometric primitive, we would call:

PK_ENTITY_t entity = HP_Compute_TagID(key, PK_CLASS_body);

We can now use the tagID of the Parasolid entity to perform Parasolid operations, or to access the HOOPS geometric primitives which represent the Parasolid entity.

Highlighting a Parasolid Entity

Highlighting a Parasolid entity involves performing a selection on the HOOPS scene and highlighting the HOOPS geometry associated with the Parasolid entity. After identifying the Parasolid entity associated with the selected HOOPS geometry (described in the previous section), we need to access the HOOPS geometry or segment which represents the entity.

Recall that Parasolid entities are composed of PK_FACE and PK_EDGE entities, whose tessellated representations are sent to HOOPS via the HP_Render_Entity call. HP_Render_Entity encapsulates calls to the PK_TOPOL_render_facet and PK_TOPOL_render_line routines. In the following code example, we compute all the HOOPS geometry that is associated with the ‘edges’ and ‘faces’ of the selected Parasolid entity. This may also be referred to as “Parasolid body-level selection”:

HC_KEY *keys;
int count;

// compute all the HOOPS geometry associated with the Parasolid
// entity

count = HP_Compute_Geometry_Key_Count (entity, (char*) "edges, faces");
keys = (HC_KEY *)malloc(count * sizeof(HC_KEY));
count = HP_Compute_Geometry_Keys(entity, count, keys, "edges, faces");

This returns the number of HOOPS keys associated with the given Parasolid entity, and places their values into the ‘keys’ array. Alternately, the call to HP_Compute_Geometry_Key_Count could be omitted, and HP_Compute_Geometry_Keys could be called using the ‘max_count’ parameter, which specifies the maximum number of keys desired. In the following code example, the last argument in the function call instructs HOOPS to limit the number of returned keys to 40000:

HC_KEY keys[40000];
int count;

// compute the HOOPS geometry associatedd with the Parasolid
// entity, limiting
// the number of entities returned to 40000
count = HP_Compute_Geometry_Keys(entity, 40000, keys, "edges, faces");

If the mapping option “create body segments” is true and we are dealing with a Parasolid ‘body’, we know that there is a unique HOOPS segment which contains all the geometry associated with a Parasolid ‘body’ entity. Therefore, in this case we can simply ask for the key of the HOOPS segment associated with the body and highlight the entire segment, rather than individually compute and highlight each geometric primitive:

// there is only one primary segment associated with a body HC_KEY key; int count;

// compute the key of the unique HOOPS segment which contains // all the geometry // associated with the Parasolid body entity count = HP_Compute_Geometry_Keys(entity, 1, key, “bodies”,);

The HOOPS primitives can be highlighted by moving them from their current HOOPS segment into a segment with different rendering attributes (color, linestyle, etc…). A HOOPS segment can be highlighted by directly modifying its rendering attributes. Using the HOOPS/MVO HSelectionSet class as a base, we can utilize defined geometry and segment selection (highlight) methods to create a Parasolid-application-specific SelectionSet class and Select function as follows:

class HSSelectionSet : public HSelectionSet
{
private:
        PK_CLASS_t m_SelectLevel;
        HVarray *m_pSolidSelection;

public:
 HSSelectionSet(HBaseView* view);
 ~HSSelectionSet();

  // overloaded virtuals
  void Init();
 void Select(long key, char* segpath, long include_key, long includer_key);

 ....

  void SetSelectLevel(PK_CLASS_t level) { m_SelectLevel = level; }
 const PK_CLASS_t GetSelectLevel() { return (m_SelectLevel);}

};

This derived class will not only support body level selection, but will also support several ‘selection levels’ as indicated by the m_SelectLevel variable. It also adds another variable of type HVarray, in order to store the list of selected Parasolid entities; the base class only deals with storing the list of selected HOOPS primitives that are associated with the selected Parasolid entity. Example:

// select (highlight) the HOOPS geometric primitives or the primary
// segment associated with the Parasolidd entity denoted by
// m_SelectLevel
void HSSelectionSet::Select(long key, char* segpath, long include_key, long includer_key)
{
        PK_ENTITY_t entity = 0;
        HC_KEY keys[40000];
        int count = 0, c;

        // if the selection level is 'body' and there is a unique HOOPS
        // segment for each body, simply ask for the key of the HOOPS
        // segment associated with the body and select the entire segment;
        // otherwise, compute all the specific HOOPS geometric primitives
        // associated with the Parasolidid entity.Note that we call the base
        // class' Select method which supports selection (highlighting) of
        // either a HOOPS segment or individual HOOPS geometric primitives

        entity = HP_Compute_TagID(key, m_SelectLevel);
        if (entity > 0)
        {
                // add the selected Parasolid entity to the derived class'
                // selection list
                m_pSolidSelection->Add((long)entity);
                if (m_SelectLevel == PK_CLASS_body) // && body segments is true
                        count = HP_Compute_Geometry_Keys(entity, 1, keys, "bodies");
                else if (m_SelectLevel == PK_CLASS_face)
                        count = HP_Compute_Geometry_Keys(entity, 40000, keys, "faces");
                else if (m_SelectLevel == PK_CLASS_edge)
                        count = HP_Compute_Geometry_Keys(entity, 40000, keys, "edges");
        }

        assert(count < 40000);

        if (count)
        {
                for (c = 0; c < count; c++)
                {
                        //call base class for each key
                        HSelectionSet::Select(keys[c], segpath, 0, 0);<br>
                }
        } else
                HSelectionSet::Select(key, segpath, include_key, includer_key);

}

Performing a Boolean Operation

Identification, and selection (highlighting) of the HOOPS geometry associated with a particular Parasolid entity is usually an intermediate step in performing a useful modeling operation. This section provides an example of how to perform a Boolean operation.

The Parasolid Boolean function, PK_BODY_Boolean_2, operates at the level of a Parasolid ‘body’. It requires the definition of a ‘target’ body entity, the ‘tool’ body entities that will operate on the target, and a description of the Boolean operation to be performed. After the function is called, the HP_Render_Entity function needs to be called to send the tessellated representation of the final target body to HOOPS. The syntax of the function is as follows:

<error_code> = PK_BODY_Boolean_2(<target>, <number_of_tools>, <tools>, <options>, <tracking>, <results>);

The result of the Boolean operation is returned as an array of bodies. We can create a utility function called parasolid_Boolean, which encapsulates the Parasolid Boolean function call and associated rendering function call as follows:

// calls the appropriate Boolean function based on the target and
// tools and renderers the resulting bodies
void parasolid_Boolean(PK_Boolean_function_t operation, PK_BODY_t target, int number_of_tools, PK_BODY_t *tools)
{
        PK_ERROR_code_t error_code;
        PK_BODY_Boolean_o_t options;
        PK_TOPOL_track_r_t *tracking;
        PK_boolean_r_t *results;
        PK_PARTITION_t partition;

        assert(target);
        assert(number_of_tools > 0);
        PK_BODY_Boolean_o_m(options);
        options.function = operation;

        // need to ensure that the tools are in the same partition as the
        // target
        PK_ENTITY_ask_partition(target, partition);

        for (int i = 0; i < number_of_tools; i++)
                PK_BODY_change_partition(tools[i], partition);

        // perform the Boolean operation
        error_code = PK_BODY_Boolean_2(target, number_of_tools, tools, &options, &tracking, &results);

        if ( report_ifail(error_code,0) != 0){
                char error_message[256];
                sprintf(error_message, "failed during Boolean operation \n Error Code : %d", error_code);
                MessageBox(NULL, error_message, 0, MB_OK);
        }

        // render bodies
        HP_Render_Entities(results->num_bodies, results->bodies);

        if (results->num_bodies > 0)
                PK_MEMORY_free(results->bodies);
}

Recall that the HOOPS geometric primitives that are created as a result of the HP_Render_Entity function will be placed into the currently open HOOPS segment, requiring us to open a segment first. Additionally, given a list of selected HOOPS geometry, we need to determine the corresponding Parasolid ‘target’ and ‘tool’ entities to be passed to the parasolid_Boolean function. The following function is a method of the HParaView class (HSolidView.h and HSolidView.cpp). It performs the following operations:

  • Walks the solid selection list object HSSelectionSet to locate the ‘target’ and ‘tool’ bodies to be used in the Boolean operation. The first entity in the solid selection list is the ‘target’, and the remaining entities are the ‘tools’.

  • Deselects all the items in the selection list. The DeSelectAll() function call will clear out the solid selection list that was added to the derived class, and also call the base class’ DeSelectAll function which deselects all the associated HOOPS primitives that have been selected/highlighted.

  • Deletes the HOOPS primitives associated with the target and tool bodies from the HOOPS database. This is necessary since the Parasolid ‘target’ and ‘tool’ bodies will be removed from the database after the Boolean, and a new body will be created and rendered. Therefore, we need to remove the HOOPS geometry that represented the target and tool bodies. We can use HP_Delete_Entity_Geometry to delete the HOOPS geometry associated with the Parasolid entities.

  • Opens a HOOPS segment in the model object and calls the parasolid_Boolean function previously discussed.

Example:

// perform a Boolean operation on selected items
void HSolidView::BooleanOperation(unsigned long operation)
{
        int j, count = 0;
        int number_of_tools = 0;

        PK_BODY_t target = 0;
        PK_BODY_t tools[MAX_BOOLEAN_TOOLS];

        int numSelections;
        HSSelectionSet* selection = 0;

        // get ptr to selection object
        selection = (HSSelectionSet *)GetSelection();
        assert(selection);

        // get the number of selected items
        numSelections = selection->GetSolidListSize();
        number_of_tools = numSelections - 1;

        // must have at least 2 selected items for a Boolean operation
        if (numSelections < 2)
                return;

        target = (PK_BODY_t)selection->GetAtSolidEntity(0);
        for (j = 0; j < number_of_tools && number_of_tools <MAX_BOOLEAN_TOOLS; j++)
        {
                tools[j] = (PK_BODY_t)selection->GetAtSolidEntity(j+1);<br>
        }

        // deselect all the HOOPS primitives and GM entities before deleting
        selection->DeSelectAll();

        // now delete ALL of the geometry associated with target
        HP_Delete_Entity_Geometry(1, &target);
        HP_Delete_Entity_Geometry(number_of_tools, tools);

        // Perform the Boolean operation and render the resulting entities</font><br>
        // Put each entity in a separate segment so that we can easily apply<br>
        // transformations to separately created entities.
        // However, before rendering bodies, check if this view is in
        // 'merge faces' mode or not, and ensure that the rendering option is
        // set accordingly. This is necessary because the HOOPS/GM Rendering
        // Options are procedural; they set the current state of the bridge,
        // and we need to make sure that all parts in each model object are
        // rendered in a consistent mode
        if (m_optimize_shells)
                HP_Set_Rendering_Options("merge faces");<br>
        else
                HP_Set_Rendering_Options("no merge faces");

        HC_Open_Segment_By_Key(GetModel()->GetModelKey());
        HC_Open_Segment("");
        parasolid_Boolean(operation, target, number_of_tools, tools);
        HC_Close_Segment();
        HC_Close_Segment();

        m_bUpdateCPGeom = true;

        Update();
}

Many other Parasolid modeling operations that delete and create new Parasolid entities would require a similar procedure of identifying and deleting the HOOPS geometric primitives associated with the Parasolid entities. In any case, the process outlined above must be followed through in order to synchronize the HOOPS database’s segments and primitives with the Parasolid kernel.

Other Parasolid operations on Parasolid entities may not require deletion of corresponding HOOPS geometry, but rather they may require that a HOOPS attribute be modified to keep the two systems in sync. For example, a transformation to a Parasolid BODY (using PK_BODY_transform) needs to be reflected in HOOPS by setting the corresponding HOOPS modeling matrix in the segment containing the geometric representation of the BODY. Such an operation is handled in the HOOPS/Parasolid Reference Application’s HPOpObjectTranslate class. (HPOpObjectTranslate.h and HPOpObjectTranslate.cpp).