40 from CollisionObject
import CollisionProxy, ViewProviderCollisionProxy
41 from PySide2.QtWidgets
import QMessageBox
42 from PySide2.QtCore
import QTimer
46 PATH_TO_ICONS = path.join(FreeCAD.getHomePath(),
"Mod",
"Animate",
"Resources",
122 fp.ViewObject.Proxy.setProperties(fp.ViewObject)
136 if prop ==
"ObservedObjects":
152 if prop ==
"ObservedObjects":
154 - set(fp.ObservedObjects)
155 for obj
in removed_objects:
157 added_objects = set(fp.ObservedObjects) \
161 fp.ValidObservedObjects = (set(self.
shape_info.keys())
162 == set(fp.ObservedObjects))
178 for obj
in self.
shape_info.pop(object_)[
"objects"]:
182 obj.ViewObject.Transparency = style[
"Transparency"]
183 obj.ViewObject.ShapeColor = tuple(style[
"ShapeColor"])
184 obj.ViewObject.LineColor = tuple(style[
"LineColor"])
185 obj.ViewObject.LineWidth = style[
"LineWidth"]
200 if not hasattr(obj,
"Shape")
and not hasattr(obj,
"Group"):
203 'Error while checking collisions',
204 "Object " + obj.Label +
" does not have a shape assigned." 205 +
"\nNeither does it group objects which do.\n" 206 +
"It is not possible to check its collisions.\n" 207 +
"Remove it from the observed objects.")
209 elif not hasattr(obj,
"Shape")
and hasattr(obj,
"Group"):
212 if groupshape
is not None:
213 self.
shape_info[obj] = {
"objects": groupobjects,
216 for obj
in groupobjects:
218 "Transparency": obj.ViewObject.Transparency,
219 "ShapeColor": obj.ViewObject.ShapeColor,
220 "LineColor": obj.ViewObject.LineColor,
221 "LineWidth": obj.ViewObject.LineWidth}
225 'Error while checking collisions',
226 "Group " + obj.Label +
" does not contain\n" 227 +
"any objects with shapes assigned.\n" 228 +
"It is not possible to check its collisions.\n" 229 +
"Remove it from the observed objects.")
236 "Transparency": obj.ViewObject.Transparency,
237 "ShapeColor": obj.ViewObject.ShapeColor,
238 "LineColor": obj.ViewObject.LineColor,
239 "LineWidth": obj.ViewObject.LineWidth}
262 if not hasattr(fp,
"ValidObservedObjects"):
264 "App::PropertyBool",
"ValidObservedObjects",
"General",
265 "All objects are valid for collision detection" 266 ).ValidObservedObjects =
False 268 if not hasattr(fp,
"ObservedObjects"):
270 "App::PropertyLinkListGlobal",
"ObservedObjects",
"General",
271 "Objects that will be checked for intersections.")
272 if not hasattr(fp,
"RememberCollisions"):
274 "App::PropertyBool",
"RememberCollisions",
"General",
275 "Remember which objects collided and show them." 276 ).RememberCollisions =
True 277 if not hasattr(fp,
"CheckingLevel"):
278 fp.addProperty(
"App::PropertyEnumeration",
"CheckingLevel",
279 "General",
"Levels of checking from coarse and\n" 280 +
"fast (Bounding box) to slow but precise\n" 281 +
"(Intersection volume). To see intersected area\n" 282 +
"select 'Intersection volume visualizations'")
283 fp.CheckingLevel = [
"Bounding box",
285 "Intersection volume",
286 "Intersection volume visualizations"]
288 if not hasattr(fp,
"IntersectionColor"):
290 "App::PropertyColor",
"IntersectionColor",
"IntersectionStyle",
291 "Color for highlighting intersections." 292 ).IntersectionColor = (1.0, 0.0, 0.0)
295 if not hasattr(fp,
"InCollisionTransparency"):
297 "App::PropertyPercent",
"InCollisionTransparency",
299 "Transparency set to objects in collision." 300 ).InCollisionTransparency = 50
301 if not hasattr(fp,
"InCollisionShapeColor"):
303 "App::PropertyColor",
"InCollisionShapeColor",
305 "Shape color for highlighting objects in collision." 306 ).InCollisionShapeColor = (1.0, 0.667, 0.333)
307 if not hasattr(fp,
"InCollisionLineColor"):
309 "App::PropertyColor",
"InCollisionLineColor",
311 "Line color for highlighting objects in collision." 312 ).InCollisionLineColor = (1.0, 0.667, 0.0)
313 if not hasattr(fp,
"InCollisionLineWidth"):
315 "App::PropertyFloatConstraint",
"InCollisionLineWidth",
317 "Line width for highlighting objects\n" 318 +
"in collision. Range is < 1 | 64 >." 319 ).InCollisionLineWidth = (2, 1, 64, 1)
321 fp.InCollisionLineWidth = (fp.InCollisionLineWidth, 2, 64, 1)
324 if not hasattr(fp,
"CollidedTransparency"):
326 "App::PropertyPercent",
"CollidedTransparency",
327 "CollidedStyle",
"Transparency set to collided objects." 328 ).CollidedTransparency = 50
329 if not hasattr(fp,
"CollidedShapeColor"):
331 "App::PropertyColor",
"CollidedShapeColor",
"CollidedStyle",
332 "Color for highlighting objects which collided." 333 ).CollidedShapeColor = (0.667, 0.333, 1.0)
334 if not hasattr(fp,
"CollidedLineColor"):
336 "App::PropertyColor",
"CollidedLineColor",
338 "Line color for highlighting objects in collision." 339 ).CollidedLineColor = (0.667, 0.0, 1.0)
340 if not hasattr(fp,
"CollidedLineWidth"):
342 "App::PropertyFloatConstraint",
343 "CollidedLineWidth",
"CollidedStyle",
344 "Line width for highlighting objects" 345 +
"in collision. Range is < 1 | 64 >." 346 ).CollidedLineWidth = (2, 1, 64, 1)
348 fp.CollidedLineWidth = (fp.CollidedLineWidth, 2, 64, 1)
350 if not hasattr(self,
"in_collision")
or \
351 (hasattr(self,
"in_collision")
and self.
in_collision is None):
353 if not hasattr(self,
"collided")
or \
354 (hasattr(self,
"collided")
and self.
collided is None):
356 if not hasattr(self,
"original_styles")
or \
357 (hasattr(self,
"original_styles")
and 360 if not hasattr(self,
"shape_info")
or \
361 (hasattr(self,
"shape_info")
and 364 self.
loadObjects(fp.ObservedObjects, save_style=
False)
370 fp.setEditorMode(
"Group", 1)
371 fp.setEditorMode(
"ValidObservedObjects", 2)
372 fp.ValidObservedObjects = (set(self.
shape_info.keys())
373 == set(fp.ObservedObjects))
375 import AnimateDocumentObserver
386 if not self.
fp.ValidObservedObjects:
389 'Error while checking collisions',
390 "One or more objects to check for collisions " 391 +
"don't have shapes attached, or are empty groups.")
396 FreeCAD.Console.PrintWarning(
"Can't check, Resetting!")
401 FreeCAD.Console.PrintWarning(
"Already Checking!")
414 if len(self.
fp.ObservedObjects) == 0:
415 FreeCAD.Console.PrintWarning(
416 "CollisionDetector observes no objects.\n")
421 elif len(self.
fp.ObservedObjects) == 1:
422 FreeCAD.Console.PrintWarning(
423 "CollisionDetector observes only 1 object.\n")
424 ok.add(self.
fp.ObservedObjects[0])
430 for obj
in self.
fp.ObservedObjects:
433 self.
shape_info[obj][
"objects"][0].Shape.fuse(
434 [o.Shape
for o
in self.
shape_info[obj][
"objects"][1:]])
435 if hasattr(obj,
"Placement"):
436 self.
shape_info[obj][
"shape"].Placement.Base = \
438 self.
shape_info[obj][
"shape"].Placement.Rotation = \
439 obj.Placement.Rotation
441 elif hasattr(obj,
"Placement"):
445 for i
in range(len(self.
fp.ObservedObjects) - 1):
446 for j
in range(i+1, len(self.
fp.ObservedObjects)):
448 self.
fp.ObservedObjects[j]):
449 in_collision.add(self.
fp.ObservedObjects[i])
450 in_collision.add(self.
fp.ObservedObjects[j])
452 ok.add(self.
fp.ObservedObjects[i])
453 ok.add(self.
fp.ObservedObjects[j])
456 ok.difference_update(in_collision)
479 objects = group.Group
481 while i < len(objects):
483 if hasattr(objects[i],
"Shape"):
484 shapes.append(objects[i].Shape)
487 if hasattr(objects[i],
"Group"):
490 if objects[i].__class__.__name__ ==
"DocumentObjectGroup":
497 if groupshape
is not None:
498 shapes.append(groupshape)
499 objects = objects[:i] + groupobjects + objects[i:]
500 i += len(groupobjects) - 1
507 shape = shapes[0].fuse(shapes[1:])
512 if hasattr(group,
"Placement"):
513 shape.Placement = group.Placement
514 return objects, shape
536 if not self.
shape_info[obj1][
"shape"].BoundBox.intersect(
541 if self.
fp.CheckingLevel ==
"Shape distance" and \
547 if self.
fp.CheckingLevel ==
"Intersection volume" or \
548 self.
fp.CheckingLevel ==
"Intersection volume visualizations":
550 intersection = self.
shape_info[obj1][
"shape"].common(
554 if intersection.Volume == 0:
558 if self.
fp.CheckingLevel ==
"Intersection volume visualizations":
560 (intersection, obj1, obj2,
561 self.
fp.IntersectionColor))
575 collision = self.
fp.newObject(
"Part::FeaturePython",
"Collision")
579 collision.purgeTouched()
603 if self.
fp.RememberCollisions:
607 o.ViewObject.Transparency = self.
fp.CollidedTransparency
608 o.ViewObject.ShapeColor = self.
fp.CollidedShapeColor
609 o.ViewObject.LineColor = self.
fp.CollidedLineColor
610 o.ViewObject.LineWidth = self.
fp.CollidedLineWidth
616 o.ViewObject.Transparency = style[
"Transparency"]
617 o.ViewObject.ShapeColor = style[
"ShapeColor"]
618 o.ViewObject.LineColor = style[
"LineColor"]
619 o.ViewObject.LineWidth = style[
"LineWidth"]
622 for obj
in in_collision:
624 o.ViewObject.Transparency = self.
fp.InCollisionTransparency
625 o.ViewObject.ShapeColor = self.
fp.InCollisionShapeColor
626 o.ViewObject.LineColor = self.
fp.InCollisionLineColor
627 o.ViewObject.LineWidth = self.
fp.InCollisionLineWidth
637 FreeCAD.Console.PrintWarning(
"Can't reset, Checking!")
643 obj = FreeCAD.ActiveDocument.getObject(obj_name)
644 obj.ViewObject.Transparency = style[
"Transparency"]
645 obj.ViewObject.ShapeColor = tuple(style[
"ShapeColor"])
646 obj.ViewObject.LineColor = tuple(style[
"LineColor"])
647 obj.ViewObject.LineWidth = style[
"LineWidth"]
695 if isinstance(args, tuple):
721 except Exception
as e:
722 FreeCAD.Console.PrintWarning(
723 "CollisionDetector: Executing a command in the " 724 +
"command_queue.\n")
725 FreeCAD.Console.PrintWarning(str(e) +
"\n")
726 except ReferenceError
as e:
727 FreeCAD.Console.PrintLog(
728 "CollisionDetector: Deleted object in the command_queue.\n")
742 "collided": [obj.Name
for obj
in self.
collided],
743 "in_collision": [obj.Name
for obj
in self.
in_collision]}
744 data = json.JSONEncoder().encode(state)
757 state = json.JSONDecoder().decode(data)
759 self.
collided = {FreeCAD.ActiveDocument.getObject(name)
760 for name
in state[
"collided"]}
761 self.
in_collision = {FreeCAD.ActiveDocument.getObject(name)
762 for name
in state[
"in_collision"]}
814 vp.Object.Proxy.reset()
824 return path.join(PATH_TO_ICONS,
"CollisionDetector.png")
837 vp.setEditorMode(
"DisplayMode", 2)
838 vp.setEditorMode(
"Visibility", 2)
857 vp.Object.Proxy.checkCollisions()
874 action = menu.addAction(
"Check collisions")
875 action.triggered.connect(
876 vp.Object.Proxy.checkCollisions)
877 action = menu.addAction(
"Reset collision display")
878 action.triggered.connect(vp.Object.Proxy.reset)
916 if hasattr(self,
"fp"):
955 return {
'Pixmap': path.join(PATH_TO_ICONS,
"CollisionDetectorCmd.png"),
956 'MenuText':
"CollisionDetector",
957 'ToolTip':
"Create CollisionDetector instance."}
968 doc = FreeCAD.ActiveDocument
969 a = doc.addObject(
"App::DocumentObjectGroupPython",
988 if FreeCAD.ActiveDocument
is None:
996 FreeCADGui.addCommand(
'CollisionDetectorCommand',
shape_info
A dict of objects, Part objects inside them and fused shapes.
checking
A flag to signal collision checking is in progress.
def setProperties(self, fp)
Method to set properties during initialization or document restoration.
def makeCollisionObject(self, shape, cause1, cause2, color)
Method to make a collision object and add it to the CollisionDetector.
def addObserver()
Adds an AnimateDocumentObserver between FreeCAD's document observers safely.
fp
A DocumentObjectGroupPython associated with the proxy.
original_styles
A dict of objects and their style original style.
Proxy class for a DocumentObjectGroupPython CollisionDetector instance.
Proxy class for a FeaturePython Collision instance.
def execute(self, fp)
Method called when recomputing a DocumentObjectGroupPython CollisionDetector.
def __init__(self, fp)
Initialization method for CollsionDetectorProxy.
Proxy class for a Gui.ViewProviderDocumentObject Collision.ViewObject.
def IsActive(self)
Method to specify when the toolbar button and the menu item are enabled.
collided
A set of objects which have collided since the last reset.
def __setstate__(self, data)
Necessary method to restore unserializable objects when loading document.
fp
A CollisionDetector object.
def __getstate__(self)
Necessary method to avoid errors when trying to save unserializable objects.
def exploreGroup(self, group)
Method to explore a group for all objects and shapes inside.
def getIcon(self)
Method used to get a path to an icon which will appear in the tree view.
def __setstate__(self, state)
Necessary method to avoid errors when trying to restore unserializable objects.
def visualize(self, ok, in_collision)
Method to visualize which object are in-collision and which have collided.
in_collision
A set of objects which are in-collision together.
def onChanged(self, fp, prop)
Method called after DocumentObjectGroupPython CollisionDetector was changed.
def setChecking(self, value)
Method necessary to be able to set checking attribute with delayed execution.
def onBeforeChange(self, fp, prop)
Method called before DocumentObjectGroupPython CollisionDetector is changed.
def intersection(self, obj1, obj2)
Method to check intersection between obj1 and obj2.
def setResetting(self, value)
Method necessary for setting resetting attribute with delayed execution.
def executeLater(self, var, command, args)
Method to postpone execution after coin is finished (and avoid crashing coin).
def setProperties(self, vp)
Method to hide properties and attach CollisionDetectorProxy.
def GetResources(self)
Method used by FreeCAD to retrieve resources to use for this command.
def __init__(self, vp)
Initialization method for ViewProviderCollisionDetectorProxy.
def __getstate__(self)
Necessary method to save unserializable objects.
list command_queue
A list of commands interfering with Coin3D to execute later.
def canDropObject(self, obj)
Method deciding which objects can be added to a CollisionDetector group.
def reset(self)
Method to reset CollisionDetector.
Proxy class for Gui.ViewProviderDocumentObject CollisionDetector.ViewObject.
observed_objects_before
An ObservedObjects property before change.
def claimChildren(self)
Method necessary for maintaining a tree structure.
resetting
A flag to signal resetting objects to previous state.
def doubleClicked(self, vp)
Method called when CollisionDetector is double-clicked in the Tree View.
def Activated(self)
Method used as a callback when the toolbar button or the menu item is clicked.
def setupContextMenu(self, vp, menu)
Method editing a context menu for right click on a CollisionDetector.
Class specifying Animate workbench's CollisionDetector button/command.
def checkCollisions(self)
Method which checks for collisions among observed objects.
def resetObject(self, object_)
Method that resets style of an object_ to what it was before.
def executeCommandQueue(self)
Method to execute queue of postponed commands so that coin does not crash.
def onDocumentRestored(self, fp)
Method called when document is restored to make sure everything is as it was.
def onDelete(self, vp, subelements)
Method called when CollisionDetector is about to be deleted.
def loadObjects(self, objects, save_style=True)
Adds shape info and loads object to restore them during reset.