HOOPS Visualize Documentation

< Table of Contents

PROGRAMMING GUIDE

9.5 Exchange integration

9.5 Integrating Visualize with HOOPS Exchange

HOOPS Exchange is Tech Soft 3D's CAD data integration library and is the recommended way to get model data from a CAD file into Visualize. Through Exchange, you can access assembly information, visualization data, and PMI annotations for inspection or manipulation within the context of a Visualize scene graph. A detailed introduction to HOOPS Exchange can be found here.

Any file that can be loaded by the HOOPS Exchange libraries can be loaded into HOOPS Visualize. The interface described in this section is the way to connect Visualize with HOOPS Exchange, and is supported on Windows, Linux, and OS X.

Important note for C# programmers

The Exchange interface has a few limitations when using C#. Functions which expect C++ pointers cannot be called directly from C#. Instead, an intermediate step using CLI (Common Language Infrastructure) is required. The Visualize solution provides the cc_exchange_lib_vc11 project which demonstrates how the C++/CLI interface is set up, allowing the C# programmer to access specific Exchange functionality that is otherwise only available in C++.

The project can be found at Applications > Demo > cc_exchange_lib_vc11, and when compiled, generates the CLI interface in the form of a DLL. The DLL needs to be added as a reference to the C# project that invokes it. You will also find a sister project called Applications > Demo > cc_exchange_vc11. This project is a demonstration on how you would invoke the CLI to call from your C# application through to C++ using the interface generated by cc_exchange_lib_vc11.

9.5.1 Prerequisites

Steps for integrating HOOPS Visualize with Exchange are delineated below:

Step 1: Install Exchange

Exchange can be downloaded from the Tech Soft 3D Developer Zone. If you don't already have Exchange installed, install it now. You do not have to generate a separate key to use the Exchange component. The key will be embedded with your Visualize license key. Note the Exchange installation directory.

Step 2: Set up the environment

The Exchange DLLs must be in the system path so that they can be located when your program is executing. For example, assuming your platform is 64-bit Windows, and the installation is at C:\Program Files\HOOPS_ExchangePublish, the path should be set as C:\Program Files\HOOPS_ExchangePublish\bin\win64;.

Step 3: Include the libraries in your project

The Visualize-Exchange interface library must be included as a dependency. C++ users will need to link to hps_sprk_exchange.lib. C# users need to reference the hps_cs_sprk_exchange_vc11 assembly. Alternatively, you may specify the library directory by calling World::SetExchangeLibraryDirectory.

Step 4: Include the header file in your source (C++ only)

Your source files that use the Visualize-Exchange interface must include sprk_exchange.h.

Step 5: Initialize the DLLs (optional)

This step is only necessary if you need to call the HOOPS Exchange API directly in order to access functionality not provided by the Visualize-Exchange interface. If this is your intention, you must make an explicit call to A3DSDKLoadLibrary. See Runtime initializations of the HOOPS Exchange Programming Guide. You should not call any other HOOPS Exchange initialization functions.

At this point, HOOPS Exchange is ready to use in your application.

9.5.2 File loading

The view hierarchy that was introduced in section 3.1 is utilized when loading Exchange models. Specifically, Visualize encapsualtes Exchange models Model objects as part of the loading process. It can also create a View using the Exchange model's camera (where supported by the file format). In any case, the end goal is to create a view hierarchy. There are multiple ways to do this, as demonstrated in the code snippet, below:

[snippet 9.5.2.a]
try
{
notifier.Wait();
CADModel modelFile = notifier.GetCADModel();
// get the View and attach it to the Canvas
View myView = modelFile.ActivateDefaultCapture();
myCanvas.AttachViewAsLayout(myView);
// alternatively, if you already have a View object, you can simply
// get the Model and attach it directly:
Model model = modelFile.GetModel();
myView.AttachModel(model);
}
catch (const IOException& ioe)
{
// handle error
}
try
{
notifier = Exchange.File.Import(filename, new Exchange.ImportOptionsKit());
notifier.Wait();
CADModel modelFile = notifier.GetCADModel();
// get the View and attach it to the Canvas
View myView = modelFile.ActivateDefaultCapture();
myCanvas.AttachViewAsLayout(myView);
// alternatively, if you already have a View object, you can simply
// get the Model and attach it directly:
model = modelFile.GetModel();
myView.AttachModel(model);
}
catch (IOException ioe)
{
// handle error
}

