CollisionDetector.py
1 # -*- coding: utf-8 -*-
2 
3 # ***************************************************************************
4 # * *
5 # * Animate workbench - FreeCAD Workbench for lightweight animation *
6 # * Copyright (c) 2019 Jiří Valášek jirka362@gmail.com *
7 # * *
8 # * This file is part of the Animate workbench. *
9 # * *
10 # * This program is free software; you can redistribute it and/or modify *
11 # * it under the terms of the GNU Lesser General Public License (LGPL) *
12 # * as published by the Free Software Foundation; either version 2 of *
13 # * the License, or (at your option) any later version. *
14 # * for detail see the LICENCE text file. *
15 # * *
16 # * Animate workbench is distributed in the hope that it will be useful, *
17 # * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 # * GNU Lesser General Public License for more details. *
20 # * *
21 # * You should have received a copy of the GNU Library General Public *
22 # * License along with Animate workbench; if not, write to the Free *
23 # * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
24 # * MA 02111-1307 USA *
25 # * *
26 # ***************************************************************************/
27 
28 
35 
36 import FreeCAD
37 import FreeCADGui
38 import json
39 
40 from CollisionObject import CollisionProxy, ViewProviderCollisionProxy
41 from PySide2.QtWidgets import QMessageBox
42 from PySide2.QtCore import QTimer
43 from os import path
44 
45 
46 PATH_TO_ICONS = path.join(FreeCAD.getHomePath(), "Mod", "Animate", "Resources",
47  "Icons")
48 
49 
50 
66 
67 class CollisionDetectorProxy(object):
68 
69 
71 
72 
74 
75 
77 
78 
80 
81 
83 
84 
86 
87 
89 
90 
92 
93 
95  command_queue = []
96 
97 
106 
107  def __init__(self, fp):
108  self.setProperties(fp)
109  fp.Proxy = self
110 
111 
120 
121  def onDocumentRestored(self, fp):
122  fp.ViewObject.Proxy.setProperties(fp.ViewObject)
123  self.setProperties(fp)
124 
125 
134 
135  def onBeforeChange(self, fp, prop):
136  if prop == "ObservedObjects":
137  self.observed_objects_before = fp.ObservedObjects
138 
139 
148 
149  def onChanged(self, fp, prop):
150  # Check if an object was deleted/added from the ObservedObjects
151  # and reset it to the original style/remember its style
152  if prop == "ObservedObjects":
153  removed_objects = set(self.observed_objects_before)\
154  - set(fp.ObservedObjects)
155  for obj in removed_objects:
156  self.resetObject(obj)
157  added_objects = set(fp.ObservedObjects) \
158  - set(self.shape_info.keys())
159  self.loadObjects(added_objects)
160  # remember if all observed objects are valid
161  fp.ValidObservedObjects = (set(self.shape_info.keys())
162  == set(fp.ObservedObjects))
163 
164 
173 
174  def resetObject(self, object_):
175  # Check that object to be reset has shape info recorded.
176  if object_ in self.shape_info.keys():
177  # Go through all 'Part objects' inside
178  for obj in self.shape_info.pop(object_)["objects"]:
179  # Reset styles
180  if obj.Name in self.original_styles.keys():
181  style = self.original_styles.pop(obj.Name)
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"]
186 
187 
195 
196  def loadObjects(self, objects, save_style=True):
197  # Go through objects
198  for obj in objects:
199  # Invalid object - No shape nor group
200  if not hasattr(obj, "Shape") and not hasattr(obj, "Group"):
201  QMessageBox.warning(
202  None,
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.")
208  # Group object
209  elif not hasattr(obj, "Shape") and hasattr(obj, "Group"):
210  # Explore it
211  groupobjects, groupshape = self.exploreGroup(obj)
212  if groupshape is not None:
213  self.shape_info[obj] = {"objects": groupobjects,
214  "shape": groupshape}
215  if save_style:
216  for obj in groupobjects:
217  self.original_styles[obj.Name] = {
218  "Transparency": obj.ViewObject.Transparency,
219  "ShapeColor": obj.ViewObject.ShapeColor,
220  "LineColor": obj.ViewObject.LineColor,
221  "LineWidth": obj.ViewObject.LineWidth}
222  else:
223  QMessageBox.warning(
224  None,
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.")
230  # Regular object
231  else:
232  self.shape_info[obj] = {"objects": [obj],
233  "shape": obj.Shape}
234  if save_style:
235  self.original_styles[obj.Name] = {
236  "Transparency": obj.ViewObject.Transparency,
237  "ShapeColor": obj.ViewObject.ShapeColor,
238  "LineColor": obj.ViewObject.LineColor,
239  "LineWidth": obj.ViewObject.LineWidth}
240 
241 
248 
249  def execute(self, fp):
250  self.checkCollisions()
251 
252 
260 
261  def setProperties(self, fp):
262  if not hasattr(fp, "ValidObservedObjects"):
263  fp.addProperty(
264  "App::PropertyBool", "ValidObservedObjects", "General",
265  "All objects are valid for collision detection"
266  ).ValidObservedObjects = False
267  # Add (and preset) properties
268  if not hasattr(fp, "ObservedObjects"):
269  fp.addProperty(
270  "App::PropertyLinkListGlobal", "ObservedObjects", "General",
271  "Objects that will be checked for intersections.")
272  if not hasattr(fp, "RememberCollisions"):
273  fp.addProperty(
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",
284  "Shape distance",
285  "Intersection volume",
286  "Intersection volume visualizations"]
287  # Intersection style
288  if not hasattr(fp, "IntersectionColor"):
289  fp.addProperty(
290  "App::PropertyColor", "IntersectionColor", "IntersectionStyle",
291  "Color for highlighting intersections."
292  ).IntersectionColor = (1.0, 0.0, 0.0)
293 
294  # Style of objects in collision
295  if not hasattr(fp, "InCollisionTransparency"):
296  fp.addProperty(
297  "App::PropertyPercent", "InCollisionTransparency",
298  "In-CollisionStyle",
299  "Transparency set to objects in collision."
300  ).InCollisionTransparency = 50
301  if not hasattr(fp, "InCollisionShapeColor"):
302  fp.addProperty(
303  "App::PropertyColor", "InCollisionShapeColor",
304  "In-CollisionStyle",
305  "Shape color for highlighting objects in collision."
306  ).InCollisionShapeColor = (1.0, 0.667, 0.333)
307  if not hasattr(fp, "InCollisionLineColor"):
308  fp.addProperty(
309  "App::PropertyColor", "InCollisionLineColor",
310  "In-CollisionStyle",
311  "Line color for highlighting objects in collision."
312  ).InCollisionLineColor = (1.0, 0.667, 0.0)
313  if not hasattr(fp, "InCollisionLineWidth"):
314  fp.addProperty(
315  "App::PropertyFloatConstraint", "InCollisionLineWidth",
316  "In-CollisionStyle",
317  "Line width for highlighting objects\n"
318  + "in collision. Range is < 1 | 64 >."
319  ).InCollisionLineWidth = (2, 1, 64, 1)
320  else:
321  fp.InCollisionLineWidth = (fp.InCollisionLineWidth, 2, 64, 1)
322 
323  # Style of collided objects
324  if not hasattr(fp, "CollidedTransparency"):
325  fp.addProperty(
326  "App::PropertyPercent", "CollidedTransparency",
327  "CollidedStyle", "Transparency set to collided objects."
328  ).CollidedTransparency = 50
329  if not hasattr(fp, "CollidedShapeColor"):
330  fp.addProperty(
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"):
335  fp.addProperty(
336  "App::PropertyColor", "CollidedLineColor",
337  "CollidedStyle",
338  "Line color for highlighting objects in collision."
339  ).CollidedLineColor = (0.667, 0.0, 1.0)
340  if not hasattr(fp, "CollidedLineWidth"):
341  fp.addProperty(
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)
347  else:
348  fp.CollidedLineWidth = (fp.CollidedLineWidth, 2, 64, 1)
349 
350  if not hasattr(self, "in_collision") or \
351  (hasattr(self, "in_collision") and self.in_collision is None):
352  self.in_collision = set()
353  if not hasattr(self, "collided") or \
354  (hasattr(self, "collided") and self.collided is None):
355  self.collided = set()
356  if not hasattr(self, "original_styles") or \
357  (hasattr(self, "original_styles") and
358  self.original_styles is None):
359  self.original_styles = dict()
360  if not hasattr(self, "shape_info") or \
361  (hasattr(self, "shape_info") and
362  self.shape_info is None):
363  self.shape_info = dict()
364  self.loadObjects(fp.ObservedObjects, save_style=False)
365 
366  self.fp = fp
367  self.checking = False
368  self.resetting = False
369 
370  fp.setEditorMode("Group", 1)
371  fp.setEditorMode("ValidObservedObjects", 2)
372  fp.ValidObservedObjects = (set(self.shape_info.keys())
373  == set(fp.ObservedObjects))
374 
375  import AnimateDocumentObserver
377 
378 
383 
384  def checkCollisions(self):
385  # Don't check if there are invalid objects
386  if not self.fp.ValidObservedObjects:
387  QMessageBox.warning(
388  None,
389  'Error while checking collisions',
390  "One or more objects to check for collisions "
391  + "don't have shapes attached, or are empty groups.")
392  return
393 
394  # Don't check if resetting
395  if self.resetting:
396  FreeCAD.Console.PrintWarning("Can't check, Resetting!")
397  return
398 
399  # Don't check if already checking
400  if self.checking:
401  FreeCAD.Console.PrintWarning("Already Checking!")
402  return
403  else:
404  self.checking = True
405 
406  # Prepare sets for objects in-collision and not-in-collision(ok)
407  in_collision = set()
408  ok = set()
409 
410  # Remove previously detected collisions
411  self.executeLater(None, self.fp.removeObjectsFromDocument, None)
412 
413  # No observed object present
414  if len(self.fp.ObservedObjects) == 0:
415  FreeCAD.Console.PrintWarning(
416  "CollisionDetector observes no objects.\n")
417  self.checking = False
418  return
419 
420  # Only 1 observed object
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])
425  self.visualize(ok, in_collision)
426  self.checking = False
427  return
428 
429  # Go through observed objects and update their placement
430  for obj in self.fp.ObservedObjects:
431  if len(self.shape_info[obj]["objects"]) >= 2:
432  self.shape_info[obj]["shape"] = \
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 = \
437  obj.Placement.Base
438  self.shape_info[obj]["shape"].Placement.Rotation = \
439  obj.Placement.Rotation
440 
441  elif hasattr(obj, "Placement"):
442  self.shape_info[obj]["shape"] = obj.Shape
443 
444  # Go through observed objects and check for intersections
445  for i in range(len(self.fp.ObservedObjects) - 1):
446  for j in range(i+1, len(self.fp.ObservedObjects)):
447  if self.intersection(self.fp.ObservedObjects[i],
448  self.fp.ObservedObjects[j]):
449  in_collision.add(self.fp.ObservedObjects[i])
450  in_collision.add(self.fp.ObservedObjects[j])
451  else:
452  ok.add(self.fp.ObservedObjects[i])
453  ok.add(self.fp.ObservedObjects[j])
454 
455  # make ok and in-collision disjoint
456  ok.difference_update(in_collision)
457  # visualize which objects are ok/in-collision/collided
458  self.visualize(ok, in_collision)
459  # set checking to false after all objects are truly removed and added
460  self.executeLater(None, self.setChecking, False)
461 
462 
474 
475  def exploreGroup(self, group):
476  # Shapes of objects in the group
477  shapes = []
478  # Objects and groups to go through
479  objects = group.Group
480  i = 0
481  while i < len(objects):
482  # Object has a shape attached
483  if hasattr(objects[i], "Shape"):
484  shapes.append(objects[i].Shape)
485 
486  # Object has a group attached
487  if hasattr(objects[i], "Group"):
488  # Remove regular groups as their content is already between
489  # objects
490  if objects[i].__class__.__name__ == "DocumentObjectGroup":
491  objects.pop(i)
492 
493  # Go through content of other groups and add it
494  else:
495  groupobjects, groupshape = self.exploreGroup(
496  objects.pop(i))
497  if groupshape is not None:
498  shapes.append(groupshape)
499  objects = objects[:i] + groupobjects + objects[i:]
500  i += len(groupobjects) - 1
501  i += 1
502 
503  # There are shapes present in the group
504  if len(shapes) != 0:
505  # There are more than 2 shapes in the group
506  if len(shapes) >= 2:
507  shape = shapes[0].fuse(shapes[1:])
508  else:
509  shape = shapes[0]
510 
511  # Group has a placement property
512  if hasattr(group, "Placement"):
513  shape.Placement = group.Placement
514  return objects, shape
515 
516  # No shapes in the group
517  else:
518  return objects, None
519 
520 
533 
534  def intersection(self, obj1, obj2):
535  # Check Bounding box is intersecting (the fastest and crudest)
536  if not self.shape_info[obj1]["shape"].BoundBox.intersect(
537  self.shape_info[obj2]["shape"].BoundBox):
538  return False
539 
540  # Check the shortest distance between the shapes is 0
541  if self.fp.CheckingLevel == "Shape distance" and \
542  self.shape_info[obj1]["shape"].distToShape(
543  self.shape_info[obj2]["shape"])[0] > 0:
544  return False
545 
546  # If requested, check intersection volume, and show intersection
547  if self.fp.CheckingLevel == "Intersection volume" or \
548  self.fp.CheckingLevel == "Intersection volume visualizations":
549  # Compute common volume to both objects
550  intersection = self.shape_info[obj1]["shape"].common(
551  self.shape_info[obj2]["shape"])
552 
553  # Test common volume is not 0 i.e. objects are not just touching
554  if intersection.Volume == 0:
555  return False
556 
557  # Make an Collision object to show the intersection if asked for
558  if self.fp.CheckingLevel == "Intersection volume visualizations":
559  self.executeLater(None, self.makeCollisionObject,
560  (intersection, obj1, obj2,
561  self.fp.IntersectionColor))
562  return True
563 
564 
572 
573  def makeCollisionObject(self, shape, cause1, cause2, color):
574  # Add new object to the CollisionDetector
575  collision = self.fp.newObject("Part::FeaturePython", "Collision")
576  # Attach a Proxy to it and it's ViewObject, then purge its touched flag
577  CollisionProxy(collision, shape, cause1, cause2)
578  ViewProviderCollisionProxy(collision.ViewObject, color)
579  collision.purgeTouched()
580 
581 
591 
592  def visualize(self, ok, in_collision):
593  # Compute which objects are no longer in collision
594  collided = self.in_collision.intersection(ok)
595  # Add them to a set of objects which have collided
596  self.collided = self.collided.union(collided)
597  # Remove objects which no longer collide from a set for such objects
598  self.in_collision.difference_update(collided)
599  # Add new object which are in-collision to the set
600  self.in_collision = self.in_collision.union(in_collision)
601 
602  # If collided objects shall be shown
603  if self.fp.RememberCollisions:
604  # show them
605  for obj in collided:
606  for o in self.shape_info[obj]["objects"]:
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
611  else:
612  # otherwise reset them
613  for obj in collided:
614  for o in self.shape_info[obj]["objects"]:
615  style = self.original_styles[obj.Name]
616  o.ViewObject.Transparency = style["Transparency"]
617  o.ViewObject.ShapeColor = style["ShapeColor"]
618  o.ViewObject.LineColor = style["LineColor"]
619  o.ViewObject.LineWidth = style["LineWidth"]
620 
621  # Show objects in-collision
622  for obj in in_collision:
623  for o in self.shape_info[obj]["objects"]:
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
628 
629 
634 
635  def reset(self):
636  if self.checking:
637  FreeCAD.Console.PrintWarning("Can't reset, Checking!")
638  return
639  else:
640  self.resetting = True
641  self.executeLater(None, self.fp.removeObjectsFromDocument, None)
642  for obj_name, style in self.original_styles.items():
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"]
648  self.in_collision = set()
649  self.collided = set()
650  # set resetting to false after all objects are truly removed
651  self.executeLater(None, self.setResetting, False)
652 
653 
662 
663  def setChecking(self, value):
664  self.checking = value
665 
666 
675 
676  def setResetting(self, value):
677  self.resetting = value
678 
679 
693 
694  def executeLater(self, var, command, args):
695  if isinstance(args, tuple):
696  self.command_queue.append((var, command, args))
697  elif args is None:
698  self.command_queue.append((var, command, ()))
699  else:
700  self.command_queue.append((var, command, (args,)))
701  QTimer.singleShot(0, self.executeCommandQueue)
702 
703 
712 
714  try:
715  for var, cmd, args in self.command_queue:
716  try:
717  if var is not None:
718  cmd(*args)
719  else:
720  var = cmd(*args)
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")
729  self.command_queue = []
730 
731 
739 
740  def __getstate__(self):
741  state = {"original_styles": self.original_styles,
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)
745  return data
746 
747 
755 
756  def __setstate__(self, data):
757  state = json.JSONDecoder().decode(data)
758  self.original_styles = state["original_styles"]
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"]}
763 
764 
765 
781 
783 
784 
786 
787 
796 
797  def __init__(self, vp):
798  vp.Proxy = self
799  self.setProperties(vp)
800 
801 
812 
813  def onDelete(self, vp, subelements):
814  vp.Object.Proxy.reset()
815  return True
816 
817 
822 
823  def getIcon(self):
824  return path.join(PATH_TO_ICONS, "CollisionDetector.png")
825 
826 
834 
835  def setProperties(self, vp):
836  # Hide unnecessary view properties
837  vp.setEditorMode("DisplayMode", 2)
838  vp.setEditorMode("Visibility", 2)
839 
840  # Add feature python as it's necessary to claimChildren
841  self.fp = vp.Object
842 
843 
853 
854  def doubleClicked(self, vp):
855  # pass do something
856  try:
857  vp.Object.Proxy.checkCollisions()
858  finally:
859  return True
860 
861 
871 
872  def setupContextMenu(self, vp, menu):
873  menu.clear()
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)
879 
880 
892 
893  def __getstate__(self):
894  return None
895 
896 
905 
906  def __setstate__(self, state):
907  return None
908 
909 
914 
915  def claimChildren(self):
916  if hasattr(self, "fp"):
917  if self.fp:
918  return self.fp.Group
919  return []
920 
921 
931 
932  def canDropObject(self, obj):
933  # Don't accept any objects
934  return False
935 
936 
937 
943 
945 
946 
953 
954  def GetResources(self):
955  return {'Pixmap': path.join(PATH_TO_ICONS, "CollisionDetectorCmd.png"),
956  'MenuText': "CollisionDetector",
957  'ToolTip': "Create CollisionDetector instance."}
958 
959 
966 
967  def Activated(self):
968  doc = FreeCAD.ActiveDocument
969  a = doc.addObject("App::DocumentObjectGroupPython",
970  "CollisionDetector")
972  if FreeCAD.GuiUp:
974  doc.recompute()
975  return
976 
977 
986 
987  def IsActive(self):
988  if FreeCAD.ActiveDocument is None:
989  return False
990  else:
991  return True
992 
993 
994 if FreeCAD.GuiUp:
995  # Add command to FreeCAD Gui when importing this module in InitGui
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.
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.