Duplicate faces in STEP file import

Good day, I am attempting to load STEP files using the STEPCAFControl_Reader class. I have a file ("KopieOrig.STEP" in the attached zip file), which has been exported from SolidWorks 2013 using the AP214 format, containing two solids. Each solid has some faces which are colored. When parsing the file using STEPCAFControl_Reader, we find that the root shape label contains 24 children, two of which are the solids, and 22 of which are copies of individual faces of the solids. These faces are indeed the ones with a dedicated color. None of these 24 nodes is marked as an assembly, nor a reference. Using XCAFDoc_ShapeTool methods we find that each node yields true for (and only for):

  • IsFree
  • IsSimpleShape
  • IsSubShape

CAD Assistant opens the file correctly, and shows 2 solids as the only children of the root node.

If we export the STEP file after loading it in CAD Assistant ("KopieCADAssi.step" in the attached zip file), we get the expected structure, with only two references to solids as children of the root node, while the two solids themselves are defined as siblings of the root node, and referred to. Here there is no duplication of faces.

We get the same result if we load the file in FreeCAD, move the two solid objects to be direct children of the project tree, and export ("KopieRoot.STEP").

We are using Open Cascade 7.8.0. While I cannot share our actual code, I have produced a minimal example of how we parse the document:

#include <iostream>

#include <TDataStd_Name.hxx>
#include <Standard_CString.hxx>
#include <TDocStd_Document.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <TDF_ChildIDIterator.hxx>
#include <XCAFDoc_ShapeTool.hxx>

static void PrintNodeRecursive(const TDF_Label& node, const int depth)
{
    // Print indentation.
    for (int i = 0; i < depth; i++)
    {
        std::cout << "    ";
    }

    // Print the current node.
    TCollection_ExtendedString name(node.Tag());
    Handle(TDataStd_Name) attribute = new TDataStd_Name();
    if (node.FindAttribute(attribute->ID(), attribute))
    {
        name = attribute->Get();
    }
    std::cout << "{" << name << "}";

    // Print some more information if the node is a (reference to) shape.
    if (XCAFDoc_ShapeTool::IsReference(node))
    {
        TDF_Label referred;
        XCAFDoc_ShapeTool::GetReferredShape(node, referred);
        TopoDS_Shape shape = XCAFDoc_ShapeTool::GetShape(node);
        if (!shape.IsNull())
        {
            std::cout << " REF -> ";
            TopAbs::Print(shape.ShapeType(), std::cout);
        }
    }
    else
    {
        TopoDS_Shape shape = XCAFDoc_ShapeTool::GetShape(node);
        if (!shape.IsNull())
        {
            std::cout << " ";
            TopAbs::Print(shape.ShapeType(), std::cout);
        }
    }

    std::cout << std::endl;

    // For each child, print its contents.
    for (TDF_ChildIterator it(node, false); it.More(); it.Next())
    {
        PrintNodeRecursive(it.Value(), depth + 1);
    }
}

static void PrintDocument(const Handle(TDocStd_Document) document)
{
    PrintNodeRecursive(document->Main(), 0);
}

int main(int argc, char** argv)
{
    // Load the step file.
    STEPCAFControl_Reader reader;
    reader.SetColorMode(true);
    reader.SetGDTMode(true);
    reader.SetLayerMode(true);
    reader.SetMatMode(true);
    reader.SetNameMode(true);
    reader.SetPropsMode(true);
    reader.SetSHUOMode(true);
    reader.SetViewMode(true);

    Handle(TDocStd_Document) document = new TDocStd_Document("storageformat");
    reader.Perform(argv[1], document);

    // Print document's content.
    PrintDocument(document);

    return 0;
}

Here is the result when parsing "KopieOrig.STEP":

{1}
    {Shapes}
        {Kopie von 00210860-_-921^79_05_V06} COMPOUND
            {1} SOLID
            {2} FACE
            {3} FACE
            {4} FACE
            {5} FACE
            {6} FACE
            {7} FACE
            {8} FACE
            {9} FACE
            {10} SOLID
            {11} FACE
            {12} FACE
            {13} FACE
            {14} FACE
            {15} FACE
            {16} FACE
            {17} FACE
            {18} FACE
            {19} FACE
            {20} FACE
            {21} FACE
            {22} FACE
            {23} FACE
            {24} FACE
    {Colors}
        {LIGHTCYAN3 (#7793A6FF)}
        {GRAY50 (#373737FF)}
        {GRAY94 (#D5DEE2FF)}
        {GOLDENROD2 (#DE6903FF)}
    {Layers}
    {D&GTs}
    {Materials}
    {Views}

Here is the result when parsing "KopieCADAssi.step":

{1}
    {Shapes}
        {Kopie von 00210860-_-921^79_05_V06} COMPOUND
            {1} REF -> SOLID
            {2} REF -> SOLID
        {SOLID} SOLID
            {1} FACE
            {2} FACE
            {3} FACE
            {4} FACE
            {5} FACE
            {6} FACE
            {7} FACE
            {8} FACE
            {9} FACE
            {10} FACE
            {11} FACE
            {12} FACE
        {SOLID} SOLID
            {1} FACE
            {2} FACE
            {3} FACE
            {4} FACE
            {5} FACE
            {6} FACE
            {7} FACE
            {8} FACE
            {9} FACE
            {10} FACE
    {Colors}
        {LIGHTCYAN3 (#7793A6FF)}
        {GRAY50 (#373737FF)}
        {GRAY94 (#D5DEE2FF)}
        {GOLDENROD2 (#DE6903FF)}
    {Layers}
    {D&GTs}
    {Materials}
    {Views}

The result when parsing "KopieRoot.STEP" is basically identical to the latter, except for the nodes having different names.

Any help in identifying the issue is appreciated.

Dmitrii Pasukhin's picture

Hello, CAD Asisstent use the "Expend" logic on the compounds. So, it is post-processing logic after import to prettier structure.

You can do the same by https://github.com/Open-Cascade-SAS/OCCT/blob/3462ea5716e69bb7f6a740e9bc...

or you can use 

https://github.com/Open-Cascade-SAS/OCCT/blob/3462ea5716e69bb7f6a740e9bc...

Best regards, Dmitrii.

 

Dmitrii Pasukhin's picture

And what about sub-shape structure of the XCAF. When you working with SimpleShape label and checkings its childs - IT is not a correct way.

Childs of the simple shape are not direct childs. They are just one of the subshape of the shapes below. The reason why we have labels for them - to attach attributes.

It means, when you iterate the SimpleShape, please to not extract geometry from its childs, just extract the shape from the main label and using childs just connect the attributes. All the childs already a part of extracted TopoDS_Shape.

Best regards, Dmitrii

Riccardo De Zen's picture

Hi, thank you for the swift answers. I did try using Expand, and the structure became more understandable, although it still had some issues.
About iterating the simple shape, I did find it weird that both the root node and the child nodes were simple shapes. We will try to fix the issue as you suggest.

Riccardo De Zen's picture

I followed your suggestion and on a few other test files everything behaves normally. I still have a doubt about in which order I should consider the child attributes. The only attribute I am currently interested in is the color of each face for proper mesh visualization. I am now parsing the children from the most top level shapes (in this case, the two solids) towards the least complex shapes (in this case, the 22 faces), but I am worried this might not be enough for more complex scenarios.

In case I find similar parsing issues in the future, are there more beginner-friendly resources for STEP parsing rules in Open Cascade, aside from the STEP file standard?

Thank you for help in solving the initial issue.

Best regards, Riccardo