API Conventions
Type Definitions
The file A3DSDKTypes.h provides essential fixed-size numeric types used throughout the API:
Type |
Description |
Standard Type Equivalent |
---|---|---|
|
Boolean descriptor. Can be |
|
|
Unsigned integer type with width of exactly 8 bits |
|
|
Signed integer type with width of exactly 8 bits |
|
|
Unsigned integer type with width of exactly 16 bits |
|
|
Signed integer type with width of exactly 16 bits |
|
|
Signed integer type with width of exactly 32 bits |
implementation dependent |
|
Unsigned integer type with width of exactly 32 bits |
implementation dependent |
|
IEEE 754 64-bits floating-point number |
|
|
IEEE 754 32-bits floating-point number |
|
|
Void type Void type |
|
|
Void pointer type type Void pointer type type |
|
|
|
Architecture
The HOOPS Exchange architecture is closely tied to its naming design. Most data in your HOOPS application are manipulated through entities, such as PRC, features, or BIM entities. Each entity type consists of:
An enumerator value within
A3DEEntityType
used for identification.A handle type that uniquely identifies the entity within HOOPS.
Optionally, a data structure for retrieving or editing the entity’s state.
Additionally, the API may provide CRUD-like functions for each entity type to manipulate its content. Not all entity types offer all functions, depending on relevance and indirect operations.
The following table illustrates the strict naming convention used for an entity type with some examples:
Model File |
BIM Data |
NURBS Curve |
Topo Body |
|
---|---|---|---|---|
Handle Type |
|
|
|
|
|
|
|
|
|
|
||||
Create |
||||
Get |
||||
Set |
||||
Delete |
Querying the Entity Type of a Handle
When you have an entity handle and need to determine its underlying type, use A3DEntityGetType
and match the result to A3DEEntityType
.
For example, to check if pHandle
is a model file:
A3DEEntityType eType = kA3DTypeUnknown;
A3DEntityGetType(pHandle, &eType);
assert(eType == kA3DTypeAsmModelFile);
Retrieving the Data of an Entity
The most common operation on an entity is retrieving its data for reading. This involves initializing the data structure, retrieving the entity data, and disposing of the data structure when no longer needed. Here’s an example function that retrieves and prints the number of children product occurrences:
void PrintChildrenPO(const A3DAsmProductOccurrence* pHandle)
{
A3DAsmProductOccurrenceData sData;
A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sData);
A3DAsmProductOccurrenceGet(pHandle, &sData);
printf("Number of children: %d\n", pData->m_uiPOccurrencesSize);
A3DAsmProductOccurrenceGet(0, &sData);
}
Let’s detail this code line by line.
To initialize a data structure, declare it as a non-const variable use the A3D_INITIALIZE_DATA
macro:
A3DAsmProductOccurrenceData sData;
A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sData); // don't use &
Once the structure is initialized, use the Get function to retrieve the data.
In the following line, pHandle
is of type const A3DAsmProductOccurrence*
.
A3DAsmProductOccurrenceGet(pHandle, &sData);
Then, when the data is not needed anymore, give the data back to the API by calling the same function. Set the handle parameter to 0.
A3DAsmProductOccurrenceGet(0, &sData);
Note
Why disposing the data structure?
HOOPS Exchange structures may contains heap allocated data such as arrays and strings. It is essentials to remain memory consistent by letting the API releasing it. See the section about memory management for more.
Arrays as struct
Fields
Lists in HOOPS Exchange offer access to different data fields, like product occurrences in a model file or representation items in an A3DRiSet
.
To work with these lists, HOOPS Exchange uses standard C sequential array types.
Each array is composed of two parts: an A3DUns32
field to store the array’s size and a pointer to the array itself.
typedef struct {
// ...
A3DUns32 m_uiPOccurrencesSize;
A3DAsmProductOccurrences** m_ppOccurrences;
// ...
} A3DAsmModelFile;
Some array size specifiers can describe multiple arrays, illustrating their close relationship.
Entity Hierarchy
The HOOPS architecture incorporates an abstraction and specialization mechanism for its entities.
For instance, all entities can be abstracted as an A3DEntity
type, while the majority of entities are specializations of A3DRootBase
.
In practical terms, this means that when an entity can be abstracted into another type, it can be safely manipulated as the abstract type..
Certain abstract types also have their own data structures.
For example, any product occurrence represented as an A3DAsmProductOccurrence
is also considered a root base entity, specifically an A3DRootBase
.
Therefore, the code remains valid even if pHandle
is an instance of A3DAsmProductOccurrence*
.
A3DRootBaseData sData;
A3D_INITIALIZE_DATA(A3DRootBaseData, sData);
A3DRootBaseGet(pHandle, &sData);
Conversely, it’s possible to pass a handle of type A3DRootBase*
to a function that anticipates an A3DAsmProductOccurrence*
.
In such cases, the responsibility falls upon the caller to ensure the entity’s correct type.
For instance, this example verifies the type of pHandle
before passing it to the PrintChildrenPO
function, as shown in the previous example.
A3DEEntityType eType = kA3DTypeUnknown;
A3DEntityGetType(pHandle, &eType);
if (eType == kA3DTypeAsmProductOccurrence) {
PrintChildrenPO(pHandle);
}
Any API call using an unexpected entity type will return A3D_INVALID_ENTITY_TYPE
.
Note
Because it’s similar to but not exactly like object-oriented programming (OOP), the way entities are organized can be something confusing. Another way to understand the relationship between entity types is to think of HOOPS Exchange as a database. For instance, in this example, the term “pHandle” would be like a key that exists in both the “ProductOccurrence” and “A3DRootBase” tables.
Structural Hierarchy
Many complex structures in HOOPS Exchange are organized like trees. For instance, the PRC tree contains all the parts that make up a model file. And the Feature tree arranges all the steps a CAD application takes to create the final part. Within these structures, one entity instance can be the “parent” of one or more other entities.
Although there’s no direct connection between the structural and type hierarchies, it’s worth noting that entities in the same tree usually belong to the same entity type.
For example, entities in a PRC tree can be things like A3DAsmProductOccurrence,
A3DRiRepresentationItem,
or A3DRiPart
(and others), but they’re all types of A3DRootBase.
Memory Management
Since HOOPS Exchange is a C API, you need to explicitly free any memory that’s allocated during runtime after you’re done using it. Many functions and data structures provide such memory, but it’s up to your application to decide when to release it.
One common scenario for memory cleanup is when you’re retrieving entity data, as shown in the example for entity data retrieval.
If the Get function allocates memory, it’s important to note that these memory blocks are specific to that particular function call.
For example, in the following case, sData0.m_pcName
and sData1.m_pcName
point to different memory locations, even though they belong to the same entity.
This happens because the Name of a root base is copied into the A3DRootBaseData parameter internally.
A3DRootBaseData sData0, sData1;
A3D_INITIALIZE_DATA(sData0);
A3DRootBaseGet(pHandle, &sData0);
A3D_INITIALIZE_DATA(sData1);
A3DRootBaseGet(pHandle, &sData1);
assert(sData0.m_pcName != sData1.m_pcName);
After you’ve finished using the code, you need to return both sData0
and sData1
to the library so that it can properly clean up the allocated memory.
You can do this by making another call to A3DRootBaseGet
with 0 as the handle.
A3DRootBaseGet(0, &sData1);
A3DRootBaseGet(0, &sData0);
Warning
Even if a data structure currently doesn’t use dynamically allocated memory, it might in future API versions. To ensure your code remains robust and avoids tricky memory leaks due to changes, it’s a good practice to always clean up your data structures, regardless of their type.
Certain functions also provide dynamically allocated memory using “out parameters” In these cases, you can find details on how to release that memory in the function’s documentation.
Providing the Memory Functions
If you want to control how memory is allocated within HOOPS API functions, you can set your own allocation and deallocation functions:
A3DPtr CustomAlloc(size_t uiSize)
{
return malloc(uiSize);
}
A3DVoid CustomFree(A3DPtr pBlock) {
free(pBlock);
}
A3DDllSetCallbacksMemory(CustomAlloc, CustomFree);
A3DDllSetCallbacksMemory
must be called before A3DDllInitialize
.
Deleting Entities
Deleting an entity itself isn’t a common operation in HOOPS Exchange, but some entity types do offer a delete function.
When you use A3DModelFileDelete()
on a model file entity, it gets entirely removed.
This action includes all the A3DRootBase
entities that make up the PRC structure.
Most of the time, this is the only delete function you need.
The more general-purpose A3DEntityDelete()
function can be used to delete a specific entity.
It removes the entity itself and any child entities in the case of an A3DRootBase
.
It’s important to note that this function doesn’t check whether the given entity exists somewhere in the PRC tree.
Some specific entities provide their own Delete() function:
A3DFaceUVPointInsideManagerDelete
A3DFileContextDelete
A3DMiscCascadedAttributesDelete
A3DMkpRTFDelete
A3DMkpRTFFieldDelete
A3DProjectPointCloudManagerDelete
Garbage Collection
When you shut down the library, the HOOPS context takes care of releasing any remaining memory blocks. This includes internal buffers and unlinked entities.
Error Management
HOOPS manages errors by returning a status code from all its functions.
If a function completes without errors, it returns the value A3D_SUCCESS
(which is 0).
Any non-zero value indicates an error.
You can find a comprehensive list of error codes in the A3DStatus
enumeration within the A3DSDKErrorCodes.h file.
It’s important to note that the same error code might have different meanings depending on the function that returns it. For detailed information about a value returned by a specific function call, consult the documentation of that function.
A3DMiscGetErrorMsg()
can be used to obtain a description of the error code:
// ...
A3DStatus iStatus = A3DAsmModelFileLoadFromFile(pcPath, &sLoadData, &pHandle);
if (iStatus) {
fprintf(stderr, "Cannot load model file: '%s'\n", A3DMiscGetErrorMsg(iStatus));
}
String Encoding
HOOPS uses standard C-Strings for handling text, which are in-memory arrays of characters with a null-termination character (0).
When dealing with character data, HOOPS Exchange adopts UTF-8 encoding. Therefore, any function or structure that needs strings as input must ensure proper conversion to UTF-8 before use. The API offers utility functions for these conversions:
A3DMiscUTF16ToUTF8
A3DMiscUTF8ToUTF16
A3DMiscUTF8ToUnicode
A3DMiscUnicodeToUTF8
Code Conventions
HOOPS Exchange is a C API, which means any source code, including our headers, is assured to compile with a C or C++ compliant compiler. To get detailed information about the compiler requirements, you can refer to the platform requirements page.
Header Files
All the header files can be found directly under the include/ folder of your HOOPS installation. These files are named using PascalCase and have one of the following prefixes:
A3DSDK for most of our SDK.
A3DPDF for files specific to the HOOPS Publish SDK.
A3DCommon for code that is common to both APIs.
The only exception is hoops_license.h. This file should be replaced with the one typically obtained from the license file generator.
All our header files contain only the declarations and inclusions they need for compilation.
They are also designed to be self-sufficient, meaning they don’t depend on the inclusion context to work correctly, as long as the #include
directive is at the global scope.
To compile a source file using HOOPS declarations, you only need to include the relevant header file that contains the declaration you require.
For example, including A3DSDKStructure.h is sufficient to compile a source code that uses the A3DAsmModelFileData
structure.
If you want to include all API header files in one go, you can use the convenience header A3DSDKIncludes.h. This file is also included within A3DSDKLoader.h to ensure that all functions are loaded correctly during initialization.
Naming Conventions
The API follows certain naming conventions:
Symbol names are in PascalCase, with the exception of macro definitions and
A3DStatus
values, which use CAPITAL_CASE.Variables, formal parameters, and struct members use a naming convention known as Systems Hungarian Notation:
Struct members begin with
m_
, followed by:p
for pointer type, and thenA prefix based on the data type:
b
forA3DBool
i
forA3DInt8
,A3DInt16
, andA3DInt32
ui
forA3DUns8
,A3DUns16
, andA3DUns32
f
andd
forA3DFloat
andA3DDouble
, respectivelyc
forA3DUTF8Char
e
for enumerationss
for structures
Namespacing
All data types and functions are organized using a series of prefixes.
The primary prefix is always A3D
, and it’s followed by additional prefixes to create sub-namespaces for functions and related symbols.
For example, A3DTess3DCreate()
and A3DTessMarkupCreate()
share the same base name but belong to different sub-namespaces, namely Tess3D
and TessMarkup
, respectively.
Associated symbols also follow the same naming pattern:
A3DAsmModelFile
: This represents an entity handle for an assembly model file.A3DAsmModelFileData
: This is a data structure that describes a model file.A3DAsmModelFileLoadFromFile()
: This function is responsible for loading a model file into memory.