Reading and displaying STEP/IGES assemblies using XDE

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_AISObjects 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? 

 

Kirill Gavrilov's picture

Just a small tip - check how shape looks in Draw Harness when displayed by XDisplay command - it uses XCAFPrs_AISObject presentation as well.

pload ALL
ReadStep D file.stp
XDisplay -dispMode 1 D
vfit
Ajith Subramanian's picture

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 a TDF_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 absolute TopLoc_Location  for each XCAFPrs_AISObject based on the scene graph, but it appears it does not do this. 

Is there any way to transform an XCAFPrs_AISObject using a TopLoc_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!

Attachments: 
Kirill Gavrilov's picture

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.

Ajith Subramanian's picture

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 its TDF_Label, is there any way to tie this to the selection mode of the viewer so I can select this sub-shape within the XCAFPrs_AISObject programmatically?

Thanks again for your help.

2265636806_166500's picture

Hi:

 here, child->Name = L"[Unnamed]";          if no name attached, how to add name to Geometric Entity?

Thanks.