Model class
The ModelData::Model class encapsulates a data structure which eventually contains entire information about 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:
ModelData::Model aModel;
ModelData::ModelReader aReader;
bool anIsOk = aReader.Read (aSource, aModel);
Product structure is provided via a graph that describes hierarchical relationship between the elements.
The graph supports the following element types (which are subclasses of ModelData::ModelElement ):
- ModelData::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).
- ModelData::Assembly is a group which contains zero, one or more graph elements, including nested sub-assemblies.
- ModelData::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" and its graph.
Assembly
|
Scene graph of an assembly model
|
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
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 has representations (see Part representations) which define geometrical shape of the part.
The following picture demonstrates a bolt part from the assembly as1:
Part
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 - a bolt and a nut.
Assembly
Instance
ModelData::Instance is a particular occurence of some element (part or assembly). Instances allow to share elements and to associate 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 a different transformation matrix.
Assembly with 3 instances of the same sub-assembly
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.
Validity Requirements
Data model contents must meet certain requirements in order to ensure correct processing by Manufacturing Toolkit. Key requirements are documented below:
Product Structure Requirements
Product structure must meet the following requirements:
- 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 Boundary Representation.
- Bodies of B-Rep (inside the same part or between the parts) may not share subshapes. Each subshape must have a single parent body.
Traversal of the product structure
The product structure can be traversed using two approaches:
Traversal using iterators
Iterators support a Java-style interface.
The root elements of the graph can be traversed as follows:
ModelData::ModelElementIterator anIterator (aModel);
while (anIterator.HasNext()) {
const ModelData::ModelElement& anElement = anIterator.Next();
}
To traverse the assembly children the iterator can be used as follows:
const ModelData::Assembly& anAsm = ...;
ModelData::ModelElementIterator anIterator (anAsm);
while (anIterator.HasNext()) {
const ModelData::ModelElement& anElement = anIterator.Next();
const ModelData::Instance& anInstance = static_cast<const ModelData::Instance&> (anElement);
}
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::ModelElementVisitor 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::ModelElementVisitor
{
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& ) override { ++myAsmCount; return true; }
virtual void VisitLeave (const ModelData::Assembly& ) override {}
virtual bool VisitEnter (const ModelData::Instance& ) override { ++myInstanceCount; return true; }
virtual void VisitLeave (const ModelData::Instance& ) override {}
virtual void operator() (const ModelData::Part& ) override { ++myPartCount; }
private:
size_t myAsmCount;
size_t myInstanceCount;
size_t myPartCount;
};
ElementCounter aCounter;
aModel.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 hierarchy. For instance, to count the number of children and the element itself the above visitor can be used as follows:
ModelData::ModelElement& anElement = ... ;
ElementCounter aCounter;
anElement.Accept (aCounter);
std::cout << "The element contains " << aCounter.PartCount() << " part(s)" << std::endl;
Taking transformation matrices into account
When traversing the assembly graph which may contain multiple shared components, make sure to track transformation matrices applied to different instances along the graph in order to correctly convert local coordinates to global ones. This is typically organized using a stack that contains transformation matrices accumulated while traversing the tree.
The following example provides some excerpts of how the exploration could be organized:
class SceneGraphVisitor : public ModelData::ModelElementVisitor
{
public:
SceneGraphVisitor()
{
Geom::Transformation anIdentity;
myTrsfs.push (anIdentity);
}
protected:
virtual bool VisitEnter (const ModelData::Instance& theInstance) override
{
Geom::Transformation aTrsf;
if (theInstance.HasTransformation()) {
aTrsf = theInstance.Transformation();
}
Geom::Transformation aCumulativeTrsf = CurrentTransformation() * aTrsf;
myTrsfs.push (aCumulativeTrsf);
return true;
}
virtual void VisitLeave (const ModelData::Instance& ) override
{
myTrsfs.pop();
}
const Geom::Transformation& CurrentTransformation() const
{
return myTrsfs.top();
}
private:
std::stack<Geom::Transformation> myTrsfs;
};
Helper Visitor classes
To reduce the amount of user code to be created, the data model offers a few helper classes: