
Mon, 03/02/2020 - 22:09
I'm working on a small application that reads STEP/IGES assemblies and displays the parts and the assembly structure. The goal of the application is similar to CAD Assistant in that I want to open a file, view its assembly hierarchy and display the model with shapes corresponding to the assembly and its components that are individually selectable. The file translation is handled by XDE (e.g. TDocStd_Document
and STEPCAFControl_Reader
) and I'm creating XCAFPrs_
AISObject
s to display the objects in a V3d_Viewer
. If I'm not mistaken, CAD Assistant uses XDE, so the above seems like a natural approach to this problem.
The assembly structure is built recursively, with each node represented by a AssemblyNode
structure:
struct AssemblyNode
{
std::vector<AssemblyNode> Children;
std::shared_ptr<AssemblyNode> Parent;
TDF_Label Label;
std::wstring Name;
};
A method called GetRootsFromDocument
builds the actual tree as a list of root nodes and child nodes are read recursively by GetChildren
:
std::vector<AssemblyNode> GetChildren(
const std::shared_ptr<AssemblyNode> &parent,
const Handle(XCAFDoc_ShapeTool) &shapeTool,
const TDF_Label &parentLabel)
{
TDF_LabelSequence components;
std::vector<AssemblyNode> children;
if (shapeTool->GetComponents(parentLabel, components, Standard_False))
{
Standard_Integer componentCount = components.Length();
for (Standard_Integer compIndex = 1; compIndex <= componentCount; ++compIndex)
{
auto child = std::make_shared<AssemblyNode>();
TDF_Label componentLabel = components.Value(compIndex);
TDF_Label shapeLabel;
if (!shapeTool->GetReferredShape(componentLabel, shapeLabel))
{
shapeLabel = componentLabel;
}
Handle(TDataStd_Name) shapeNameAttr;
if (shapeLabel.FindAttribute(TDataStd_Name::GetID(), shapeNameAttr))
{
child->Name = shapeNameAttr->Get().ToWideString();
}
else
{
child->Name = L"[Unnamed]";
}
child->Label = componentLabel;
child->Parent = parent;
child->Children = GetChildren(child, shapeTool, shapeLabel);
children.push_back(*child);
}
}
return children;
}
std::vector<AssemblyNode> GetRootsFromDocument(Handle(TDocStd_Document) doc)
{
TDF_Label mainLabel = doc->Main();
Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(mainLabel);
TDF_LabelSequence rootLabels;
shapeTool->GetFreeShapes(rootLabels);
Standard_Integer rootLabelCount = rootLabels.Length();
std::vector<AssemblyNode> roots;
for (Standard_Integer rootLabelIndex = 1; rootLabelIndex <= rootLabelCount; ++rootLabelIndex)
{
TDF_Label rootLabel = rootLabels.Value(rootLabelIndex);
std::shared_ptr<AssemblyNode> root = std::make_shared<AssemblyNode>();
Handle(TDataStd_Name) nameAttr;
if (rootLabel.FindAttribute(TDataStd_Name::GetID(), nameAttr))
{
root->Name = nameAttr->Get().ToWideString();
}
root->Label = rootLabel;
root->Children = GetChildren(root, shapeTool, rootLabel);
roots.push_back(*root);
}
return roots;
}
Finally, the shapes themselves are displayed in the viewer as follows:
void DisplayShapeXCAF(const AssemblyNode &node, Standard_Boolean updateViewer)
{
Handle(XCAFPrs_AISObject) displayedShape = new XCAFPrs_AISObject(node.Label);
myAISContext->Display(displayedShape, updateViewer);
}
Unfortunately, while I'm able to parse the labels in the TDocStd_Document
and create the assembly structure, I'm not having much success with displaying each assembly node's corresponding shape correctly. I have attached a sample STEP file (Gate_Sample_Model.STEP) and a screenshot of what the output of my program looks like (GateAssembly.png). I have also attached a screenshot of what this STEP file looks like when imported in CAD Assistant (GateModelCADAssistant.png).
In the STEP file, component "NAUO5" is a located reference of the assembly "GateModel", which itself contains a sub-assembly reference to the shape "T-2 OPT-2", which contains a reference to "CHUTE T-2 DIVERTER".
It seems that simply using the XCAFPrs_AISObject
generated from the TDF_Label
for a sub-assembly doesn't take into account parent transformations. Do I have to walk the tree and manually apply transformations for sub-assemblies?
Mon, 03/02/2020 - 22:32
Just a small tip - check how shape looks in Draw Harness when displayed by XDisplay command - it uses
XCAFPrs_AISObject
presentation as well.Tue, 03/03/2020 - 01:26
Thanks for your quick reply. I tried running the commands you mentioned and it appears to display the model correctly (see attached picture).
I think I've figured out my issue, but I don't know how to solve it. I think I have to build a transformation for every component based on its parent nodes (i.e. document labels that are "users" of a given component) in order to display it in its absolute location. Simply creating a
XCAFPrs_AISObject
from aTDF_Label
corresponding to a component doesn't account for its parent transformations. This is where I was confused since I assumed the XDE/XCAF API would automatically resolve the absoluteTopLoc_Location
for eachXCAFPrs_AISObject
based on the scene graph, but it appears it does not do this.Is there any way to transform an
XCAFPrs_AISObject
using aTopLoc_Location
object? I'd still like to be able to use the XCAF/XDE presentation for the object since I'm using XCAF document labels to maintain associativity with the UI of my program (i.e. highlighting a part in an assembly after clicking a node in the tree UI)?Thanks again for your help!
Tue, 03/03/2020 - 04:08
TDF_Label, which you are creating XCAFPrs_AISObject from, is like a pointer, that can appear multiple times in the document within different parent labels. That's why label on it own cannot provide you final transformation of the object.
Tue, 03/03/2020 - 21:54
I think I understand a little bit more. My goal is to be able to highlight and select individual parts within an assembly. This is why I was using the
TDF_Label
of the sub-shapes, although I see now that is the wrong approach.Is there any way to tell the viewer to highlight a sub-shape given its assembly
XCAFPrs_AISObject
? For example, if I have a sub-shape's label entry "0:1:1:1:2" or itsTDF_Label
, is there any way to tie this to the selection mode of the viewer so I can select this sub-shape within theXCAFPrs_AISObject
programmatically?Thanks again for your help.
Tue, 01/05/2021 - 06:29
Hi:
here, child->Name = L"[Unnamed]"; if no name attached, how to add name to Geometric Entity?
Thanks.
Thu, 03/10/2022 - 10:14
hello,I'm doing the same thing, can you give me your source code for reference?