pythonocc-core icon indicating copy to clipboard operation
pythonocc-core copied to clipboard

Extract STEP Entity Instance Id's of Shapes

Open flolu opened this issue 1 year ago • 1 comments

Let's suppose I'm using this STEP file data as input:

#417=ADVANCED_FACE('face_1',(#112),#405,.F.);
#418=ADVANCED_FACE('face_2',(#113),#406,.F.);
#419=ADVANCED_FACE('face_3',(#114),#407,.F.);

Then the following code will print the names of the ADVANCED_FACE instances (face_1,face_2 and face_3):

from OCC.Core.STEPControl import STEPControl_Reader
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE
from OCC.Core.StepRepr import StepRepr_RepresentationItem

reader = STEPControl_Reader()
tr = reader.WS().TransferReader()
reader.ReadFile('model.stp')
reader.TransferRoots()
shape = reader.OneShape()


exp = TopExp_Explorer(shape, TopAbs_FACE)
while exp.More():
    s = exp.Current()
    exp.Next()

    item = tr.EntityFromShapeResult(s, 1)
    item = StepRepr_RepresentationItem.DownCast(item)
    name = item.Name().ToCString()
    print(name)

How can I access the identifiers of the individual shapes? (#417,#418 and #419)

flolu avatar Jul 18 '22 17:07 flolu

This issue is similar to #522. But #522 does not provide a solution for accessing ID's

flolu avatar Jul 18 '22 18:07 flolu

Put this after roots transfer:

model = reader.StepModel()

and this into cycle where you retrieving face's names:

id = model.IdentLabel(item)
print(id)

temurka1 avatar Aug 30 '22 11:08 temurka1

@temurka1 Thank you so much! Here is the code for anyone reading this in the future.

flolu avatar Aug 30 '22 14:08 flolu

@flolu I would also recommend to split loop - you are using XSControl_TransferReader::EntityFromShapeResult(..) which could be slow for large STEP files. It's better to collect faces into TopTools_HSequenceOfShape and then pass collected faces to XSControl_TransferReader::EntitiesFromShapeList(..) and iterate over returned list

temurka1 avatar Aug 31 '22 06:08 temurka1

@temurka1 Performance is not that relevant for my use case, but you may provide an example for future readers. Thanks :)

flolu avatar Aug 31 '22 08:08 flolu

@flolu Hello, how do you add the names "face_1" and "face_2" of ADVANCED FACE to the step file. I'm building a dataset containing tens of thousands of step files. I need to name the ADVANCED FACE in the step file, but I don't know how you do this. Please help me, thank you!

MaFangchang avatar Oct 31 '22 02:10 MaFangchang

@MaFangchang I did not add the labels myself. They were added by the creators of the CAD models I'm working with.

flolu avatar Nov 04 '22 19:11 flolu

@flolu Hello, how do you add the names "face_1" and "face_2" of ADVANCED FACE to the step file. I'm building a dataset containing tens of thousands of step files. I need to name the ADVANCED FACE in the step file, but I don't know how you do this. Please help me, thank you!

I figured this out a while ago, its quite complicated. Here some example code. @MaFangchang the intersting part is at the end of my code, see # set names of the entities and item.SetName(TCollection_HAsciiString('banana_' + str(counter))) The faces are called banana_0 to banana_5 in the step file.

Result: Modell2.zip

@tpaviot If you want, you can add this to the examples: How to colorize single faces and add entity names in the step file.

import numpy as np
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface

from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCC.Core.GeomAbs import GeomAbs_Cylinder
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_sRGB
from OCC.Core.STEPCAFControl import STEPCAFControl_Writer
from OCC.Core.STEPConstruct import stepconstruct_FindEntity
from OCC.Core.STEPControl import STEPControl_AsIs
from OCC.Core.TCollection import TCollection_ExtendedString, TCollection_HAsciiString
from OCC.Core.TDataStd import TDataStd_Name
from OCC.Core.TDocStd import TDocStd_Document
from OCC.Core.TopoDS import TopoDS_Compound, topods
from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorSurf
from OCC.Core.XSControl import XSControl_WorkSession
from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere, BRepPrimAPI_MakeCylinder
from OCC.Extend.TopologyUtils import TopologyExplorer


