===============================
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 <parasolid/HSolidView.h>`_ and `HSolidView.cpp <parasolid/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*).
