Open joaoprioli opened 4 years ago
A very simple solution, but definitely not the fastest: Make a boolean intersection!
If the resulting shape has a finite volume, you have an intersection.
There are probably faster algorithms, working e.g. on a surface mesh and checking for intersections.
How could I split the solids in the STEP file and then check one against each one?
@joaoprioli from the less accurate/fastest to the most accurate/slowest methods :
check clashes using bounding boxes
check class using meshed shapes and a mesh intersection algorithm
use BRep boolean operation
Note for myself it should be worth trying to wrap the vtk part of opencascade technology.
And how would you check, if a solid is completly inside of another solid?
@johannesmichael Depends on how you define the "is completely inside".
A wonderful collision detection library I've integrated with PythonOCC is FCL, the flexible collision library. Recommended.
I have bigger boxes/spaces, like areas in a building and I want to add properties to the solids (walls, pipes, ...) that are inside that space. So first I am trying to find all that are inside that space, second will be the task to define where the ones are belonging, that extend to both spaces. So with completly I just wanted to express: the whole solid is inside the other.
Edit: Working with IFC
Have you looked into the BRepClass3d_SolidClassifier / BRepClass3d_SolidExplorer?
@jf--- yes, but are still in the process of understanding how to use it. I had used the BrepAlgoAPI before, so I started there. The FCL doesn 't help here, or did I missed something?
Boolean ops are probably in terms of performance the slowest, but as a quick hack its clever.
On the BRep
level something like BRepClass3d_SolidExplorer
is already much more optimised for the use case.
The FCL doesn 't help here, or did I missed something?
Well you can access the tesselation of your TopoDS_* and infer intersections via FCL
.
This is faster & more robust, when you intend to implement clash detection, you really have to do so at the mesh
rather than the BRep
level, its orders of magnitude in terms of performance.
With the booleans I could not find a way to identify if inside the box. Inside and outside behave the same.
With BRepClass3d_SolidExplorer
I am struggling to find the right method. Is it right, that there I have to check if the points or line are in the TopoDS_Shape?
Something like
BRepClass3d_SolidExplorer(boxshape).RejectShell(line_of_other_shape)
@jf--- Interesting, didn't know about this library. Does it take meshes ? and what about using vtk or cgal to perform collision detection ?
@joaoprioli from the less accurate/fastest to the most accurate/slowest methods :
- check clashes using bounding boxes
- check class using meshed shapes and a mesh intersection algorithm
- use BRep boolean operation
Could you have a brief code to explain how to use the BRep boolean operation in the collision, I still facing problems trying to check the collision in the STEP file.
When I play the following code seems that I can't split the solids inside the STEP file. Any clue?
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core import TopoDS
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Common as makeCommon
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Check
from OCC.Core.BRepClass3d import BRepClass3d_SolidExplorer
from OCC.Core.BRepAlgo import BRepAlgo_BooleanOperation, BRepAlgo_Common, BRepAlgo_Cut
from OCC.Core.IntAna import IntAna_IntConicQuad, IntAna_QuadQuadGeo
import os
from random import randint
from OCC.Display.WebGl.jupyter_renderer import JupyterRenderer, format_color
from OCC.Extend.TopologyUtils import TopologyExplorer
from OCC.Extend.DataExchange import read_step_file
shp = read_step_file(os.path.join('Assem3.stp'))
my_renderer = JupyterRenderer(size=(700, 700))
# loop over subshapes so that each subshape is meshed/displayed
t = TopologyExplorer(shp)
if (t.number_of_solids()!=1):
for solid in t.solids():
for solid2 in t.solids():
if (solid != solid2):
collision = BRepAlgo_Common(solid, solid2)
if (BRepAlgoAPI_Check.IsValid(collision)):
print("colliding")
RuntimeError: Standard_ConstructionError Geom_TrimmedCurve::U1 == U2 wrapper details:
Does it take meshes ?
Yes indeed...
and what about using vtk or cgal to perform collision detection ?
CGAL
is a master piece of engineering, but challenging to install, and the python lib is a subset of the C++ API. CGAL
has a focus on exactness rather than speed.
FCL
is a library from the ROS
project, and is specifically focussed on collision detection and highly optimized for this task. The cool thing is that you can also give a margin ( think offset ) for collisions to occur within and it performs sweeping; testing collisions during a transformation.
In contrast to vtk
and CGAL
, FCL
is a specialised on collision detection. Where the respective focus of vtk
and CGAL
is visualization and (discrete) geometry
When I play the following code seems that I can't split the solids inside the STEP file. Any clue?
kind of looks like solid != solid2
is not doing what is expected
Are you processing non-manifold geometry? Eg, a box within a box that doesn't intersect.
Are you processing non-manifold geometry? Eg, a box within a box that doesn't intersect.
Not in this case, I using two manifold geometries that intersects in part.
The interesting thing is when I change the function to BRepAlgoAPI_Common()
it worked but I can't check the validity of the intersection, follows the code:
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core import TopoDS
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Common
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Check
from OCC.Core.BRepClass3d import BRepClass3d_SolidExplorer, BRepClass3d_SolidClassifier
from OCC.Core.BRepAlgo import BRepAlgo_BooleanOperation, BRepAlgo_Common, BRepAlgo_Cut
from OCC.Core.IntAna import IntAna_IntConicQuad, IntAna_QuadQuadGeo
import os
from random import randint
from OCC.Display.WebGl.jupyter_renderer import JupyterRenderer, format_color
from OCC.Extend.TopologyUtils import TopologyExplorer
from OCC.Extend.DataExchange import read_step_file
from OCC.Core.BRepCheck import BRepCheck_Analyzer
shp = read_step_file(os.path.join('Assem3.stp'))
my_renderer = JupyterRenderer(size=(700, 700))
t = TopologyExplorer(shp)
h = BRepClass3d_SolidExplorer(shp)
print(h)
if (t.number_of_solids()!=1):
for solid in t.solids():
for solid2 in t.solids():
if (solid != solid2):
collision = BRepAlgoAPI_Common(solid, solid2)
check_coli = BRepAlgoAPI_Check(collision)
if not check_coli.IsValid():
print('Shape is not valid. Attempting to fix...')
elif check_coli.IsValid():
print("colliding")
I get the follow error: TypeError: in method 'new_BRepAlgoAPI_Check', argument 1 of type 'TopoDS_Shape const &'
BTW, I found a quick solution for my problem:
Bnd_Box.IsOut()
https://dev.opencascade.org/doc/refman/html/class_bnd___box.html#a66bc2b9bf21ddc0bfe74db79a6bcf5b5
bbox_check.IsOut(bbox)
is False if the bbox_check
is completly or is partly inside.
Thanks for helping!
@johannesmichael You could also use the BRepExtrema_DistShapeShape
class to compute the minimal distance between two solids. If this distance is greater than an epsilon you define, then you ensure there's no collision.Otherwise returns 0.0. See an example here https://github.com/tpaviot/pythonocc-demos/blob/master/examples/core_geometry_minimal_distance.py
Using the minimal distance to detect collision might be a good balance between accuracy and computation time/memory cost. Can you please report your result if you compare both solutions ?
@tpaviot I looked at the Bnd_Box.Distance(), but since I am only interested in the objects that are inside a space, this was not of more value for me. But I will evaluate your aproach and report back.
You could also use the
BRepExtrema_DistShapeShape
@tpaviot , I used the BRepExtrema_DistShapeShape
class to compute the collision, it works for most of the situations, but it still don't tell if the shape is trespassing or just in direct contact with the other. Do you have some idea to differ from trespassing and contact?
Hi, I have recently found BRepExtrema
. It has collision detection.
Check a function I've prepared:
def test_proximity(shape_a, shape_b):
aMesher = BRepMesh_IncrementalMesh(shape_a, 1.0);
aMesher = BRepMesh_IncrementalMesh(shape_b, 1.0);
proximity = BRepExtrema_ShapeProximity(shape_a, shape_b)
proximity.Perform()
shapes_in_contact_a = []
shapes_in_contact_b = []
if proximity.IsDone():
print('ShapeProximity is done')
subs1 = proximity.OverlapSubShapes1().Keys()
nb_subs1 = proximity.OverlapSubShapes1().Size()
print(f'subshapes1: {subs1} {nb_subs1}')
subs2 = proximity.OverlapSubShapes2().Keys()
nb_subs2 = proximity.OverlapSubShapes2().Size()
print(f'subshapes2: {subs2} {nb_subs2}')
for sa in subs1:
temp = translate_shp(proximity.GetSubShape1(sa), gp_Vec(0, 0, 5))
shapes_in_contact_a.append(temp)
for sa in subs2:
temp = translate_shp(proximity.GetSubShape2(sa), gp_Vec(0, 0, 5))
shapes_in_contact_b.append(temp)
else:
print('No contact or interference')
aFaceList = BRepExtrema_ShapeList()
ex = TopExp_Explorer(shape_b, TopAbs_FACE)
while ex.More():
#print('cara..')
aFaceList.Append(topods_Face(ex.Current()))
ex.Next()
aTriangleSet = BRepExtrema_TriangleSet(aFaceList)
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()
display.SetSelectionModeVertex() # review
display.DisplayShape(shape_a, update=False, transparency=0.)
display.DisplayShape(shape_b, update=True, transparency=0.1)
display.DisplayShape(shapes_in_contact_a, color='red')
display.DisplayShape(shapes_in_contact_b, color='blue', transparency=0.3)
start_display()
The translation is for visualization purposes.
BTW, I made a wrapper for PQP (PQP: Fast Proximity Queries with Swept Sphere Volumes) https://gamma.cs.unc.edu/SSV/; but I liked FCL better when working with OpenCASCADE in C++. But the python wrapper for Win32 gave false results.
I think BRepExtrema_ShapeProximity
might be enough for your use case.
Well you can access the tesselation of your TopoDS_* and infer intersections via
FCL
.
@jf--- Your approach is very interesting for the app I'm currently working on. I'm trying to implement this by using the ShapeTesselator. Firstly I extract vertices & triangles from the Tesselator, than I create a mesh using fcl.BVHModel(). Generelly works, but I didn't find a proper way to validate visually. So I rendered meshes using Trimesh for visualization purposes. Seems like my meshes are very inaccurate and seem to have some wrong faces.
def get_tessellation(shape, mesh_quality):
tess = ShapeTesselator(shape)
tess.Compute(mesh_quality=mesh_quality)
vertices_position = tess.GetVerticesPositionAsTuple()
number_of_vertices = len(vertices_position)
number_of_triangles = tess.ObjGetTriangleCount()
vertices = np.array(vertices_position).reshape(int(number_of_vertices / 3), 3)
triangles = []
for triangle in range(0, number_of_triangles):
i1, i2, i3 = tess.GetTriangleIndex(triangle)
triangles.append([i1, i2, i3])
return vertices, triangles
def create_mesh(vertices, triangles):
m = fcl.BVHModel()
m.beginModel(len(vertices), len(triangles))
m.addSubModel(vertices, triangles)
m.endModel()
return m
This is what the shape looks like:
This is my result showing the Trimesh Visualization:
hi @wzl-muenker, so rather than creating another mesh, I explore the mesh that's representing the shape in the viewer, with BRep_Tool_Triangulation
, and IIRC, BRepMesh_IncrementalMesh
updates the existing mesh to the parameters supplied in the call to the function ( I'm not certain though, its been a while )
Note that some discrepancies between the brep
and the fcl.BVHModel
mesh might occur, due to the requirement of convexity. If that's not acceptable, probably a per-subshape collision detection is required.
The following might help.
def mesh_from_brep(occ_brep, theLinDeflection=0.8):
"""
returns a list of triangles that represent the mesh, represening the `occ_brep` BRep
"""
# create / update a mesh
# this is important when a mesh has not been display
# in this case it has no mesh to iterate through
inc_mesh = BRepMesh_IncrementalMesh(occ_brep, theLinDeflection)
assert inc_mesh.IsDone()
tp = Topo(occ_brep, ignore_orientation=True)
triangles = collections.deque()
for f in tp.faces():
loc = TopLoc_Location()
triangulation = BRep_Tool_Triangulation(f, loc)
if triangulation.IsNull():
continue
facing = triangulation.GetObject()
tri = facing.Triangles()
nodes = facing.Nodes()
for i in range(1, facing.NbTriangles() + 1):
trian = tri.Value(i)
index1, index2, index3 = trian.Get()
tria=nodes.Value(index1) #.Coord()
trib=nodes.Value(index2) #.Coord()
tric=nodes.Value(index3) #.Coord()
triangles.append(
( (tria.X(),tria.Y(),tria.Z()),
(trib.X(),trib.Y(),trib.Z()),
(tric.X(),tric.Y(),tric.Z()),
)
)
return triangles
def fcl_collision_object_from_shape(shape):
"""
create a fcl.BVHModel instance from the `shape` TopoDS_Shape
"""
occ_mesh_tris = mesh_from_brep(shape)
_mesh = fcl.BVHModel()
n_tris = len(occ_mesh_tris)
_mesh.beginModel(n_tris, n_tris * 3)
map(lambda x: _mesh.addTriangle(*x), occ_mesh_tris)
_mesh.endModel()
return fcl.CollisionObject(_mesh)
Thanks you very much @jf--- ! Helped me a lot. I combined your code with the code from pythonocc-demos/examples/core_simple_mesh.py
to get it running. Now I'm pretty confident, that I have correct & similar meshes for fcl collision check & for visualization.
Now I just have two more issues to solve:
fcl
issue now). The situation throws me a collision, even though no surfaces are in contact.Hi, sorry to start this thread again. I am a final year studied understanding a Masters in Mechanical Engineering, at present, I am studying the use of python and pythonocc concerning automating the pre-processing phase when converting from model to analysis. I have minimal experience with pythonocc as I have only started to explore it this year. I am wishing to identify which solids have collisions and also which face the collision occurs on, I am aware this may be a simple solution, I was wondering if there was the possibility for some guidance or to obtain some existing code. I am working from STEP files at present and would ideally like to iterate through the solids initially to identify if there is a collision then to identify which face the collision occurs on.
Many thanks in advance,
In order to look for clashes, you can for instance compute the minimal distance for each couple of TopoDS_Shape
or TopoDS_Compound
available from the STEP file. This will be time and memory consuming, but that's possible. See demo code at https://github.com/tpaviot/pythonocc-demos/blob/4832a91404272f2da27bb2d454718e78e9512a35/examples/core_geometry_minimal_distance.py
If the minimal distance is < 0, then the two shapes collide (I guess, this should be tested, please report any related feedback).
Hi, I have previously tried this method and found that it is good for showing a gap is present. However, I found that through using the BRepExtrema module I can obtain collisions reliably. I have tried to use the minimal distance script to determine the overlap of interfering shapes. I have found that the minimal distance method will not return a value less than zero in my use case, where there are any collisions present the minimal distance of the two shapes involved is always 0 regardless of overlap.
Hello, I trying to write a code that opens a STEP file and check if any solid is colliding/intersecting with the rest of the solids in the STEP file. In that case, the STEP can contain solids colliding or not, so the code would output True/False for each solid check.
Example of STEP colliding/not colliding
I started with the code below, but I have no idea how to continue. Could someone help me?