This guide explains how to use the HOOPS/Parasolid classes to integrate the Parasolid Kernel Modeller with HOOPS/3dGS and HOOPS/MVO. It assumes familiarity with the Parasolid Kernel Modeller, HOOPS/3dGS and HOOPS/MVO.
The HOOPS/MVO Parasolid, source code, export library (for Windows)and libraries/dlls are located in the /dev_tools/hp_bridge subdirectories. The /dev_tools/hp_bridge/source directory includes the Microsoft Visual Studio Project files or necessary make files to enable rebuilding.
To startup Parasolid and the HOOPS/Parasolid Integration module, call HP_Init() in an application initialization routine. This function initializes the Parasolid kernel and registers the HOOPS-specific implementation of the Parasolid GO_Routines. All geometry rendered via Parasolid's PK_TOPOL_render_XXX routines will be mapped to corresponding segments and geometry in the HOOPS graphics database. (HOOPS does not need an explicit initialization routine, as it is automatically initialized during the first call of a HOOPS function.)
Note that the HP_Init() function requires a Schema path and Boolean value. The Schema path instructs Parasolid where to look for some utility files necessary to read in .x_t files. The schema files are located on the CD installation in the /parasolid/schema directory.
The Boolean should be set to false, unless you are working with Tech Soft 3D to test a new automatic update feature that keeps HOOPS and Parasolid continuously in synchronization.
void MyParaHoopsAppInit() { CString SchemaPath; SchemaPath =GetProfileString(registry,"PARASOLID_SCHEMA_PATH"); HP_Init(SchemaPath, PK_LOGICAL_false); }
To shutdown Parasolid as well as the HOOPS/Parasolid module, call HP Close(). This should be called in an application cleanup routine, when Parasolid is no longer needed. This does not shutdown the HOOPS Graphics System; the HOOPS function HC_Reset_System() will still need to be called to directly shutdown HOOPS:
void MyParaHoopsAppCleanup() { HP_Close();// shutdown Parasolid HC_Reset_System();// shutdown HOOPS }
To read a Parasolid file, call HP_Read_Xmt_File(). This parses the specified Parasolid file, populates the Parasolid modeling kernel with its contents, and then maps the model to a corresponding HOOPS segment tree hierarchy that includes all geometry and attributes.
// an application specific read function; HSolidModel is derived // from the HBaseModel MVO class int HSolidModel::Read(const char * FileName) { unsigned int i; char extension[120]; bool success = true; extension[0]='\0'; HC_Parse_String(FileName,".",-1,extension);// make it lower case for(i=0; i<strlen(extension);i++) extension[i]=(char) tolower(extension[i]);// read the file into the model object's model segment HC_Open_Segment_By_Key(m_ModelKey); // read each file into it's own subsegment of the main model segment so that // we can easily apply transformations to different parts that might be read // into the same model object; currently not supported in MVO, but could be // added in the future. This is also required so that when we do segment // level deletion later on, we don't delete the model_keysegment HC_Open_Segment(""); if(streq(extension,"xmt_txt") || streq(extension,"x_t")) { PK_PART_receive_o_t options; PK_ERROR_code_t result; PK_PART_receive_o_m(options); options.transmit_format = PK_transmit_format_text_c;// read an XT file result = HP_Read_Xmt_File(FileName, GetPartition(), 0, 0, &options); if (result 0) success = false; } else success = false; HC_Close_Segment(); HC_Close_Segment(); return success; }
p>The HOOPS/Parasolid Reference Application contains an example of how to use this function. Refer to the HParaModel::Read() function located in HSolidModel.cpp.
Rendering Parasolid entities requires a sequence of steps, most of which are encapsulated by the HOOPS/Parasolid Integration functions.
First, a 'Frustum', which includes a set of Graphical Output (GO) routines, must be implemented and registered with Parasolid.
Second, the tessellated geometric data intercepted by the GO routines must be stored and displayed to various devices (rendered).
Once these are accomplished, the Parasolid render routines (PK_TOPOL_render_xxx) must be called in order that the geometry associated with each Parasolid object can be rendered via the Frustum.
The HOOPS/Parasolid Integration fully encapsulates the first two items. Additionally, when a Parasolid file is read via the HP_Read_Xmt_File() function, all the necessary PK_TOPOL_render_xxx calls are made automatically as each Parasolid part/entity is parsed. However, the HP_Render_Entity routine needs to be manually called when Parasolid entities are being dynamically created and edited via the application's user-interface and underlying logic.
As an example, we will go through the process for inserting a cylinder object into Parasolid, and passing the tessellated information to HOOPS for subsequent rendering and interaction. This and future sections assume familiarity with the types of Parasolid entities, denoted by the Parasolid type PK_CLASS_t.
A cylinder, along with many other predefined Parasolid objects, is represented by a PK_BODY entity, and is created via the routine:
<error_code> = PK_BODY_create_solid_cyl(Mradius>,<height>,<cyl_basis_set>,<cyl_body>);
Parasolid entities are referred to by a 'TagID', so this function populates the Parasolid kernel with a cylinder of the specified radius and basis_set, and returns the TagID of the associated Parasolid body. However, the cylinder will not actually be rendered (mapped to corresponding HOOPS segments and geometry) until a call is made to the HP_Rendering_Entity() routine, discussed in the next section.
The following code demonstrates how to create a Parasolid cylinder. Note that it calls HP_Render_Entity().
// create a parasolid cylinder and return the cylinder's body id PK_BODY_t HOpCreateSolidCylinder::CreateSolidCylinder(HPoint ¢er, HVector radius_vector, HVector height_vector) { PK_ERROR_code_t error_code; PK_AXIS2_sf_t cyl_basis_set; PK_BODY_t cyl_body; float radius, height; double view[3], up[3]; HPoint add, subtract; HUtility::GetViewplaneVectors(m_pView->GetSceneKey(), view, up); radius = (float) HC_Compute_Vector_Length(&radius_vector); height = (float) HC_Compute_Vector_Length(&height_vector); // to determine orientation add and subtract the view plane up // vector and the cylinder up vector. By measuring the size of // each vector we can determine the direction of the cylinder add.Set(height_vector.x + (float) up[0], height_vector.y + (float) up[1], height_vector.z + (float) up[2]); subtract.Set(height_vector.x - (float) up[0], height_vector.y - (float) up[1], height_vector.z - (float) up[2]); cyl_basis_set.location.coord[0] = (double) center.x; cyl_basis_set.location.coord[1] = (double) center.y; cyl_basis_set.location.coord[2] = (double) center.z; if (HC_Compute_Vector_Length(&add) > HC_Compute_Vector_Length(&subtract)) { cyl_basis_set.axis.coord[0] = up[0]; cyl_basis_set.axis.coord[1] = up[1]; cyl_basis_set.axis.coord[2] = up[2]; } else { cyl_basis_set.axis.coord[0] = -up[0]; cyl_basis_set.axis.coord[1] = -up[1]; cyl_basis_set.axis.coord[2] = -up[2]; } cyl_basis_set.ref_direction.coord[0] = view[0]; cyl_basis_set.ref_direction.coord[1] = view[1]; cyl_basis_set.ref_direction.coord[2] = view[2]; HSolidModel * model = (HSolidModel*)(m_pView->GetModel()); PK_PARTITION_set_current(model->GetPartition()); error_code = PK_BODY_create_solid_cyl(radius, height, &cyl_basis_set,&cyl_body); if ( report_ifail(error_code,0) != 0) { char error_message[256]; sprintf(error_message, "failed rendering cylinder\n Error Code %d", error_code); MessageBox(NULL, error_message, 0, MB_OK); return 0; } // now put it in the HOOPS database HP_Render_Entity(cyl_body) return cyl_body; }
After a Parasolid entity is defined and inserted into the Parasolid database,
the tessellated geometric information that represents the entity must be generated.
PK_BODY entities (along with many others) are composed of PK_FACE and PK_EDGE
entities. These entities denote the logical surface and edge/boundary
information associated with the body. Specifically, a cylinder body object
is composed of three PK_FACE entities, one for the top, one for the bottom,
and one for the 'tubular' part of the cylinder. It is composed of
two PK_EDGE entities, one for a circle denoting the top edge, and one for a
circle denoting the bottom edge. The tessellated geometric information
associated with the PK_FACE and PK_EDGE entities would typically need to be
generated by manually calling the following Parasolid routines:
<error_code> = PK_TOPOL_render_facet(<number_of_bodies>,<bodies>,<topol_transfs>, <view_transf>,<rendering_options>); <error_code> = PK_TOPOL_render_line(<number_of_bodies>,<bodies>,<topol_transfs>,<view_transf>,<line_options>);
In addition to tessellating the body into facets and lines, these functions will send the tessellated information to the Graphical Output routines.
The HOOPS/Parasolid Integration includes a function which automatically makes the above PK_TOPOL_render_facet calls, and because the HOOPS/Parasolid Integration implements the underlying GO routines, the geometric data that represents the cylinder is automatically mapped to a group of geometric primitives in the HOOPS database. (By default, these primitives are inserted in the currently open HOOPS segment. The HP_Set_Rendering_Options function discussed later in this document, provides for control of this behavior, including creation of subsegments and optimization of the geometry.)
To render Parasolid entities and have their tessellated representation automatically mapped to HOOPS graphical primitives, you may simply call:
HP_Render_Entities (count, entities);
where 'count' is the number of entities, and 'entities' is the array of Parasolid
entities (of type PK_Entity_t). This function also updates the bi-directional
mapping (which uses a hash table) maintained by the HOOPS/Parasolid integration.
Recall that the HOOPS geometry and any additional segments generated as a result of the HP_Render_Entity and HP_Render_Entities routines will be placed in the currently open HOOPS segment. The following code ties together the code necessary to insert a cylinder object into both Parasolid and HOOPS. It assumes that we have obtained the creation parameters for a cylinder from the UI, opens a segment and then calls the 'CreateSolidCylinder' function discussed previously. After CreateSolidCylinder is called (which calls HP_Render_Entity, discussed above), the Parasolid database will contain a parametric representation of the cylinder (a Parasolid 'body'), and the HOOPS database will contain the corresponding geometric representation (a HOOPS 'shell'):
// open up a HOOPS segment which will contain the cylinder geometry HC_Open_Segment_By_Key(GetView()->GetModel()->GetModelKey()); // put each entity in a separate segment so that we can easily apply // transformations to separately created entities HC_Open_Segment(""); PK_BODY_t cyl_tag; cyl_tag = CreateSolidCylinder(m_ptFirst, m_world_radius_vector,height_vector); HC_Close_Segment(); HC_Close_Segment();
The Parasolid 'body' entity that represents the cylinder is accessible via the 'cyl_tag' TagID. Details on how to identify the HOOPS geometric primitives associated with the Parasolid entity are covered later in this document, in the section entitled 'Operating on Parasolid Entities'.
The HOOPS/Parasolid Reference Application implements and uses the platform-independent creation/rendering functions discussed above. It also includes additional geometry creation functions:
- The HOpCreateSolidCone, HOpCreateSolidCylinder and HOpCreateSolidSphere classes are all derived from the HBaseOperator MVO class, and provide for creation of 'rubberband' construction geometry in addition to creation/rendering of the Parasolid and HOOPS entities. They contain implementations of CreateSolidCone, CreateSolidCylinder, and CreateSolidSphere functions as outlined above.
We will recall that the HOOPS/Parasolid Integration maintains a mapping between Parasolid entities and HOOPS Geometry, which enables the developer to determine the Parasolid entities associated with a given HOOPS geometric primitive or segment, and vice versa. The HP_Render_Entities function causes this mapping to be created/updated each time it is called to populate the HOOPS database with the tessellated representation of Parasolid entities.
As a result, another HOOPS/Parasolid function must be called when deleting HOOPS geometry associated with Parasolid entities to ensure that the internal mapping is kept up to date. The function is called HP_Delete_Entity_Geometry, and would be used to delete the HOOPS geometry associated with a Parasolid entity after the latter is deleted. For example, the following function is a wrapper function which deletes a Parasolid entity along with its associated HOOPS geometry:
void HSolidModel::DeleteParasolidEntity( PK_ENTITY_t entity)
{
HP_Delete_Entity_Geometry(1, &entity);
PK_ENTITY_delete(1, &entity);
}
It is important to use the HP_Delete_Entity_Geometry function whenever deleting HOOPS geometric primitives associated with deleted Parasolid entities. If only the normal HOOPS/3DGS deletion functions were called (HC_Delete_By_Key, etc...), then the HOOPS/Parasolid mapping would not be correct, and problems would occur during subsequent operations.
A call to HP_Delete_Entity_Geometry() normally deletes all associated entities from HOOPS including segments. Sometimes however it is desirable to preserve the segment structure if e.g. extra information with no connection to the solid modeller has been previously associated to a body. To allow that there is a third parameter in the call to HP_Delete_Entity_Geometry() called "PreserveSegments". If this boolean is set to "true" (it defaults to false) then only true HOOPS geometry is deleted and keys that are associated with segments are disregarded. This means that if a body is deleted it will be retesselated into the same segment again on the next call to HP_Render_Entity().
However, let's consider the following scenario. Say we have a simple cylinder
body which contains (among other things) two edges. After normal tesselation
we want to move one of the edges (consisting of 2 HOOPS ellipses) into a separate
segment (let's call it "special") which contains e.g. some text annotation and
a different line weight attribute. We can do this with HC_Move_By_Key().
Unfortunately after a retesselation (even when using HP_Delete_Entity_Geometry
with PreserveSegments = true) the two HOOPS ellipses are recreated in their
default segment again and the newly created "special" segment is empty.
To avoid this HP_Associate_Entity_To_Key() can be used which associates a HOOPS keys to a Parasolid entity. In our example we use it to associate the key of the "special" segment to the Parasolid edge entity that we want to store underneath it. Whenever the Parasolid body needs to be rerendered we delete it's associated geometry by using "HP_Delete_Entity_Geometry" with the PreserveSegment flag set to true. This way even though all the HOOPS geometry of the body is deleted there is still the connection to the "special" segment in the database.
Here is some example code://first we render the object let's assume it is a cylinder) normally HC_Open_Segment_By_Key(((HBaseModel*)my_view->GetModel())->GetModelKey()); HP_Render_Entities(1, &body); //this code simply queries all edges of the cylinder and moves the //first edge into a segment called special which contains a new line //weight attribute setting long list[10000]; int count; int num_edges; PK_EDGE_t *edges; PK_BODY_ask_edges(body,&num_edges, &edges);count = HP_Compute_Geometry_Keys(edges[0],10000,list,"edges"); long key = HC_KOpen_Segment("special"); for(int i=0;i<count;i++) { HC_Move_By_Key(list[i],"."); HC_Set_Line_Weight(8.0); } HC_Close_Segment(); //we are associating the edge entity to the key of the segment where //it's geometry now resides in HP_Associate_Key_To_Entity(edges[0],key); ... //assuming the body has changed (e.g. it was the target of a boolean //operation) we are first deleting all the bodies that are completely //gone normally HP_Delete_Entity_Geometry(number_of_tools, tools); //the target body (let's assume it's the one that we have created //above) survives the operation so we delete it's geometry but //preserve it's segment structure HP_Delete_Entity_Geometry(1, &target,true); ... (some boolean operation applied) //the body is now rerendered in the same segment as it was sitting in //before and (important!) the edge that we have put into the "special" //segment is again in the special segment in it's new form (it might //be partly cut due to the boolean) HP_Render_Entities(1, &target);
HP_Render_Entity() and HP_Render_Entities() have a third parameter which is a Parasolid transformation entity (0 by default) that represents the view matrix for view dependent tessellation. It is the responsibility of the caller to set the appropriate tessellation options for the frustum routines. This example demonstrates how to temporarily overwrite tesselation options to render the body in a view dependent way.
//create the PS transform from a supplied matrix PK_TRANSF_t view_transf; PK_TRANSF_create(&rot_m,&view_transf); //temporarily set tesselation options //for silhouette+hidden line output PK_TOPOL_render_line_o_t g_lineOptions,g_lineOptionsSav; HP_Show_Tessellation_Options(&g_lineOptions, NULL, NULL); HP_Show_Tessellation_Options(&g_lineOptionsSav, NULL, NULL); g_lineOptions.edge = PK_render_edge_yes_c; g_lineOptions.silhouette = PK_render_silhouette_yes_c; g_lineOptions.visibility = PK_render_vis_hid_c; HP_Set_Tessellation_Options(&g_lineOptions, NULL,NULL); //rerender the bodies with the supplied view transformation HC_Open_Segment_By_Key(GetModel()->GetModelKey()); HSolidModel * model = (HSolidModel *)(GetModel()); PK_PARTITION_set_current(model->GetPartition()); HP_Render_Entities(numbodies, bodies, view_transf); HC_Close_Segment(); //restore old tessellation settings HP_Set_Tessellation_Options(&g_lineOptionsSav, NULL, NULL);
As mentioned in the previous section, the HP_Render_Entity function instructs HOOPS to insert the tessellated geometric primitives into the currently open HOOPS segment. The methods by which the HOOPS/Parasolid Integration maps the tessellated representation of Parasolid entities to HOOPS segments and geometric primitives are controlled using the function HP_Set_Rendering_Options(). (Note: this function is different than the HOOPS/3DGS function called HC_Set_Rendering_Options, which controls how the HOOPS 3D Graphics Systems performs 2d/3d rendering.)
The default mapping behavior will create additional HOOPS subsegments within the currently open segment for each Parasolid body being rendered. Because each HOOPS segment has one opinion about each HOOPS attribute (color, line_pattern, modeling matrix, etc...), such a segment hierarchy facilitates applying unique sets of attributes to the group of geometric primitives that represent a Parasolid body. (The new attribute can be directly set on the unique HOOPS segment associated with the body.)
The default mapping also preserves any color attributes which may have been stored in the Parasolid kernel by creating a HOOPS segment for each unique color in the Parasolid kernel. These segments are created underneath each body segment. Creating multiple segments for each color is again necessary due to the attribute coherence rule of a HOOPS segment. Though some HOOPS geometric primitives (shells and meshes) can be individually colored, the other primitives are rendered using the color and other attributes set in the containing segment.
The Integration also maps each Parasolid PK_FACE entity, which are rendered as a result of the PK_TOPOL_render_facet call, to a HOOPS 'shell' primitive by default. (The alternative is to map multiple PK_FACE entities to a single HOOPS shell, which is supported by the "merge faces" option discussed later in this section.) Mapping each PK_FACE to a single HOOPS shell facilitates Parasolid operations that work at the level of a PK_FACE; it is easier to access an entire HOOPS shell, rather than access a subset of edges/faces/vertices within a HOOPS shell. Each PK_EDGE entity is mapped to one or more HOOPS 'line' primitives, which include 'line', 'polyline', 'arc', 'circular arc', 'ellipse', and 'elliptical arc'.
The following diagram depicts how two Parasolid bodies, each containing two different colors, would be mapped to the HOOPS database using the default Mapping_Options; the 'part' segment denotes the currently open segment at the time the PK_TOPOL_render_XXX calls are made:
Setting the Rendering_Option "preserve color = off" instructs HOOPS to ignore any color attributes stored in the Parasolid kernel when mapping Parasolid entities to the HOOPS database. HOOPS will not create subsegments within the currently open segment for each unique color:
Color attributes are frequently stored in Parasolid files, hence color should typically be preserved when reading in a file. ( HP_Read_Xmt_File() ) Additionally, when dynamically creating/inserting a Parasolid entity, it is useful to set the entity's desired color inside of Parasolid so that the color will be stored along with the entity when writing out an Xmt file. In this common situation, color should also be preserved so that the corresponding HOOPS segments/geometry will be rendered with the correct color. However, if there is no need to either create entities with different/specific colors, or to preserve color information contained in a file, "preserve color = off" may be used.
Setting the Mapping_Option "create body segments = off" instructs HOOPS to not create extra subsegments within the currently open segment for each body.
Because each HOOPS segment has one opinion about each HOOPS attribute (color, line_pattern, modeling matrix, etc...), such a segment hierarchy does not allow for attribute granularity among the various geometric primitives that represent the Parasolid geometry rendered within the currently open HOOPS segment. It only allows attributes to be set based on the color partitioning of the segments. Any HOOPS attribute that is set on the 'red' segment in the above example will affect all geometry in the segment; hence all bodies that are being represented will be affected equally by the attribute change. If a body needed to have a unique modeling matrix applied to it, its associated HOOPS geometry would have to be identified via the HP_Compute_TagID and HP_Compute_Geometry_Keys() functions, and moved into a separate segment that contained the new modeling matrix. In contrast, if the default segment structure outlined in the beginning of this section was used, the new attribute could be directly set on the unique HOOPS segment associated with the body.
Setting the Mapping_Options "merge faces = on" instructs the Integration to merge all the HOOPS shells that were generated to represent a PK_BODY (one shell for each PK_FACE) into a single HOOPS shell. In the case of a multi-colored PK_BODY, the shells corresponding to PK_FACES that have the same color attribute will be merged into one HOOPS shell, resulting in as many HOOPS shells are there are distinct colors in the PK_BODY entity.
This mapping results in optimal rendering speed at the expense of ease of manipulating individual PK_Body entities. Applications that are concerned only with viewing, or that do not need to select at the PK_FACE level (to perform 'face-level' Parasolid operations) will gain significant rendering speed advantages by making use of this feature.
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_TagID function 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.)
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 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.
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'. (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.)
HC_KEY *keys; int count; // compute all the HOOPS geometry associated with the Parasolid // entity count = HP_Compute_Geometry_Key_Count (entity, "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.
// 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); }
The HOOPS/Parasolid Reference Applicationcontains an implementation of HPSelectionSet, which includes the above Select function and definition of the m_SelectLevel type. Refer to HSSelectionSet.h and HSSelectionSet.cpp for the interface and implementation of the class.
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, 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. (See the section on Creating and Rendering Parasolid Entities.)
The syntax of the function is as follows:
<error_code> = <b>PK_BODY_Boolean</b>(<target>, <number_of_tools>,<tools>,<options>, <number_of_bodies>,<bodies>);
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; int number_of_bodies; PK_BODY_t *bodies; 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, "ition); 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(target, number_of_tools, tools, &options, ½mber_of_bodies, &bodies); 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(number_of_bodies, bodies); if (number_of_bodies > 0) PK_MEMORY_free(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), which is part of the HOOPS/Parasolid Reference Application. It performs the following operations:
// 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).