Extracting solid names and colours from step file - XCAFPrs_DocumentExplorer nodes are TopAbs_COMPOUNDS only.

Hello,

I'm trying to load a step file, print its structure and show each componet / solids name and depth in the structure tree, along with the RGB value of any solids.

https://unlimited3d.wordpress.com/2022/09/09/colored-step-model-in-occt-...

Using this turorial which explaines things well. This tutorial shows they can print names for compounds and shapes. 

I can use an XCAFPrs_DocumentExplorer to get CAF components as nodes, and print the structure tree but it only shows the nodes which are TopAbs_COMPOUNDS. I can then access the sub shapes, which are the TopAbs_SOLIDS, and retrieve the RGB values of these using a ShapeTool and ColourTool, but I can't get the name os TopAbs_SOLIDS via the attribute access.

I can see the solid names in the STEP file, "#89=MANIFOLD_SOLID_BREP('sphere_black',#166);".

Some other posts suggest similar issues, and that they had to use alternate methods (https://dev.opencascade.org/content/how-get-manifoldsolidbrep-entity-nam...). But i've not had much luck accessing the solids this way as I get segfaults.

Could anyone see what I'm doing wrong, or suggest altername methods, please? I'm using Open Cascade 7.8 on Rocky Linux 8.6. 

Thanks in advance

#include <iostream>
#include <string>
#include <vector>
#include <OSD.hxx>

#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Compound.hxx>
#include <BRep_Builder.hxx>
#include <BRepAlgoAPI_Algo.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <BOPAlgo_Splitter.hxx>
#include <BRepPrimAPI_MakeHalfSpace.hxx>
#include <STEPCAFControl_Controller.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <TDocStd_Document.hxx>
#include <XCAFApp_Application.hxx>
#include <XCAFPrs_DocumentExplorer.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_ColorType.hxx>
#include <XCAFPrs.hxx>
#include <XCAFPrs_AISObject.hxx>
#include <BinXCAFDrivers.hxx>
#include <TDataStd_Name.hxx>
#include <TopExp_Explorer.hxx>
#include <TDataStd_Name.hxx>
#include <Interface_EntityIterator.hxx>
#include <StepData_StepModel.hxx>


bool loadStepFile(std::string fileName, Handle(TDocStd_Document) doc) {
    // set up bool flag to return 
    bool fileRead, fileTransfered;

    // initialise step caf controller 
    STEPCAFControl_Controller::Init();

    // set up the step file reader
    STEPCAFControl_Reader stepReader;
    stepReader.SetColorMode(true);
    stepReader.SetNameMode(true);
    stepReader.SetMatMode(true);
    stepReader.SetGDTMode(true);
    stepReader.SetLayerMode(true);
    stepReader.SetPropsMode(true);
    stepReader.SetSHUOMode(true);
    stepReader.SetViewMode(true);

    // IFSelect_ReturnStatus readFileStatus = ;
    if (stepReader.ReadFile(fileName.c_str()) == IFSelect_RetDone){
        std::cout << "[OK] file loaded" << std::endl;
        fileRead = true;
    }
    else{
        std::cout << "[ERROR] file failed to load" << std::endl;
        fileRead = false;
    }
    
    // transfer model to document
    if (stepReader.Transfer(doc)) {
        std::cout << "[OK] model transfered to documnet" << std::endl;
        fileTransfered = true;
    }
    else{
        std::cout << "[ERROR] model transfer failed" << std::endl;
        fileTransfered = false;
    }

    return true;
}

TCollection_AsciiString shapeTypeAsString(TopoDS_Shape shape) {
    if (shape.ShapeType() == TopAbs_COMPOUND) {
        // std::cout << "shape type = TopAbs_COMPOUND" << std::endl;
        return " (TopAbs_COMPOUND)";
    }
    else if (shape.ShapeType() == TopAbs_SOLID) {
        return " (TopAbs_SOLID)";
    }
    else {
        // std::cout << "shape type nor TopAbs_COMPOUND or TopAbs_SOLID" << std::endl;
        return " (not TopAbs_COMPOUND or TopAbs_SOLID)";
    }
}

std::vector<int> getColour(TopoDS_Shape shape, TDF_Label label, Handle(XCAFDoc_ColorTool) colourTool) {
    Quantity_ColorRGBA colourSurf;
    Quantity_ColorRGBA colourGen;
    Quantity_ColorRGBA colourCurv;
    bool hasColourSurf = colourTool->GetColor(label, XCAFDoc_ColorSurf, colourSurf);
    bool hasColourGen = colourTool->GetColor(label, XCAFDoc_ColorSurf, colourGen);
    bool hasColourCurv = colourTool->GetColor(label, XCAFDoc_ColorSurf, colourCurv);
    
    std::vector<int> coloursRGB = {-1, -1, -1};

    double red, green, blue;
    if (!hasColourSurf && !hasColourGen && !hasColourCurv) {
        // use default colours -1, -1, -1
    }
    else if (hasColourSurf) {
        // std::cout << "surf" << std::endl;
        colourSurf.GetRGB().Values(red, green, blue, Quantity_TOC_sRGB);
    }
    else if (hasColourGen) { 
        // std::cout << "gen" << std::endl;
        colourGen.GetRGB().Values(red, green, blue, Quantity_TOC_sRGB);
    }
    else if (hasColourCurv) {
        // std::cout << "curv" << std::endl;
        colourSurf.GetRGB().Values(red, green, blue, Quantity_TOC_sRGB);
    }

    int redInt   = static_cast<int>(Round(red * 255));
    int greenInt = static_cast<int>(Round(green * 255));
    int blueInt  = static_cast<int>(Round(blue * 255));
    coloursRGB = {redInt, greenInt, blueInt};
    
    return coloursRGB;
}

void printStructure(Handle(TDocStd_Document) doc, Handle(XCAFDoc_ShapeTool) shapeTool, Handle(XCAFDoc_ColorTool) colourTool) {
    for (XCAFPrs_DocumentExplorer docExplorer (doc, XCAFPrs_DocumentExplorerFlags_None); docExplorer.More(); docExplorer.Next()) {
        // get the current node
        const XCAFPrs_DocumentNode& node = docExplorer.Current();

        // get shape
        TopoDS_Shape nodeShape = XCAFPrs_DocumentExplorer::FindShapeFromPathId(doc, node.Id);

        // get node shape type
        TCollection_AsciiString nodeTypeString = shapeTypeAsString(nodeShape);

        // case for compound found
        if (nodeShape.ShapeType() == TopAbs_COMPOUND) {
            // get node name and location in tree
            TCollection_AsciiString nameWithDepth ((docExplorer.CurrentDepth()), '-');

            // get name in structure
            Handle(TDataStd_Name) nodeName;
            if (node.RefLabel.FindAttribute (TDataStd_Name::GetID(), nodeName) || node.Label.FindAttribute (TDataStd_Name::GetID(), nodeName))
            {
                nameWithDepth += nodeName->Get();
            }

            nameWithDepth += nodeTypeString;
            
            std::cout << nameWithDepth << " [id: " << node.Id << "]" << std::endl;

            // get list of sub shapes in compound
            TDF_LabelSequence subShapeLabels;
            
            if (shapeTool->GetSubShapes(node.RefLabel, subShapeLabels) || shapeTool->GetSubShapes(node.Label, subShapeLabels)) {
                for (Standard_Integer i = 1; i <= subShapeLabels.Length(); ++i) {
                    // get the label of the sub shape
                    TDF_Label subShapeLabel = subShapeLabels.Value(i);

                    // get the shape of the sub shape
                    TopoDS_Shape subShape = shapeTool->GetShape(subShapeLabel);

                    // get node shape type
                    TCollection_AsciiString subShapeTypeAsString = shapeTypeAsString(subShape);
                    
                    // check if subshape label is a referred label
                    TDF_Label subShapeRefferedLabel;
                    if (XCAFDoc_ShapeTool::GetReferredShape(subShapeLabel, subShapeRefferedLabel)) {
                        subShapeLabel = subShapeRefferedLabel;
                    }

                    // get sub shape name and location in tree
                    TCollection_AsciiString subShapeNameWithDepth ((docExplorer.CurrentDepth() + 1), '-');

                    // get name in structure
                    Handle(TDataStd_Name) subShapeName;
                    if (subShapeLabel.FindAttribute (TDataStd_Name::GetID(), subShapeName) || subShapeLabel.FindAttribute (TDataStd_Name::GetID(), subShapeName))
                    {
                        subShapeNameWithDepth += subShapeName->Get();
                    }
                    else {
                        subShapeNameWithDepth += "name_not_found";
                    }

                    subShapeNameWithDepth += subShapeTypeAsString;

                    std::vector<int> subShapeColour = getColour(subShape, subShapeLabel, colourTool);

                    std::cout << subShapeNameWithDepth << "RGB = " << subShapeColour[0] << " " << subShapeColour[1] << " " << subShapeColour[2] << std::endl;
                }
            }
            else{
                // std::cout << "no sub shape labels" << std::endl;
            }
        }
        else {
            std::cout << "Unknown shape found" << std::endl;
        }   
    }
}
cmake_minimum_required(VERSION 3.16)

# set the project name
project(Structure)

# add include directories for header files
include_directories(include)

# find open cascade package
find_package(OpenCASCADE COMPONENTS FoundationClasses REQUIRED)
find_package(OpenCASCADE COMPONENTS ModelingAlgorithms REQUIRED)
find_package(OpenCASCADE COMPONENTS ModelingData REQUIRED)
find_package(OpenCASCADE COMPONENTS DataExchange REQUIRED)

include_directories(${OpenCASCADE_INCLUDE_DIR})
link_directories(${OpenCASCADE_LIBRARY_DIR})

MESSAGE("================================")
MESSAGE(${OpenCASCADE_LIBRARY_DIR})

add_executable(Structure src/structure.cpp)

target_link_libraries(${PROJECT_NAME} PRIVATE TKBO TKBin TKCAF TKCDF TKMath
                                      TKernel TKGeomAlgo TKTopAlgo TKDESTEP
                                      TKLCAF TKXCAF TKBRep TKPrim TKShHealing
                                      TKGeomBase TKG3d TKG2d TKBinL TKDE
                                      TKXSBase TKVCAF TKV3d TKService
                                      TKMesh TKHLR TKBinXCAF)


Attachments: 
Green Yoshi's picture

I think the STEPCAFControl_Reader will group solids into compounds be default, which can be switched off using the following code. This seems to fix my issues. Could this lead me to other problems, such as slower code or loss of other onformation I'm not yet seeing?

Standard_CString readerSetting = "read.stepcaf.subshapes.name"; 

// 0 = default 
if (Interface_Static::SetIVal(readerSetting, 1)) {
    std::cout << "[OK] " <<  readerSetting << " has been set to " << Interface_Static::IVal(readerSetting) << std::endl;
}
else {
    std::cout << "[ERROR] " << readerSetting << " has not been set. Default value still used " << Interface_Static::IVal(readerSetting) << std::endl;
}