This snippet demonstrates the importance of calling Wait() on your notifier object. Because all notifier classes do their I/O in a separate thread, the potential exists to use the object before it is done loading, especially if you try to interact with it immediately after you call Import(). Calling Wait() ensures the I/O operation is complete before continuing. If you do not use Wait(), you could end up with unexpected behavior.

In addition to waiting, the Exchange::ImportNotifier also can provide you with useful information about the status of the import. You can even use the notifier to cancel the import, if needed. All of the possible return values for the status are listed in the IOResult reference manual page.

[snippet 9.5.2.b]
// get the status of the import
IOResult result = notifier.Status();
if (result == IOResult::Success)
{
// file loading successfully
}
// you can cancel the import by calling:
notifier.Cancel();
// get the status of the import
HPS.IOResult result = notifier.Status();
if (result == HPS.IOResult.Success)
{
// file loading successfully
}
// you can cancel the import by calling:
notifier.Cancel();

After the model using Exchange, it can be manipulated like any other Visualize model. The components of the model are translated from their native format into the Visualize entities as discussed in the data mapping section.

Options for file loading

When loading a file, you can choose beforehand whether certain elements will be loaded. Default import options will be used for any import settings that are not set on the Stream::ImportOptionsKit passed to the Exchange::File::Import function. These defaults can be seen via ImportOptionsKit::GetDefault(). The options specified during import will determine how long the import takes, the amount of memory used, and the size of exported files. Additionally, if data is not loaded during import, it will not be available afterwards without reimporting the data. It is important to keep these things in mind when selecting the import options. For example, if you have a file that contains PMI data, but know you will not be needing it, you can disable PMI as an import option:

[snippet 9.5.2.d]
importOptions.SetPMI(false);
importOptions.SetPMI(false);

Additional options that can be controlled during import are B-rep data, solids, surfaces, and others.

Configurations

Configurations are an important option that can be utilized for certain file formats. A configuration is a sub-model within a larger model - sometimes these files are also referred to as "multi-model" or "multi-entry" files. Only CATIAV4, SolidWorks, and IDEAS files support configurations. Some files can contain multiple configurations. If you do not specify a configuration to load, the default configuration will be loaded, if possible. Multi-model CATIA V4 files do not have a default configuration, so attempting to load such a file without specifying a configuration will cause the import to fail.

[snippet 9.5.2.e]
// get a list of all configurations in the file
Exchange::ConfigurationArray configArray = Exchange::File::GetConfigurations(filename);
for (size_t i = 0; i < configArray.size(); i++)
{
// get name of configuration
UTF8 configName = configArray[i].GetName();
// set configuration
Exchange::ImportOptionsKit importOptionsKit;
importOptionsKit.SetConfiguration(configName);
// configurations can be nested - you can get a list of subconfigurations
Exchange::ConfigurationArray subconfigArray = configArray[0].GetSubconfigurations();
}
// at this point, the file can be loaded using the ImportOptionsKit
// get a list of all configurations in the file
HPS.Exchange.Configuration[] configArray = Exchange.File.GetConfigurations(filename);
for (int i = 0; i < configArray.Length; i++)
{
// get name of configuration
String configName = configArray[i].GetName();
// set configuration
HPS.Exchange.ImportOptionsKit importOptionsKit = new HPS.Exchange.ImportOptionsKit();
importOptionsKit.SetConfiguration(configName);
// configurations can be nested - you can get a list of subconfigurations
Exchange.Configuration[] subconfigArray = configArray[0].GetSubconfigurations();
}
// at this point, the file can be loaded using the ImportOptionsKit

