CAD Exchanger SDK
Product Structure

ModelData_Model class

The ModelData_Model class encapsulates a data structure which eventually contains entire information on the 3D model. ModelData_Model contains a product structure, or a hierarchy of elements, often called a scene graph.

The model object is used by readers and writers when converting across supported formats, for instance:

ModelData_Model aModel;
STEP_Reader aReader;
bool anIsOk = aReader.ReadFile(aSource) && aReader.Transfer (aModel);
Para_Writer aWriter;
anIsOk = aWriter.Transfer (aModel) && aWriter.WriteFile (aDest);

Scene Graph

Product structure is provided via a graph that describes hierarchical relationship between the elements.

The graph (often called a 'scene graph') supports the following element types (which are subclasses of ModelData_SceneGraphElement):

  • Part is a leaf node in a graph, corresponding to a mechanical part or a product. A part is something logically undivisible and often consists of a single body (e.g. solid body).
  • Assembly is a group which contains zero, one or more graph elements, including nested sub-assemblies.
  • Instance Instance is a particular occurence of (or a reference to) another element (part or assembly). Allows to share elements and associate individual attributes (e.g. colors) and transformation matrices.

Example - as1 assembly

Below is an example of an assembly named "as1" (STEP file can be downloaded from here) and its graph. Assemblies are shown with the folder icon (e.g. l-bracket-assembly), instances - with the arrow icons (e.g. nut-bolt-assembly_1), parts - with the sheet-like icons (e.g. bolt).

dm_assembly.png
Assembly
dm_assembly_sg.png
Scene graph of an assembly model

This example will be referred to when explaining concepts of the data model.

Scene Graph Creation

The following code demonstrates creation of a graph:

ModelData_Assembly aRoot ("as1");
aModel.AddRoot (aRoot);
...
ModelData_Assembly aNBA ("nut-bolt-assembly");
ModelData_Part aBolt ("bolt");
aNBA.AddInstance (aBolt, aBTransformation, "bolt_1");
...
ModelData_Assembly aLBA ("l-bracket-assembly");
aLBA.AddInstance (aNBA, aNBATransformation, "nut-bolt-assembly_1");
aRoot.AddInstance (aLBA, aLBATransformation, "l-bracket-assembly_1");
...

Element types

Part

Part (ModelData_Part) is a leaf node in a graph, corresponding to a mechanical part. A part is something logically undivisible (in the context of application). A part refers to representations (see Part representations) which define geometrical shape of the part. Depending on an input format the part may have precise geometrical representation (B-Rep) and/or an approximated polygonal one.

The following picture demonstrates a bolt part from the assembly as1:

dm_bolt.png
Part

Assembly

Assembly (ModelData_Assembly) is a group which contains zero, one or more graph elements, including nested sub-assemblies.

The following picture demonstrates the nut-bolt-assembly from the assembly as1. The nut-bolt-assembly includes two parts - bolt and nut.

dm_nut_bolt_assembly.png
Assembly

Assembly may refer to associated properties.

Instance

Instance (ModelData_Instance) is a particular occurence of some element (part or assembly). Instances allow to share elements and to associate individual attributes (e.g. colors) and transformation matrices.

The following picture demonstrates the l-bracket-assembly, which includes a single l-bracket part (in green) and three instances of the nut-bolt-assembly, each with different transformation matrix.

dm_l_bracket_assembly.png
Assembly with 3 instances of the same sub-assembly

Each graph element can be assigned a name, which is a Unicode string:

Base_UTF16String aName ("nut");
aPart.SetName (aName);
...
aName = anAsm.Name();
if (!aName.IsEmpty()) {
//non-empty name
}

A graph element may also refer to:

Sharing graph elements

Parts and (sub-)assemblies may be shared between assemblies. For instance, in the example above, the "nut-bolt-assembly" is used three times by the "l-bracket-assembly".

Sharing is enabled by using unique instances which refer to the same shared element and have unique transformation matrices and names, for instance:

