=========
Parasolid
=========

The |HPSNOW|-Parasolid bridge supports a connection between Parasolid entities and their corresponding representation in the |HPSNOW| scene graph. This interface facilitates visualization of Parasolid models, as well as creation and editing of Parasolid entities. If you haven't done so already, you should read through the Programming Guide page on the :doc:`component hierarchy <0904_component_hierarchy>`, as the Parasolid interface is based on it.

All Parasolid-related classes are members of the ``HPS::Parasolid`` container class. Supported platforms are listed in the :doc:`release notes </general/release_notes/index>` compatibility table. Developers can choose to work with either C++ or C# (C# is Windows-only).

Convergent Modeling is available in the |HPSNOW|-Parasolid bridge, supporting both solid B-rep operations and faceted geometry operations in a single 
modelling component. It is available with Parasolid version 28.1 or higher.

**NOTE**: |HPSNOW| automatically initializes Parasolid if it detects that the Parasolid integration is present. It will also shut down Parasolid at the end of the application, and restart it as needed. Your application should not initialize or shutdown Parasolid if it is using the Parasolid integration. If you need to use Parasolid after |HPSNOW| is finished, you need to restart the session.


Prerequisites
=============

Steps for loading Parasolid models are delineated below:


Step 1: Set Up Parasolid
-------------------------

In order to use the ``HPS::Parasolid`` functionality, you must download the Parasolid package from its developer and include it in your environment path. The supported version of Parasolid is noted in the :doc:`release notes </general/release_notes/index>`. The Parasolid runtime library file must be distributed with your application.

.. csv-table::
	:header: "Platform", "Required file"
	
	"Linux", "*libpskernel.so*"
	"OS X", "*libpskernel.dylib*"
	"Windows", "*pskernel.dll* - pskernel_net.dll also required for C# apps"


Step 2: Include Parasolid Header
--------------------------------

To utilize the Parasolid importer, you need to include *sprk_parasolid.h* in your source.


Step 3: Set Schema Directory Location
-------------------------------------

Many Parasolid files depend on a schema. If a schema is required but the importer cannot find the schema directory, the import will fail. |HPSNOW| will check the following locations, in order, for the schema directory:

#. The schema directory set by your application (see code snippet below)
#. The ``P_SCHEMA`` environment variable
#. The `./schema` directory

The schema directory is set on the ``HPS::World`` object. World objects are discussed in :doc:`this section <0101_database>`.

.. tabs::

	.. group-tab:: C++
	
		.. code-block:: cpp
	
			world.SetParasolidSchemaDirectory("path/to/directory");
		   
	.. group-tab:: C#
	
		.. code-block:: cs

			world.SetParasolidSchemaDirectory("path/to/directory");


Step 4: Link Against Parasolid (Optional)
-----------------------------------------

If you are only planning to use the ``HPS::Parasolid`` classes, then you can skip this step. However, if you have a need to connect to the Parasolid API directly, you also need to set your compiler to link against the Parasolid library and include the Parasolid header files in your source.


Importing
=========

|HPSNOW|'s support for importing of Parasolid files is consistent with other supported file types. The main difference is the use of functions from the ``HPS::Parasolid`` container class. Like other file I/O operations, Parasolid models are loaded asynchronously, so be sure to call ``ImportNotifier::Wait`` before operating on your model.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [import]
		   :end-before: //! [import]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [import]
		   :end-before: //! [import]
		   
After ``HPS::ImportNotifier::Wait`` returns, the model is fully loaded. Note that setting the file format is mandatory and |HPSNOW| will likely throw an exception if this field is set incorrectly. By convention, Parasolid text files will have a *.X_T* extension, whereas binary files have a *.X_B* extension.


.. _import_options:

Import Options
--------------

Various import options are available when loading Parasolid models. These options enable you to set the file type, tessellation options, behavior directives, and whether user fields are imported. All options are set on the import options kit. Note that the tessellation fields require options kits of their own.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [import_options]
		   :end-before: //! [import_options]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [import_options]
		   :end-before: //! [import_options]

The options kits in the code snippet above take numerical values for their tessellation parameters. These values are expected to be in the model's units. While this gives you fine control over how the model is tessellated, it requires you to make some precalculations to determine the size of the model and what makes sense for values. Using arbitrary values could result in very long load times as |HPSNOW| tries to create precise tessellation.

For this reason, there is an alternative way to specify tessellation. The ``SetTessellationLevel`` function will accept an enum value ranging from "extra low" to "extra high", which instructs |HPSNOW| to automatically calculate an appropriate tessellation value based on the model's extents. When this method is used, any other tolerance option set on the facet kit gets ignored (these are the options set through ``SetChordTolerance``, ``SetSurfacePlaneTolerance``, ``SetFacetPlaneTolerance``). This method is only available for facet tessellation.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [relative_tessellation]
		   :end-before: //! [relative_tessellation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [relative_tessellation]
		   :end-before: //! [relative_tessellation]
		   

.. _importing_non_parasolid:

Importing Other File Formats Into a Parasolid Session
-----------------------------------------------------