9.5.3 Handling views, metadata, and filters

Handling views

In code snippet 9.5.2.a, notice that ActivateDefaultCapture returns a View object. This is the same View discussed in section 3.1. Some model formats include a view of a particular state of the model, and ActivateDefaultCapture will load that view. The view may include a camera, toggle visibility for cutting planes, PMI, and geometry with the scene. Additionally, it may apply transformations to some geometry, for example, create an exploded view of an assembly.

Certain model formats allow multiple views to be pre-defined, and you may be interested in loading some view other than the default. The code below demonstrates how to do this.

[snippet 9.5.3.a]
CaptureArray captureArray = modelFile.GetAllCaptures();
for (size_t i = 0; i < captureArray.size(); i++)
{
Capture capture = captureArray[i];
StringMetadata metadata = capture.GetMetadata("Name");
UTF8 viewName = metadata.GetValue();
if (viewName == "<some interesting view>")
{
capture.Activate();
break; // exit loop
}
}
Capture[] captureArray = modelFile.GetAllCaptures();
for (int i = 0; i < captureArray.Length; i++)
{
Capture capture = captureArray[i];
HPS.StringMetadata metadata = (StringMetadata) capture.GetMetadata("Name");
String viewName = metadata.GetValue();
if (viewName == "<some interesting view>")
{
capture.Activate();
// exit loop
}
}

Handling metadata

Metadata is additional non-geometric information that is associated with a Exchange::Component. Each piece of metadata is a name-value pair, and the values can be strings, integers, unsigned integers, doubles, or times. Any Exchange::Component can have metadata associated with it. During the import process, metadata is generally only imported if the Exchange::ImportOptionsKit::SetAttributes is set to true. However, some metadata, such as faces, certain IDs, layers, and materials, are always imported even if ImportOptionsKit::SetAttributes is set to false. A list of that metadata is as follows:

Metadata nameType
"Name"String
"PersistentID"non-zero unsigned int
"NonPersistentID"non-zero unsigned int
"Layer"unsigned int
"Filename"String
"FileFormat"String

Metadata can be embedded with a Exchange::Component in many different forms. The following example demonstrates how to get metadata associated with a Exchange::Component:

[snippet 9.5.3.b]
// get all metadata associated with a component
MetadataArray metadataArray = modelFile.GetAllMetadata();
double someDouble;
int someInteger;
unsigned int someUnsignedInt;
for (size_t i = 0; i < metadataArray.size(); i++)
{
Metadata metadata = metadataArray[i];
// metadata can be of many different types
// you must test for type of metadata
if (metadata.Type() == Type::StringMetadata)
{
StringMetadata sm(metadata);
UTF8 metadataName = sm.GetName();
UTF8 someString = sm.GetValue();
}
else if (metadata.Type() == Type::DoubleMetadata)
{
DoubleMetadata dm(metadata);
UTF8 metadataName = dm.GetName();
someDouble = dm.GetValue();
}
else if (metadata.Type() == Type::IntegerMetadata)
{
IntegerMetadata im(metadata);
UTF8 metadataName = im.GetName();
someInteger = im.GetValue();
}
else if (metadata.Type() == Type::UnsignedIntegerMetadata)
{
UnsignedIntegerMetadata uim(metadata);
UTF8 metadataName = uim.GetName();
someUnsignedInt = uim.GetValue();
}
else if (metadata.Type() == Type::TimeMetadata)
{
TimeMetadata tm(metadata);
UTF8 metadataName = tm.GetName();
someUnsignedInt = tm.GetValue();
// as a convenience, TimeMetadata can also be provided as a String
UTF8 timeString = tm.GetValueAsString();
}
}
// get all metadata associated with a component
Metadata[] metadataArray = modelFile.GetAllMetadata();
for (int i = 0; i < metadataArray.Length; i++)
{
Metadata metadata = metadataArray[i];
// metadata can be of many different types
// you must test for type of metadata
if (metadata.Type() == HPS.Type.StringMetadata)
{
String metadataName = sm.GetName();
String someString = sm.GetValue();
}
else if (metadata.Type() == HPS.Type.DoubleMetadata)
{
String metadataName = dm.GetName();
double someDouble = dm.GetValue();
}
else if (metadata.Type() == HPS.Type.IntegerMetadata)
{
String metadataName = im.GetName();
int someInteger = im.GetValue();
}
else if (metadata.Type() == HPS.Type.UnsignedIntegerMetadata)
{
String metadataName = uim.GetName();
uint someUnsignedInt = uim.GetValue();
}
else if (metadata.Type() == HPS.Type.TimeMetadata)
{
TimeMetadata tm = new TimeMetadata((TimeMetadata) metadata);
String metadataName = tm.GetName();
uint someUnsignedInt = tm.GetValue();
// as a convenience, TimeMetadata can also be provided as a String
String timeString = tm.GetValueAsString();
}
}

