From 711ed509e359afabb97a5be792cfb8ae1e0d0e0c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 24 Apr 2015 17:11:33 +0200 Subject: [PATCH 1/3] Add a setter for calculate AABB to SceneNode and use it where needed --- BuildVolume.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildVolume.py b/BuildVolume.py index f40f6df016..c8fcc48a02 100644 --- a/BuildVolume.py +++ b/BuildVolume.py @@ -28,7 +28,7 @@ class BuildVolume(SceneNode): self._disallowed_areas = [] self._disallowed_area_mesh = None - self._calculate_aabb = False + self.setCalculateBoundingBox(False) def setWidth(self, width): self._width = width From 952f7b68ec66ca14f1cc9c8eca597c6b434963c7 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 24 Apr 2015 17:12:54 +0200 Subject: [PATCH 2/3] Add ConvexHullJob and ConvexHullNode that can be used for Convex hull calculations and display --- ConvexHullJob.py | 32 +++++++++++++++++++++ ConvexHullNode.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 ConvexHullJob.py create mode 100644 ConvexHullNode.py diff --git a/ConvexHullJob.py b/ConvexHullJob.py new file mode 100644 index 0000000000..617a0a9c35 --- /dev/null +++ b/ConvexHullJob.py @@ -0,0 +1,32 @@ +from UM.Job import Job +from UM.Application import Application +from UM.Math.Polygon import Polygon + +import numpy + +from ConvexHullNode import ConvexHullNode + +class ConvexHullJob(Job): + def __init__(self, node): + super().__init__() + + self._node = node + + def run(self): + if not self._node or not self._node.getMeshData(): + return + + mesh = self._node.getMeshData() + vertexData = mesh.getTransformed(self._node.getWorldTransformation()).getVertices() + + hull = Polygon(numpy.rint(vertexData[:, [0, 2]]).astype(int)) + + # First, calculate the normal convex hull around the points + hull = hull.getConvexHull() + # Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull. + hull = hull.getMinkowskiHull(Polygon(numpy.array([[-1, -1], [-1, 1], [1, 1], [1, -1]], numpy.float32))) + + hull_node = ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot()) + + self._node._convex_hull = hull + delattr(self._node, "_convex_hull_job") diff --git a/ConvexHullNode.py b/ConvexHullNode.py new file mode 100644 index 0000000000..fb760b874a --- /dev/null +++ b/ConvexHullNode.py @@ -0,0 +1,72 @@ +from UM.Scene.SceneNode import SceneNode +from UM.Resources import Resources +from UM.Math.Color import Color +from UM.Math.Vector import Vector +from UM.Mesh.MeshData import MeshData + +import numpy + +class ConvexHullNode(SceneNode): + def __init__(self, node, hull, parent = None): + super().__init__(parent) + + self.setCalculateBoundingBox(False) + + self._material = None + + self._original_parent = parent + + self._inherit_orientation = False + self._inherit_scale = False + + self._node = node + self._node.transformationChanged.connect(self._onNodePositionChanged) + self._node.parentChanged.connect(self._onNodeParentChanged) + #self._onNodePositionChanged(self._node) + + self._hull = hull + + hull_points = self._hull.getPoints() + center = (hull_points.min(0) + hull_points.max(0)) / 2.0 + + mesh = MeshData() + mesh.addVertex(center[0], 0.1, center[1]) + + for point in hull_points: + mesh.addVertex(point[0], 0.1, point[1]) + + indices = [] + for i in range(len(hull_points) - 1): + indices.append([0, i + 1, i + 2]) + + indices.append([0, mesh.getVertexCount() - 1, 1]) + + mesh.addIndices(numpy.array(indices, numpy.int32)) + + self.setMeshData(mesh) + + def render(self, renderer): + if not self._material: + self._material = renderer.createMaterial(Resources.getPath(Resources.ShadersLocation, 'basic.vert'), Resources.getPath(Resources.ShadersLocation, 'color.frag')) + + self._material.setUniformValue('u_color', Color(35, 35, 35, 128)) + + renderer.queueNode(self, material = self._material, transparent = True) + + return True + + def _onNodePositionChanged(self, node): + #self.setPosition(node.getWorldPosition()) + if hasattr(node, "_convex_hull"): + delattr(node, "_convex_hull") + self.setParent(None) + + + #self._node.transformationChanged.disconnect(self._onNodePositionChanged) + #self._node.parentChanged.disconnect(self._onNodeParentChanged) + + def _onNodeParentChanged(self, node): + if node.getParent(): + self.setParent(self._original_parent) + else: + self.setParent(None) From b58a5456cc60fba8516139518ea5f0ef01e9eadf Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Fri, 24 Apr 2015 17:14:04 +0200 Subject: [PATCH 3/3] Calculate convex hulls for scene nodes and use it for collision testing --- PlatformPhysics.py | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/PlatformPhysics.py b/PlatformPhysics.py index c6e0494ddd..a1217c1953 100644 --- a/PlatformPhysics.py +++ b/PlatformPhysics.py @@ -9,6 +9,7 @@ from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Application import Application from PlatformPhysicsOperation import PlatformPhysicsOperation +from ConvexHullJob import ConvexHullJob import time import threading @@ -19,7 +20,6 @@ class PlatformPhysics: self._controller = controller self._controller.getScene().sceneChanged.connect(self._onSceneChanged) self._build_volume = volume - self._signal_source = None self._change_timer = QTimer() self._change_timer.setInterval(100) @@ -39,13 +39,47 @@ class PlatformPhysics: if not bbox or not bbox.isValid(): continue + # Mark the node as outside the build volume if the bounding box test fails. if self._build_volume.getBoundingBox().intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True else: node._outside_buildarea = False + # Move the node upwards if the bottom is below the build platform. + move_vector = Vector() if not Float.fuzzyCompare(bbox.bottom, 0.0): - self._signal_source = node - op = PlatformPhysicsOperation(node, Vector(0, -bbox.bottom, 0)) - Application.getInstance().getOperationStack().push(op) - self._signal_source = None + move_vector.setY(-bbox.bottom) + + # If there is no convex hull for the node, start calculating it and continue. + if not hasattr(node, '_convex_hull'): + if not hasattr(node, '_convex_hull_job'): + job = ConvexHullJob(node) + job.start() + node._convex_hull_job = job + else: + # Check for collisions between convex hulls + for other_node in BreadthFirstIterator(root): + # Ignore root, ourselves and anything that is not a normal SceneNode. + if other_node is root or type(other_node) is not SceneNode or other_node is node: + continue + + # Ignore nodes that do not have the right properties set. + if not hasattr(other_node, '_convex_hull') or not other_node.getBoundingBox(): + continue + + # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. + if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: + continue + + # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. + overlap = node._convex_hull.intersectsPolygon(other_node._convex_hull) + if overlap is None: + continue + + print(overlap) + move_vector.setX(-overlap[0]) + move_vector.setZ(-overlap[1]) + + if move_vector != Vector(): + op = PlatformPhysicsOperation(node, move_vector) + op.push()