PlatformPhysics.py 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # Copyright (c) 2015 Ultimaker B.V.
  2. # Cura is released under the terms of the AGPLv3 or higher.
  3. from PyQt5.QtCore import QTimer
  4. from UM.Scene.SceneNode import SceneNode
  5. from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
  6. from UM.Operations.TranslateOperation import TranslateOperation
  7. from UM.Operations.ScaleToBoundsOperation import ScaleToBoundsOperation
  8. from UM.Math.Float import Float
  9. from UM.Math.Vector import Vector
  10. from UM.Math.AxisAlignedBox import AxisAlignedBox
  11. from UM.Application import Application
  12. from . import PlatformPhysicsOperation
  13. from . import ConvexHullJob
  14. import time
  15. import threading
  16. class PlatformPhysics:
  17. def __init__(self, controller, volume):
  18. super().__init__()
  19. self._controller = controller
  20. self._controller.getScene().sceneChanged.connect(self._onSceneChanged)
  21. self._build_volume = volume
  22. self._change_timer = QTimer()
  23. self._change_timer.setInterval(100)
  24. self._change_timer.setSingleShot(True)
  25. self._change_timer.timeout.connect(self._onChangeTimerFinished)
  26. def _onSceneChanged(self, source):
  27. self._change_timer.start()
  28. def _onChangeTimerFinished(self):
  29. root = self._controller.getScene().getRoot()
  30. for node in BreadthFirstIterator(root):
  31. if node is root or type(node) is not SceneNode:
  32. continue
  33. bbox = node.getBoundingBox()
  34. if not bbox or not bbox.isValid():
  35. continue
  36. # Mark the node as outside the build volume if the bounding box test fails.
  37. if self._build_volume.getBoundingBox().intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
  38. node._outside_buildarea = True
  39. else:
  40. node._outside_buildarea = False
  41. # Move the node upwards if the bottom is below the build platform.
  42. move_vector = Vector()
  43. if not Float.fuzzyCompare(bbox.bottom, 0.0):
  44. move_vector.setY(-bbox.bottom)
  45. # If there is no convex hull for the node, start calculating it and continue.
  46. if not hasattr(node, "_convex_hull"):
  47. if not hasattr(node, "_convex_hull_job"):
  48. job = ConvexHullJob.ConvexHullJob(node)
  49. job.start()
  50. node._convex_hull_job = job
  51. else:
  52. # Check for collisions between convex hulls
  53. for other_node in BreadthFirstIterator(root):
  54. # Ignore root, ourselves and anything that is not a normal SceneNode.
  55. if other_node is root or type(other_node) is not SceneNode or other_node is node:
  56. continue
  57. # Ignore nodes that do not have the right properties set.
  58. if not hasattr(other_node, "_convex_hull") or not other_node.getBoundingBox():
  59. continue
  60. # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects.
  61. if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection:
  62. continue
  63. # Get the overlap distance for both convex hulls. If this returns None, there is no intersection.
  64. overlap = node._convex_hull.intersectsPolygon(other_node._convex_hull)
  65. if overlap is None:
  66. continue
  67. move_vector.setX(-overlap[0])
  68. move_vector.setZ(-overlap[1])
  69. if move_vector != Vector():
  70. op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
  71. op.push()
  72. if node.getBoundingBox().intersectsBox(self._build_volume.getBoundingBox()) == AxisAlignedBox.IntersectionResult.FullIntersection:
  73. op = ScaleToBoundsOperation(node, self._build_volume.getBoundingBox())
  74. op.push()