Activating filters

Filters are found in some CAD files and provide the capability to show or hide certain pieces of geometry or objects that lie in certain layers. Filters can be toggled once a CAD file has been imported via the Visualize-Exchange API. Filters may also be toggled when captures are activated. Note that a filter can be attached to any component of the model. You may use CADModel::GetAllFilters to get a list of all Filter objects in the CADModel. The following code example shows how to activate a filter on a view:

[snippet 9.5.3.c]
CADModel cadModel = notifier.GetCADModel();
View myFirstView = cadModel.GetAllCaptures()[0].Activate();
// get list of filters
FilterArray filters = cadModel.GetAllFilters();
filters[0].Activate(myFirstView); // activate first filter
CADModel cadModel = notifier.GetCADModel();
// get list of filters
HPS.Filter[] filters = cadModel.GetAllFilters();
filters[0].Activate(view); // activate first filter

More than one filter may be active at a particular time. To activate or deactivate a filter, you first need to get a reference to it. You can get a list of all the active filters on a View by using this logic:

[snippet 9.5.3.d]
FilterArray filterArray = modelFile.GetActiveFilters(myFirstView);
for (size_t i = 0; i < filterArray.size(); i++)
{
Filter filter = filterArray[i];
// deactivates this filter
filter.Deactivate(myFirstView);
}
HPS.Filter[] filterArray = modelFile.GetActiveFilters(view);
for (int i = 0; i < filterArray.Length; i++)
{
HPS.Filter filter = filterArray[i];
// deactivates this filter
filter.Deactivate(view);
}

9.5.4 Exporting files

All file formats that Exchange can export are supported via the Visualize-Exchange API (ExportACIS, ExportIGES, ExportJT, ExportParasolid, ExportPRC, ExportSTEP, ExportSTL, ExportU3D, and ExportVRML). All export formats have their own Export*OptionsKit (except for VRML which has no options).

Writing a file through Exchange is an operation similar to reading:

[snippet 9.5.4.a]
try
{
}
catch (const IOException& ioe)
{
// handle error
}
try
{
Exchange.File.ExportJT(modelFile, filename, new Exchange.ExportJTOptionsKit());
}
catch (IOException ioe)
{
// handle error
}

Exchange can import many more file types than it can export. A list of supported file formats is located here (authentication required).

9.5.5 Event handling

During the import process, Exchange will trigger events when it detects a problem or has information to give you. For example, if Exchange couldn't find a referenced subcomponent in an assembly, or if there was some sort of import failure, a warning or error could be triggered. It is the responsibility of your application to catch these events and respond appropriately.

The Visualize-Exchange interface will capture all events and wrap them into one of three types: InformationEvent, ErrorEvent, or WarningEvent. Each of these types derive from Event, thus, handling them is identical to handling any other type of Visualize event (event handling is discussed here).

WarningEvent and InformationEvent contain both a code and a message field that help to determine what caused the event.

9.5.6 Data mapping