# data for Modell 2
name = "Modell2"
sph_centre = np.array([0, 0, 0])
sph_diameter = 50
cyl_diameters = np.array([22, 20, 21, 19, 19, 19])
cyl_height = np.array([30, 30, 30, 30, 30, 30])
cyl_vectors = np.array([[-1, 0, 0],
                        [1, 0, 0],
                        [0, 1, 0],
                        [0, 0, 1],
                        [0, -1, 0],
                        [0, 0, -1]], dtype=float)
# normalize vectors
cyl_vectors = cyl_vectors / np.linalg.nor
m(cyl_vectors, axis=1, keepdims=True)

# colors for the faces
COLORS = [[0.00784314, 0.24313725, 1.        ],
          [1.        , 0.48627451, 0.        ],
          [0.10196078, 0.78823529, 0.21960784],
          [0.90980392, 0.        , 0.04313725],
          [0.54509804, 0.16862745, 0.88627451],
          [0.62352941, 0.28235294, 0.        ],
          [0.94509804, 0.29803922, 0.75686275],
          [0.63921569, 0.63921569, 0.63921569],
          [1.        , 0.76862745, 0.        ],
          [0.        , 0.84313725, 1.        ]]


def create_node_buildvolume(sph_diameter: float,
                            cyl_diameters: np.ndarray,
                            cyl_height: np.ndarray,
                            cyl_vectors: np.ndarray) -> TopoDS_Compound:
    """
    Create 3D buildvolume containing a sphere and cylinders

    Args:
        sph_diameter (float): Diameter of the sphere D
        cyl_diameters (np.array): List of diameters of the cylinders [d1, d2]
        cyl_height (np.array): List of lengths of the cylinders [l1, l2]
        cyl_vectors (np.array): List of midline vectors of the cylinders [[x1, y1, z1], [x1, y1, z1]]

    Returns:
        TopoDS_Compound: 3D Model of the buildvolume

    """
    sph_radius = sph_diameter / 2

    origin = gp_Pnt(0.0, 0.0, 0.0)
    sphere = BRepPrimAPI_MakeSphere(origin, sph_radius).Solid()

    for i in range(len(cyl_vectors)):
        cylinder_origin = gp_Ax2(origin, gp_Dir(*cyl_vectors[i, :]))
        cyl = BRepPrimAPI_MakeCylinder(cylinder_origin, cyl_diameters[i]/2, cyl_height[i]+sph_radius).Solid()
        sphere = BRepAlgoAPI_Fuse(sphere, cyl).Shape()

    return topods.Compound(sphere)

# create model
buildvolume = create_node_buildvolume(sph_diameter, cyl_diameters, cyl_height, cyl_vectors)

# create TDocStd_Document
doc = TDocStd_Document(TCollection_ExtendedString("color-doc"))
# get shape tool

shapeTool = XCAFDoc_DocumentTool.ShapeTool(doc.Main())
# get color tool
colorTool = XCAFDoc_DocumentTool.ColorTool(doc.Main())
# create new empty shape
buildvolume_Label = shapeTool.NewShape()
# set the shape of the new model to the one created
shapeTool.SetShape(buildvolume_Label, buildvolume)
# set name of the whole model
TDataStd_Name.Set(buildvolume_Label, TCollection_ExtendedString(name))

# color all cylindrical faces
color_index = 0
topoexp = TopologyExplorer(buildvolume)
for face in topoexp.faces():
    surface = BRepAdaptor_Surface(face)
    if surface.GetType() == GeomAbs_Cylinder:
        cyl_Label = shapeTool.AddSubShape(buildvolume_Label, face)
        if not cyl_Label.IsNull():
            shapeTool.SetShape(cyl_Label, face)
        else:
            shapeTool.FindShape(face, cyl_Label)
        colorTool.SetColor(face,
                           Quantity_Color(*COLORS[color_index % len(COLORS)], Quantity_TOC_sRGB),
                           XCAFDoc_ColorSurf)
        color_index += 1