If you are a HOOPS Exchange licensee, you may also import models from other file formats into an active Parasolid session using the ``HPS::ExchangeParasolid`` importer. This importer will load the geometry into Parasolid and use it to tessellate the model. It will also preserve PMI and capture information which Parasolid doesn't support natively. The importer creates ``HPS::Exchange::Component`` objects for the PMI and captures. Everything else will be translated to Parasolid entities behind the scenes, which will be represented as ``HPS::Parasolid::Component`` objects. You can reference both object types through the normal component interface. This includes accessing the underlying native data. For example, you can access Parasolid data directly through the Parasolid components and you can access HOOPS Exchange data (B-rep, PMI, etc.) directly through the Exchange components.

Loading the file in this way is done similarly to loading other file types:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_exchange_parasolid.cpp
		   :start-after: //! [import]
		   :end-before: //! [import]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_exchange_parasolid.cs
		   :start-after: //! [import]
		   :end-before: //! [import]
		   

Getting a Reference to Subcomponents
------------------------------------

The Parasolid importer uses the same component hierarchy structure used by other |HPSNOW| model importers. It is very important to understand the :doc:`component hierarchy <0904_component_hierarchy>` before continuing. The ``HPS::CADModel`` object is the root of the component structure and is available from the import notifier. The subcomponents are then available from the CAD model object.

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [subcomponent]
		   :end-before: //! [subcomponent]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [subcomponent]
		   :end-before: //! [subcomponent]
		   

Tessellating
------------

When |HPSNOW| reads a Parasolid model into memory, the model is tessellated and the definition is retained in the component hierarchy structure. Because the definition is retained, |HPSNOW| is able to retessellate the model at a later time using different tessellation parameters and without rereading the file. To retessellate, simply set up new tessellation kits and call ``Tessellate`` on the target component:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid_retessellate.cpp
		   :start-after: //! [retessellate]
		   :end-before: //! [retessellate]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid_retessellate.cs
		   :start-after: //! [retessellate]
		   :end-before: //! [retessellate]

Retessellating a component will also retessellate all subcomponents. Note that ``Tessellate`` can only be called on either an assembly or body entity. If you need to retessellate the entire model, you can either reload the model or call ``HPS::CADModel::GetSubcomponents`` and retessellate each component individually.

**IMPORTANT:** If you make modifications to a model using the Parasolid API you *must* call ``Tessellate`` in order for |HPSNOW| to receive the updated model.


.. _attaching:

Attaching the Parasolid Model to Your Scene Graph
=================================================

In order to attach the imported Parasolid model to the |HPSNOW| scene graph, you must use a :doc:`view hierarchy <0301_core>`. The ``HPS::CADModel`` provides a default view which you obtain in the following way:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [default_view]
		   :end-before: //! [default_view]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [default_view]
		   :end-before: //! [default_view]
		   
When calling ``HPS::CADModel::ActivateDefaultCapture``, a ``FitWorld`` is automatically performed, which centralizes the model in the window.

Once you call ``HPS::Canvas::AttachViewAsLayout``, the model becomes part of the main scene graph and will be rendered at the next update.


.. metadata:

Handling Metadata
=================

Metadata is additional non-geometric information that is associated with a ``HPS::Parasolid::Component``. Each piece of metadata is a name-value pair, and the values can be strings, integers, doubles, or booleans. Any ``HPS::Parasolid::Component`` can have metadata associated with it, although some types of metadata are only available on certain types of components. A list of metadata is as follows:

.. csv-table::
	:header: "Metadata name", "Metadata type", "Description"
	
	"Name", "``StringMetadata``", "The name of the Parasolid entity associated with the component. If the entity does not have a name, a default name will be generated. The default name is in the form of ``<entity_type>_<entity_tag>``."
	"Identifier", "``IntegerMetadata``", "Returns whether the Parasolid entity associated with the component is a void region. This is only valid if the component is of type ``ParasolidTopoRegion``."
	"IsRegionVoid", "``BooleanMetadata``", "Returns the identifier of the Parasolid entity associated with the component."
	"Filename", "``StringMetadata``", "Name of the file imported. This is only valid from the ``CADModel``."
	"FileFormat", "``StringMetadata``", "Format of the file imported. This is only valid from the ``CADModel``."
    "ImportTime", "``DoubleMetadata", "Time taken to import the Parasolid file into memory, in milliseconds. This is only valid from the ``CADModel``."
	"ParseTime", "``DoubleMetadata``", "Time taken to parse the Parasolid information into a scene graph, in milliseconds. This is only valid from the ``CADModel``."

While metadata with any name can be set in a Parasolid model, |HPSNOW| is only able to retrieve metadata with names that are in the table above. If you need to retrieve other metadata, you need to use the Parasolid API.

Metadata can be embedded with any ``HPS::Parasolid::Component``. The following example demonstrates how to get metadata associated with a component:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [metadata]
		   :end-before: //! [metadata]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [metadata]
		   :end-before: //! [metadata]
		   

Data Mapping
============

