Collision detection without cut or common

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
mario52
Veteran
Posts: 4692
Joined: Wed May 16, 2012 2:13 pm

Re: Collision detection without cut or common

Post by mario52 »

hi
FreeMake wrote: Wed Jul 10, 2019 11:43 am micro_elly always uses cut or common in her scripts as far as I know.
no the microelly2 macro use the BoundBox dimension not the real volume dimension of the object (not cut or common)

try this modification, select 2 objects and run the macro

if the BoundBox of the second oject is in the BoundBox of the first object the second object is colored in red

Code: Select all

# https://forum.freecadweb.org/viewtopic.php?f=24&t=12733#p102130
def col(actor,obstacles):
	
	av=actor.Shape.BoundBox
	for obl in obstacles:
		ov=obl.Shape.BoundBox
		if ov.XMin < av.XMax  and  ov.XMax > av.XMin and ov.YMin <= av.YMax and  ov.YMax >= av.YMin and  ov.ZMin <= av.ZMax and  ov.ZMax >= av.ZMin:
			print( obl.Label)
			obl.ViewObject.DiffuseColor=(1.0,0.0,0.0)
		else:
			obl.ViewObject.DiffuseColor=(1.0,1.0,0.0)  

#def checkCollision():
#
#	App=FreeCAD
#	cy=App.ActiveDocument.Cylinder
#	c=App.ActiveDocument.Cone
#	gd=App.ActiveDocument.GeoDome
#	b=App.ActiveDocument.Box
#	col(b,[cy,c,gd,App.ActiveDocument.Torus])
#checkCollision()

sel = FreeCADGui.Selection.getSelection()                               # Select an object

col(sel[0], [sel[1]])
mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
mario52
Veteran
Posts: 4692
Joined: Wed May 16, 2012 2:13 pm

Re: Collision detection without cut or common

Post by mario52 »

hi

here using distToShape

if the distance = 0.0 then collision if not the distance and one line (on the shortest distance) hare displayed

Line Between Spheres - Help (wandererfan)

select 2 object and run the macro

Code: Select all

import Draft, Part
#http://forum.freecadweb.org/viewtopic.php?f=3&t=5921&hilit=sphere
sel = FreeCADGui.Selection.getSelection()                               # Select an object

s1 = sel[0].Shape #App.ActiveDocument.Sphere.Shape
s2 = sel[1].Shape #App.ActiveDocument.Sphere001.Shape

dist, pts, geom = s1.distToShape(s2)

print( "Minimum Distance   : ", dist)
print( "Number of Solutions: ", len(pts))
#print( "_____________________")
#print( "Geometry           : ",geom)
print( "_____________________")
ends = pts[0]
Draft.makeLine(ends[0],ends[1])

mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
FreeMake
Posts: 33
Joined: Tue Mar 19, 2019 12:10 pm

Re: Collision detection without cut or common

Post by FreeMake »

Hi Mario,

Thanks for the suggestions but two comments on this:

1. Boundbox will not work for objects that have cavities for example. The boundbox will detect a collision while there actually is none.
2. distToShape also returns True when objects are just touching and not colliding.

I found a solution that covers (almost) all cases. It works as follows:

1. Perform distToShape
2. If distToShape is 0 take the closest points that distToShape returns and do isInside with last argument set to False to take on Face points as False.

It does not cover the case in which the closest point detected by distToShape lies just on the interesection while there is another point that lies inside the other object.

Code: Select all

def collision_detect(main_shape, other_shapes):
    """Checks if an object is colliding with another object. If they are touching 'False' is returned

    Input:
    main_shape: An OCC shape object
    other_shapes: An OCC shape object or a list of OCC shape objects

    Output:
    A list of booleans indicating if the respective input shape in 'other_shapes' list is colliding with 'main_shape'

    WARNING: Step 3. is not waterproof. If by a lot of coincidence the point that is found by distToShape is on the
    intersection point than it will return False while it is actually True. Happens very rarely."""

    #0. Make a list if other shapes is not a list yet
    if type(other_shapes) is not list:
        other_shapes = [other_shapes]

    collision_bools = [] #List in which the answers will be gathered
    for other_shape in other_shapes:

        #1. Check if boundary boxes collide
        if not main_shape.BoundBox.intersect(other_shape.BoundBox):
            collision_bools.append(False) #bbox does not intersect --> no collision
            continue


        #2. Check shortest distance (if 0.0, check further)
        dist, points, geom = main_shape.distToShape(other_shape)
        if dist>0.0:
            collision_bools.append(False)  # min dist is larger than 0.0 --> no collision
            continue

        #3. Check if the points are inside and not only touching
        if not main_shape.isInside(points[0][1],10**-6, False) and not other_shape.isInside(points[0][1],10**-6, False): #Last argument indicates if 							lying on face is considered inside
            collision_bools.append(False)
            continue

        #Must be colliding
        collision_bools.append(True)

    return collision_bools
User avatar
bernd
Veteran
Posts: 12851
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Collision detection without cut or common

Post by bernd »

Chris_G wrote: Wed Jul 10, 2019 7:56 am Maybe Shape.proximity is less expensive ?
proximity(Shape s): Returns two lists of Face indexes for the Faces involved in the intersection.

Code: Select all

