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

Getting the relationship of connected subparts from assembly step files.

Open sandeshchand opened this issue 3 years ago • 7 comments

I have many parts in the assembly step files.Some parts are connected to other parts and some are not connected.(For example A table has four legs and one flat surface.All legs are connected to the flat surface.Relationship between each leg with flat surface).I want to make a relationship of parts which are directly connected to each other.how could i achieve my goal using pythonocc.It would be great help.Thanks

sandeshchand avatar Apr 27 '22 15:04 sandeshchand

Open CASCADE describes a closed body as a "solid". Solids can share edges and faces with other solids. It's not guaranteed though that your input data actually describes shared faces/edges as such or even that your input data contains solids and not just a bag of faces. If all these assumptions apply, here's a possible solution to find all connections between solids (haven't testet it):

  • iterate over all solids. for each solid: iterate over all edges. Normalize the orientation (forward) and put them all into a set (they can be hashed).
  • intersect sets of different solids with each other. If they share at least one edge, they must have a connection

Here's a sketch how the code could look like:

from OCC.Extend import TopologyUtils
from OCC.Core.TopoDS import TopAbs_FORWARD
from itertools import combinations
exp_solids = TopologyUtils.TopologyExplorer(root_shape_of_assembly)
edge_sets = {}
for solid in exp_solids.solids():
    edge_set = set()
    exp_solid = TopologyUtils.TopologyExplorer(solid)
    for edge in exp_solid.edges():
        edge_set.insert(edge.Oriented(TopAbs_FORWARD)
    edge_sets[solid] = edge_set
for a,b in combinations(edge_sets, 2):
    es_a = edge_sets[a]
    es_b = edge_sets[b]
    if len(es_a & es_b) > 0:
        print(f"solids {a} and {b} are connected")

cafhach avatar Apr 27 '22 19:04 cafhach

Dear cafhach, Thanks for your code. i tried your code by updating some lines.but i cannot get the result.i have one asembled part which contains 4 subparts (solid).They are interrelated to each other. but the code doesnot shows the connected part.Sorry, due to some reasons , i cann#t supply my step files. Could you please check the code and test the program from your side with some sample step files. It would be great help.

from OCC.Extend import TopologyUtils #from OCC.Core.TopoDS import TopAbs_FORWARD from OCC.Core.TopAbs import TopAbs_FORWARD from itertools import combinations file=r"D:\a.stp" shp = read_step_file(os.path.join(file)) exp_solids = TopologyUtils.TopologyExplorer(shp) #print("root",exp_solids) edge_sets = {} for solid in exp_solids.solids(): edge_set = set() exp_solid = TopologyUtils.TopologyExplorer(solid) #print("jonty",exp_solid) for edge in exp_solid.edges(): edge_set.add(edge.Oriented(TopAbs_FORWARD)) #edge_set.insert(edge.Oriented(TopAbs_FORWARD)) edge_sets[solid] = edge_set

for a,b in combinations(edge_sets, 2): es_a = edge_sets[a] es_b = edge_sets[b] if len(es_a & es_b) > 0: print(f"solids {a} and {b} are connected")

sandeshchand87 avatar Apr 28 '22 11:04 sandeshchand87

Hi @sandeshchand87 ,

to check if my assumptions hold at all I changed your code to check for connected faces by changing the line "for solid in exp_solids.solids():" into "for solid in exp_solids.faces():". In this case for my example step files the code lists a couple of connections. Thus I would assume that the solids are not actually exported by your CAD software as connected solids.

One possible next step would be to see if you can find a similarity metric which allows you to identify the faces or edges which are considered separate but actually coincide. One simple way could be to use the positions of the two vertices describing the start and end point of each edge respectively.

from OCC.Extend import TopologyUtils
from OCC.Extend.DataExchange import read_step_file
from itertools import combinations
from OCC.Core.ShapeAnalysis import ShapeAnalysis_Edge
from OCC.Core.BRep import BRep_Tool
file=r"/tmp/stp.stp"
shp = read_step_file(file)
exp_solids = TopologyUtils.TopologyExplorer(shp)

edge_sets = {}
for solid in exp_solids.solids():
    edge_set = set()
    exp_solid = TopologyUtils.TopologyExplorer(solid)
    for edge in exp_solid.edges():
        edgeana = ShapeAnalysis_Edge()
        if edgeana.HasCurve3d(edge):
            pnt_start = BRep_Tool.Pnt(edgeana.FirstVertex(edge))
            pnt_stop  = BRep_Tool.Pnt(edgeana.LastVertex(edge))
            # round as an attempt to deal with small numerical differences. But might
            # as well be counterproductive
            start = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            stop  = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            edge_set.add(frozenset({start, stop}))

    edge_sets[solid] = edge_set

for a,b in combinations(edge_sets, 2):
    es_a = edge_sets[a]
    es_b = edge_sets[b]
    if len(es_a & es_b) > 0:
        print(f"solids {a} and {b} are connected")

cafhach avatar May 02 '22 19:05 cafhach

Good thing to use set() !

if I may, I think that using frozenset get the last iteration difficult to perform, no ?

A fix could be done as this, but it is far from perfect, and I have added a display to help debugging

from OCC.Extend import TopologyUtils
from OCC.Extend.DataExchange import read_step_file
from itertools import combinations
from OCC.Core.ShapeAnalysis import ShapeAnalysis_Edge
from OCC.Core.BRep import BRep_Tool
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere
from OCC.Core.gp import gp_Pnt

from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()

file = "example.stp"
shp = read_step_file(file)
display.DisplayShape(shp)
exp_solids = TopologyUtils.TopologyExplorer(shp)

edge_sets = {}
for solid in exp_solids.solids():
    edge_set = set()
    exp_solid = TopologyUtils.TopologyExplorer(solid)
    for edge in exp_solid.edges():
        edgeana = ShapeAnalysis_Edge()
        if edgeana.HasCurve3d(edge):
            pnt_start = BRep_Tool.Pnt(edgeana.FirstVertex(edge))
            pnt_stop  = BRep_Tool.Pnt(edgeana.LastVertex(edge))
            # round as an attempt to deal with small numerical differences. But might
            # as well be counterproductive
            start = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            stop = tuple(round(getattr(pnt_stop, p)(), 2) for p in ("X", "Y", "Z"))
            edge_set.add(start)
            edge_set.add(stop)

    edge_sets[solid] = edge_set

for a, b in combinations(edge_sets, 2):
    es_a = edge_sets[a]
    es_b = edge_sets[b]
    if len(es_a & es_b) > 0:
        print(f"solids {a} and {b} are connected")
        p = es_a.intersection(es_b)
        for pt in p:
            point = gp_Pnt(pt[0], pt[1], pt[2])
            sphere = BRepPrimAPI_MakeSphere(point, 1).Shape()
            display.DisplayShape(sphere, color='BLUE')

start_display()

Tanneguydv avatar May 03 '22 13:05 Tanneguydv

Hi @sandeshchand87 ,

to check if my assumptions hold at all I changed your code to check for connected faces by changing the line "for solid in exp_solids.solids():" into "for solid in exp_solids.faces():". In this case for my example step files the code lists a couple of connections. Thus I would assume that the solids are not actually exported by your CAD software as connected solids.

One possible next step would be to see if you can find a similarity metric which allows you to identify the faces or edges which are considered separate but actually coincide. One simple way could be to use the positions of the two vertices describing the start and end point of each edge respectively.

from OCC.Extend import TopologyUtils
from OCC.Extend.DataExchange import read_step_file
from itertools import combinations
from OCC.Core.ShapeAnalysis import ShapeAnalysis_Edge
from OCC.Core.BRep import BRep_Tool
file=r"/tmp/stp.stp"
shp = read_step_file(file)
exp_solids = TopologyUtils.TopologyExplorer(shp)

edge_sets = {}
for solid in exp_solids.solids():
    edge_set = set()
    exp_solid = TopologyUtils.TopologyExplorer(solid)
    for edge in exp_solid.edges():
        edgeana = ShapeAnalysis_Edge()
        if edgeana.HasCurve3d(edge):
            pnt_start = BRep_Tool.Pnt(edgeana.FirstVertex(edge))
            pnt_stop  = BRep_Tool.Pnt(edgeana.LastVertex(edge))
            # round as an attempt to deal with small numerical differences. But might
            # as well be counterproductive
            start = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            stop  = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            edge_set.add(frozenset({start, stop}))

    edge_sets[solid] = edge_set

for a,b in combinations(edge_sets, 2):
    es_a = edge_sets[a]
    es_b = edge_sets[b]
    if len(es_a & es_b) > 0:
        print(f"solids {a} and {b} are connected")

Thanks a lot.I have applied on my code.Unfortunately, my stepfile is exported from cad sotware with all connection in solid.I got some connection results but not perfect.My solid contains 4 subparts.There are 3 connections but i am getting only 2 connections using above code.Is there any thing missing??Can we get the internal connection or internal egdes of subparts?Thanks

sandeshchand avatar May 03 '22 14:05 sandeshchand

I guess that when your speaking about internal connection it means that there are no actual connections between edges or vertex of your 2 solids? Perhaps a boolean operation could get you the result that you expect? example :

from OCC.Extend import TopologyUtils
from OCC.Extend.DataExchange import read_step_file
from itertools import combinations
from OCC.Core.ShapeAnalysis import ShapeAnalysis_Edge
from OCC.Core.BRep import BRep_Tool
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Section
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere
from OCC.Core.gp import gp_Pnt
from OCC.Core.TopAbs import TopAbs_VERTEX
from OCC.Core.TopoDS import topods_Vertex
from OCC.Core.TopExp import TopExp_Explorer


from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()

file = "table_sphere_cylinder.stp"
shp = read_step_file(file)
display.DisplayShape(shp)
exp_solids = TopologyUtils.TopologyExplorer(shp)

solids = []
edge_sets = {}
for solid in exp_solids.solids():
    solids.append(solid)
    edge_set = set()
    exp_solid = TopologyUtils.TopologyExplorer(solid)
    for edge in exp_solid.edges():
        edgeana = ShapeAnalysis_Edge()
        if edgeana.HasCurve3d(edge):
            pnt_start = BRep_Tool.Pnt(edgeana.FirstVertex(edge))
            pnt_stop  = BRep_Tool.Pnt(edgeana.LastVertex(edge))
            # round as an attempt to deal with small numerical differences. But might
            # as well be counterproductive
            start = tuple(round(getattr(pnt_start, p)(), 2) for p in ("X", "Y", "Z"))
            stop = tuple(round(getattr(pnt_stop, p)(), 2) for p in ("X", "Y", "Z"))
            edge_set.add(start)
            edge_set.add(stop)

    edge_sets[solid] = edge_set


def edge_ana_method(edge_sets):
    for a, b in combinations(edge_sets, 2):
        es_a = edge_sets[a]
        es_b = edge_sets[b]
        if len(es_a & es_b) > 0:
            print(f"solids {a} and {b} are connected")
            p = es_a.intersection(es_b)
            for pt in p:
                point = gp_Pnt(pt[0], pt[1], pt[2])
                sphere = BRepPrimAPI_MakeSphere(point, 1).Shape()
                display.DisplayShape(sphere, color='BLUE')


def bool_method(solids):
    for two_solids in combinations(solids, 2):
        basis = two_solids[0]
        cutter = two_solids[1]
        result = BRepAlgoAPI_Section(basis, cutter).Shape()
        topExp_vertex = TopExp_Explorer()
        topExp_vertex.Init(result, TopAbs_VERTEX)
        vertices = []
        while topExp_vertex.More():
            vert = topods_Vertex(topExp_vertex.Current())
            point = BRep_Tool.Pnt(vert)
            vertices.append(point)
            topExp_vertex.Next()
        if len(vertices) > 0:
            print(f"solids {basis} and {cutter} are connected")
        for v in vertices:
            sphere2 = BRepPrimAPI_MakeSphere(v, 1).Shape()
            display.DisplayShape(sphere2, color='RED')

bool_method(solids)
# edge_ana_method(edge_sets)

start_display()

when running bool_method(solids) I get : bool_method

when running edge_ana_method(edge_sets) I get: edge_ana

It's certainly not the best way to proceed though it works to detect connections between solids

Tanneguydv avatar May 04 '22 14:05 Tanneguydv

Dear Tanneeguydv, Thanks alot. the above solution provides more connection information as compare to previous one.I hope it helps me alot. Thanks for your support.

sandeshchand87 avatar May 06 '22 09:05 sandeshchand87