# create WorkSession to create STEPCAFControl_Writer and FinderProcess
WS = XSControl_WorkSession()
# create stepCAFWriter from WorkSession
stepCAFWriter = STEPCAFControl_Writer(WS)
# create FinderProcess from WorkSession
finderProcess = WS.TransferWriter().FinderProcess()
# transfer the model to the stepCAFWriter
stepCAFWriter.Transfer(doc, STEPControl_AsIs)

###
# set names of the entities
###
topoexp = TopologyExplorer(buildvolume)
counter = 0
for face in topoexp.faces():
    surface = BRepAdaptor_Surface(face)
    if surface.GetType() == GeomAbs_Cylinder:
        item = stepconstruct_FindEntity(finderProcess, face)
        if item:
            item.SetName(TCollection_HAsciiString('banana_' + str(counter)))
            counter += 1

# Export as step file
status = stepCAFWriter.Write(f"{name}.stp")

EmJay276 avatar Nov 05 '22 23:11 EmJay276

@MaFangchang I did not add the labels myself. They were added by the creators of the CAD models I'm working with.

OK, thank you for your reply.

MaFangchang avatar Nov 11 '22 14:11 MaFangchang

@flolu Hello, how do you add the names "face_1" and "face_2" of ADVANCED FACE to the step file. I'm building a dataset containing tens of thousands of step files. I need to name the ADVANCED FACE in the step file, but I don't know how you do this. Please help me, thank you!

I figured this out a while ago, its quite complicated. Here some example code. @MaFangchang the intersting part is at the end of my code, see # set names of the entities and item.SetName(TCollection_HAsciiString('banana_' + str(counter))) The faces are called banana_0 to banana_5 in the step file.

Result: Modell2.zip

@tpaviot If you want, you can add this to the examples: How to colorize single faces and add entity names in the step file.

import numpy as np
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface

from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuse
from OCC.Core.GeomAbs import GeomAbs_Cylinder
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_sRGB
from OCC.Core.STEPCAFControl import STEPCAFControl_Writer
from OCC.Core.STEPConstruct import stepconstruct_FindEntity
from OCC.Core.STEPControl import STEPControl_AsIs
from OCC.Core.TCollection import TCollection_ExtendedString, TCollection_HAsciiString
from OCC.Core.TDataStd import TDataStd_Name
from OCC.Core.TDocStd import TDocStd_Document
from OCC.Core.TopoDS import TopoDS_Compound, topods
from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool, XCAFDoc_ColorSurf
from OCC.Core.XSControl import XSControl_WorkSession
from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere, BRepPrimAPI_MakeCylinder
from OCC.Extend.TopologyUtils import TopologyExplorer


# data for Modell 2
name = "Modell2"
sph_centre = np.array([0, 0, 0])
sph_diameter = 50
cyl_diameters = np.array([22, 20, 21, 19, 19, 19])
cyl_height = np.array([30, 30, 30, 30, 30, 30])
cyl_vectors = np.array([[-1, 0, 0],
                        [1, 0, 0],
                        [0, 1, 0],
                        [0, 0, 1],
                        [0, -1, 0],
                        [0, 0, -1]], dtype=float)
# normalize vectors
cyl_vectors = cyl_vectors / np.linalg.nor
m(cyl_vectors, axis=1, keepdims=True)

# colors for the faces
COLORS = [[0.00784314, 0.24313725, 1.        ],
          [1.        , 0.48627451, 0.        ],
          [0.10196078, 0.78823529, 0.21960784],
          [0.90980392, 0.        , 0.04313725],
          [0.54509804, 0.16862745, 0.88627451],
          [0.62352941, 0.28235294, 0.        ],
          [0.94509804, 0.29803922, 0.75686275],
          [0.63921569, 0.63921569, 0.63921569],
          [1.        , 0.76862745, 0.        ],
          [0.        , 0.84313725, 1.        ]]