import FreeCAD
doc1 = FreeCAD.getDocument('CollisionDetect')
obj1 = doc1.getObject('Part__Feature')
obj2 = doc1.getObject('Part__Feature001')
obj1.Shape.proximity(obj2.Shape)
>>> ([3, 9, 10, 11, 16, 17, 18, 19, 20, 22, 23], [7, 8, 9, 10, 11, 12, 19, 20, 29, 57])
never heard of this one ... Is it new?
User avatar
bernd
Veteran
Posts: 12851
Joined: Sun Sep 08, 2013 8:07 pm
Location: Zürich, Switzerland
Contact:

Re: Collision detection without cut or common

Post by bernd »

User avatar
Chris_G
Veteran
Posts: 2598
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Collision detection without cut or common

Post by Chris_G »

Not that new : 2015 !!!
https://github.com/FreeCAD/FreeCAD/comm ... 42304f10c1
But I had never used it yet.
FreeMake
Posts: 33
Joined: Tue Mar 19, 2019 12:10 pm

Re: Collision detection without cut or common

Post by FreeMake »

Version that is a lot faster in almost all cases (same principle):

Code: Select all

def collision_detect(main_shape, other_shapes):
    """Checks if an object is colliding with another object. If they are touching 'False' is returned

    Input:
    main_shape: An OCC shape object
    other_shapes: An OCC shape object or a list of OCC shape objects

    Output:
    A list of booleans indicating if the respective input shape in 'other_shapes' list is colliding with 'main_shape'

    WARNING: Step 3. is not waterproof. If by a lot of coincidence the point that is found by distToShape is on the
    intersection point than it will return False while it is actually True. Happens very rarely."""

    #0. Make a list if other shapes is not a list yet
    if type(other_shapes) is not list:
        other_shapes = [other_shapes]

    collision_bools = [] #List in which the answers will be gathered
    for other_shape in other_shapes:

        #1. Check if boundary boxes collide
        if not main_shape.BoundBox.intersect(other_shape.BoundBox):
            collision_bools.append(False) #bbox does not intersect --> no collision
            continue

        #Time savers
        #1.2. Check if boundary boxes have volume, if not they are just touching
        intersection = main_shape.BoundBox.intersected(other_shape.BoundBox)
        if intersection.XLength<bc.epsilon or intersection.YLength<bc.epsilon or intersection.ZLength<bc.epsilon:
            collision_bools.append(False)
            continue
        #1.3. Check if any of the vertexes of the object with the smallest amount of vertexes are inside
        if len(main_shape.Vertexes)>len(other_shape.Vertexes):
            shape_big = main_shape
            shape_small = other_shape
        else:
            shape_small = main_shape
            shape_big = other_shape
        inside_pt_found = False
        for vertex in shape_small.Vertexes:
            if intersection.isInside(vertex.Point):
                if shape_big.isInside(vertex.Point,bc.epsilon,False):
                    inside_pt_found=True
                    break
        if inside_pt_found:
            collision_bools.append(True)
            continue

        #2. Check shortest distance (if 0.0, check further)
        dist, points, geom = main_shape.distToShape(other_shape)
        if dist>0.0:
            collision_bools.append(False)  # min dist is larger than 0.0 --> no collision
            continue

        #3. Check if the points are inside and not only touching
        if not main_shape.isInside(points[0][1],bc.epsilon, False) and not other_shape.isInside(points[0][1],bc.epsilon, False): #Last argument indicates if lying on face is considered inside
            collision_bools.append(False)
            continue

        #Must be colliding
        collision_bools.append(True)

    return collision_bools
User avatar
Chris_G
Veteran
Posts: 2598
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Collision detection without cut or common

Post by Chris_G »

Your tessellation values are probably too high :

prop_view_3.png
prop_view_3.png (39.74 KiB) Viewed 1113 times
User avatar
brst
Posts: 87
Joined: Thu Apr 18, 2019 10:11 am
Location: Germany

Re: Collision detection without cut or common

Post by brst »

Chris_G wrote: Wed Jul 10, 2019 7:56 am
FreeMake wrote: Wed Jul 10, 2019 7:38 am I assume there is no other way than a cut or a common to check if two objects are colliding and not adjacent? Because it also seems quite an expensive operation for what you want to know. (If you know that one point is inside of the other object you already know sufficient in fact).
Maybe Shape.proximity is less expensive ?
proximity(Shape s): Returns two lists of Face indexes for the Faces involved in the intersection.

Code: Select all

import FreeCAD
doc1 = FreeCAD.getDocument('CollisionDetect')
obj1 = doc1.getObject('Part__Feature')
obj2 = doc1.getObject('Part__Feature001')
obj1.Shape.proximity(obj2.Shape)
>>> ([3, 9, 10, 11, 16, 17, 18, 19, 20, 22, 23], [7, 8, 9, 10, 11, 12, 19, 20, 29, 57])
Hmm it seems to be less reliable than .common, as i get false positives / negatives, except of just false negatives with the common function. (Tessellation with 0.01, what value is used within .common?)
It seems the function is, like .common, based on a mesh operation.
FreeCAD rookie
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Collision detection without cut or common

Post by microelly2 »

We should test how fast Shape.proximity is.

I go this way:
first compare boundig boxes of both objects
then compare bounding boxes of all faces
then use time consuming methods to compere faces with intersecting bounding boxes
Post Reply