As mentioned on the :doc:`component hierarchy <0904_component_hierarchy>` page, |HPSNOW| associates each Parasolid Component object with native Parasolid entities. You can retrieve the underlying Parasolid entity with a call to ``GetParasolidEntity``. A ``ParasolidEntity`` is equal to a ``PK_ENTITY_t``.

The code snippet below demonstrates how to get an entity's Parasolid ID from a |HPSNOW| key:

.. tabs::

	.. group-tab:: C++
	
		.. code-block:: cpp
	
			// get |HPSNOW|-Parasolid component interface object for corresponding |HPSNOW| key
			HPS::Component myComponent = modelFile.GetComponentFromKey(hpsKey);

			// 'parasolidEntity' == PK_ENTITY_t
			ParasolidEntity parasolidEntity = myComponent.GetParasolidEntity();

			// test if the object type is a Parasolid assembly
			// [see Component reference manual page for other Parasolid types]
			if (component.GetComponentType() == HPS::Component::ComponentType::ParasolidAssembly)
			{
				// do something with this object
			}

			// gets the |HPSNOW| keys associated with the Component
			KeyArray keys = component.GetKeys();

			// at this point, you can use the returned entity to directly query model information from the Parasolid API
		   
	.. group-tab:: C#
	
		.. code-block:: c#

			// get |HPSNOW|-Parasolid component interface object for corresponding |HPSNOW| key
			HPS.Component myComponent = modelFile.GetComponentFromKey(hpsKey);

			// 'parasolidEntity' == PK_ENTITY_t
			ParasolidEntity parasolidEntity = myComponent.GetParasolidEntity();

			// test if the object type is a Parasolid assembly
			// [see Component reference manual page for other Parasolid types]
			if (component.GetComponentType() == HPS.Component.ComponentType.ParasolidAssembly)
			{
				// do something with this object
			}

			// gets the keys associated with the Component
			KeyArray keys = component.GetKeys();

			// at this point, you can use the returned entity to directly query model information from the Parasolid API
		   
See the ``HPS::Component`` reference manual entry for a list of all the component enums.


.. _manipulating_entities:

Manipulating Entities at Runtime
================================

Entity Creation
---------------

While Parasolid models may sometimes be loaded from a file, it is of course common for modeling operations to create entities dynamically. If you have a need to start from scratch (an empty model), you need to create all entities beginning with the CAD model. The ``HPS::Parasolid::CADModel`` object is created using a factory method:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [create_cad_model]
		   :end-before: //! [create_cad_model]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [create_cad_model]
		   :end-before: //! [create_cad_model]

After you have the ``HPS::Parasolid::CADModel`` object, new Parasolid kernel entities would be created using the Parasolid API, then attached to the model post-creation. The following example demonstrates how you would add a solid block to a model's root entity at runtime, and then access the keys of the corresponding |HPSNOW| scene-graph entities which provide the visual representation:

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [entity_creation]
		   :end-before: //! [entity_creation]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [entity_creation]
		   :end-before: //! [entity_creation]
		   
The new solid block is ready to be rendered during the next update.


Entity Modification
-------------------

A typical flow for entity modification would begin with the user selecting on a |HPSNOW| scene-graph object. Given a |HPSNOW| key, you would obtain the ``HPS::Component`` object and the Parasolid entity ID as previously demonstrated. After modifying the entity using the Parasolid API, call ``HPS::Parasolid::Component::Tessellate`` to update the |HPSNOW| scene-graph representation.


Entity Deletion
---------------

To delete a component, call ``HPS::Component::Delete``. Deleting a component will also delete any subcomponents, along with all associated |HPSNOW| scene-graph segments and graphical primitives. The ``HPS::Component::Delete`` function will also delete the underlying Parasolid entity.


Exporting
=========

Exporting files with the |HPSNOW|-Parasolid interface is possible if the associated ``HPS::Parasolid::CADModel`` was obtained through a Parasolid import. The export process is synchronous and follows a pattern similar to other file exporters.

The ``HPS::Parasolid::ExportOptionsKit`` has two options:

* *File format* - you can choose between binary, neutral binary, or text
* *User data* - this is a flag indicating whether or not user data should be included in the export

.. tabs::

	.. group-tab:: C++
	
		.. literalinclude:: ../../../internals/tests/docs/source/cpp/00760_parasolid.cpp
		   :start-after: //! [export]
		   :end-before: //! [export]
		   
	.. group-tab:: C#

		.. literalinclude:: ../../../internals/tests/docs/source/cs/00760_parasolid.cs
		   :start-after: //! [export]
		   :end-before: //! [export]
		   

.. _warnings_exceptions:

Warnings and Exceptions
=======================

The importer will issue a warning or throw an exception if it detects a condition that doesn't make sense or cannot recover from. For example, if you specify a maximum value that is smaller than a minimum value, a warning is issued. Or, if you specify the file type to be text, but the imported file is actually a binary file, |HPSNOW| will throw an ``HPS::IOException``. Your code should be prepared to handle these occurrences so that it can take steps to recover.