def create_node_buildvolume(sph_diameter: float,
                            cyl_diameters: np.ndarray,
                            cyl_height: np.ndarray,
                            cyl_vectors: np.ndarray) -> TopoDS_Compound:
    """
    Create 3D buildvolume containing a sphere and cylinders

    Args:
        sph_diameter (float): Diameter of the sphere D
        cyl_diameters (np.array): List of diameters of the cylinders [d1, d2]
        cyl_height (np.array): List of lengths of the cylinders [l1, l2]
        cyl_vectors (np.array): List of midline vectors of the cylinders [[x1, y1, z1], [x1, y1, z1]]

    Returns:
        TopoDS_Compound: 3D Model of the buildvolume

    """
    sph_radius = sph_diameter / 2

    origin = gp_Pnt(0.0, 0.0, 0.0)
    sphere = BRepPrimAPI_MakeSphere(origin, sph_radius).Solid()

    for i in range(len(cyl_vectors)):
        cylinder_origin = gp_Ax2(origin, gp_Dir(*cyl_vectors[i, :]))
        cyl = BRepPrimAPI_MakeCylinder(cylinder_origin, cyl_diameters[i]/2, cyl_height[i]+sph_radius).Solid()
        sphere = BRepAlgoAPI_Fuse(sphere, cyl).Shape()

    return topods.Compound(sphere)

# create model
buildvolume = create_node_buildvolume(sph_diameter, cyl_diameters, cyl_height, cyl_vectors)

# create TDocStd_Document
doc = TDocStd_Document(TCollection_ExtendedString("color-doc"))
# get shape tool

shapeTool = XCAFDoc_DocumentTool.ShapeTool(doc.Main())
# get color tool
colorTool = XCAFDoc_DocumentTool.ColorTool(doc.Main())
# create new empty shape
buildvolume_Label = shapeTool.NewShape()
# set the shape of the new model to the one created
shapeTool.SetShape(buildvolume_Label, buildvolume)
# set name of the whole model
TDataStd_Name.Set(buildvolume_Label, TCollection_ExtendedString(name))

# color all cylindrical faces
color_index = 0
topoexp = TopologyExplorer(buildvolume)
for face in topoexp.faces():
    surface = BRepAdaptor_Surface(face)
    if surface.GetType() == GeomAbs_Cylinder:
        cyl_Label = shapeTool.AddSubShape(buildvolume_Label, face)
        if not cyl_Label.IsNull():
            shapeTool.SetShape(cyl_Label, face)
        else:
            shapeTool.FindShape(face, cyl_Label)
        colorTool.SetColor(face,
                           Quantity_Color(*COLORS[color_index % len(COLORS)], Quantity_TOC_sRGB),
                           XCAFDoc_ColorSurf)
        color_index += 1

# create WorkSession to create STEPCAFControl_Writer and FinderProcess
WS = XSControl_WorkSession()
# create stepCAFWriter from WorkSession
stepCAFWriter = STEPCAFControl_Writer(WS)
# create FinderProcess from WorkSession
finderProcess = WS.TransferWriter().FinderProcess()
# transfer the model to the stepCAFWriter
stepCAFWriter.Transfer(doc, STEPControl_AsIs)

###
# set names of the entities
###
topoexp = TopologyExplorer(buildvolume)
counter = 0
for face in topoexp.faces():
    surface = BRepAdaptor_Surface(face)
    if surface.GetType() == GeomAbs_Cylinder:
        item = stepconstruct_FindEntity(finderProcess, face)
        if item:
            item.SetName(TCollection_HAsciiString('banana_' + str(counter)))
            counter += 1

# Export as step file
status = stepCAFWriter.Write(f"{name}.stp")

Thank you very much. Your code is very helpful to me.

MaFangchang avatar Nov 11 '22 14:11 MaFangchang

And it's possible to do the inverse of this?

from let's say this ID:

#28=TRIANGULATED_FACE('',#26,10,((-1000.,-0.,-0.)),$,(),((10,9,8),(10,8,7),(10,7,6),(10,6,5),(10,5,4),(10,4,3),(10,3,2),(10,
   2,1)));

Get the solids faces edges?

JorgeFernandes-Git avatar Jun 14 '23 16:06 JorgeFernandes-Git