ModelData_Assembly aLBA ("l-bracket-assembly");
aLBA.AddInstance (aNBA, aNBATransformation1, "nut-bolt-assembly_1");
aLBA.AddInstance (aNBA, aNBATransformation2, "nut-bolt-assembly_2");
aLBA.AddInstance (aNBA, aNBATransformation3, "nut-bolt-assembly_3");

Instances shall always be unique and shall never be shared.

Traversal of the product structure

The product structure can be traversed using two approaches:

  • with the help of iterators;
  • with the help of visitors.

Traversal using iterators

Iterators support a Java-style interface.

The root elements of the graph can be traversed as follows:

ModelData_Model::Iterator anIterator (aModel);
while (anIterator.HasNext()) {
const ModelData_SceneGraphElement& aSGE = anIterator.Next();
//...
}

To traverse the assembly children the iterator can be used as follows:

const ModelData_Assembly& anAsm = ...;
ModelData_Model::Iterator anIterator (anAsm);
while (anIterator.HasNext()) {
const ModelData_SceneGraphElement& aSGE = anIterator.Next();
const ModelData_Instance& anInstance = static_cast<const ModelData_Instance&> (aSGE);
//...
}

Traversal using visitors

The graph implements the visitor pattern, and even more specifically the hierarchical visitor pattern.

Each graph element (part, assembly, instance) accepts a visitor which must be a subclass of an abstract class ModelData_Model::ElementVisitor with redefined methods to visit respective element type. The element invokes respective visitor's methods and (recursively) sends the visitor to nested elements. This mechanism ensures visiting of entire graph or a sub-tree starting with some particular element.

The following example demonstrates how to count types of all elements in the graph:

class ElementCounter : public ModelData_Model::ElementVisitor
{
public:
ElementCounter() : myAsmCount (0), myInstanceCount (0), myPartCount (0) {}
size_t AsmCount() const { return myAsmCount; }
size_t InstanceCount() const { return myInstanceCount; }
size_t PartCount() const { return myPartCount; }
virtual bool VisitEnter (const ModelData_Assembly& /*theAssembly*/) override { ++myAsmCount; return true; }
virtual void VisitLeave (const ModelData_Assembly& /*theAssembly*/) override {}
virtual bool VisitEnter (const ModelData_Instance& /*theInstance*/) override { ++myInstanceCount; return true; }
virtual void VisitLeave (const ModelData_Instance& /*theInstance*/) override {}
virtual void operator() (const ModelData_Part& /*thePart */) override { ++myPartCount; }
private:
size_t myAsmCount;
size_t myInstanceCount;
size_t myPartCount;
};
ElementCounter aCounter;
aSG.Accept (aCounter);
std::cout << "The graph contains " <<
aCounter.AsmCount() << " assembly(ies), " <<
aCounter.InstanceCount() << " instance(s), " <<
aCounter.PartCount() << " part(s)" << std::endl;

If the redefined VisitEnter() method returns false then the respective assembly or instance will not be visited, i.e. assembly's children or instance's referred element will be skipped.

Traversal may start at any element in the hirarchy. For instance, to count the number of children and the element itself the above visitor can be used as follows:

ModelData_SceneGraphElement& aSGE = ... ; //either part, or assembly, or instance
ElementCounter aCounter;
aSGE.Accept (aCounter);
std::cout << "The element contains " << aCounter.PartCount() << " part(s)" << std::endl;

To reduce the amount of user code to be created, the data model offers a few helper classes:

Validity Requirements

Data model contents must meet certain requirements in order to ensure correct processing by CAD Exchanger. Key requirements are documented below:

Product Structure Requirements

Product structure must meet the following requirements:

  • A graph may have zero, one or more root elements.
  • Roots may be parts and assemblies only. Instances may not be roots.
  • Assembly's children are always instances, so an instance shall be created to refer to a part or a nested sub-assembly.
  • A part may have zero or one B-Rep representation and/or zero, one or more polygonal representation.
  • Bodies of B-Rep representation (inside the same part or between the parts) may not share subshapes. Each subshape must have a single parent body.