It is important for developers to be able to map objects within their Visualize database to the corresponding entities as loaded by HOOPS Exchange. Exchange first maps all incoming CAD data to the industry standard 'PRC' format, which in turn is then mapped to the Visualize scene graph. Visualize interfaces to each Exchange entity through an Exchange::Component object. Using a Exchange::Component, you can query the underlying PRC data for model information. Exchange objects are called entities and Visualize objects are components. Here is an example of some basic Exchange interactions:

[snippet 9.5.6.a]
// get Visualize-Exchange component interface object for corresponding Visualize key
Component component = modelFile.GetComponentFromKey(hpsKey);
// test if the object type is a product occurrence
// [see Component reference manual page for other Exchange types]
if (component.GetComponentType() == Component::ComponentType::ExchangeProductOccurrence)
{
// do something with this object
}
// gets the native Exchange entity that corresponds to the component
const A3DEntity* entity = ((Exchange::Component) component).GetExchangeEntity();
// gets the keys associated with the Component
KeyArray keys = component.GetKeys();
// at this point, you can use the returned entity to query model information using the Exchange API
$exchange_data_mapping

At the end of code snippet 9.5.6.a, Exchange is integrated, and you can use then use the entity references to manipulate model data using the Exchange API.

Using type masks

Commonly, it is necessary to determine whether a component contains a certain type of entity. If you are only looking for a specific type, you can use Component::GetComponentType to test against the returned type. But some generalized entities can take many forms. For example, PMI can exist as plain text, rich text, or GD&T, among other things. Rather than checking against all types of PMI, you can use a PMI mask with the HasComponentType function. This operation will tell you whether the component contains any type of PMI.

Type masks also exist for topology and representation items. A usage example is as follows:

[snippet 9.5.6.a]
if (component.HasComponentType(Component::ComponentType::ExchangePMIMask))
{
// it is a piece of PMI
}
if (component.HasComponentType(Component::ComponentType::ExchangeTopologyMask))
{
// it is topology
}
if (component.HasComponentType(Component::ComponentType::ExchangeRepresentationItemMask))
{
// it is a representation item
}
if (component.HasComponentType(HPS.Component.ComponentType.ExchangePMIMask))
{
// it is a piece of PMI
}
if (component.HasComponentType(HPS.Component.ComponentType.ExchangeTopologyMask))
{
// it is topology
}
if (component.HasComponentType(HPS.Component.ComponentType.ExchangeRepresentationItemMask))
{
// it is a representation item
}

9.5.7 Component creation using a custom importer

HOOPS Visualize takes care of all of the import logic for supported file formats, including HOOPS Exchange. In doing so, it creates an in-memory model structure which consists of the Component objects mentioned in the previous section. If you have a need to write your own importer, you will need a way to create these components as you parse the input file. HOOPS Visualize allows you to do this using its component creation API functions.

The first step in this process is to create the root component, which is the Exchange::CADModel. This is the only object in the Exchange structure that has no owner. From this point, you can build the model structure as you parse it, using the Exchange::Factory static class to create each component.

[snippet 9.5.7.a]
// create the CAD model
// ... parse file
// if your entity is a product occurrence
(cadModel, Component::ComponentType::ExchangeProductOccurrence, myEntity);
// create the CAD model
HPS.Exchange.CADModel cadModel = HPS.Exchange.Factory.CreateCADModel();
// ... parse file
// if your entity is a product occurrence
HPS.Exchange.Component component = HPS.Exchange.Factory.CreateComponent
(cadModel, HPS.Component.ComponentType.ExchangeProductOccurrence, myEntity);

Additionally, see the reference manual entries for Component::AddKey, Component::AddOwner, and Component::AddReference, which aid with component creation in certain situations. For example, to associate a geometry key will a component, use AddKey.

9.5.8 Limitations

As Exchange itself is only implemented for Windows and Linux, the Visualize-Exchange interface is not applicable to other operating systems.

It is also important to note that the Visualize-Exchange interface ignores B-rep data and asks HOOPS Exchange to provide all data in tessellated form.

Further reading

A further discourse on the inner workings of Exchange is beyond the scope of this manual, however, the main source for Exchange documentation can be viewed here (authentication required).