Browse Source

Do boundary checks on nodes for which the boundary check is unknown

Just before deciding whether to drop down the node on the build plate.

Contributes to issue CURA-4797.
Ghostkeeper 7 years ago
parent
commit
27e441ecd9
2 changed files with 49 additions and 48 deletions
  1. 42 44
      cura/BuildVolume.py
  2. 7 4
      cura/PlatformPhysics.py

+ 42 - 44
cura/BuildVolume.py

@@ -1,8 +1,7 @@
-# Copyright (c) 2017 Ultimaker B.V.
+# Copyright (c) 2018 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 
 from cura.Settings.ExtruderManager import ExtruderManager
-from UM.Settings.ContainerRegistry import ContainerRegistry
 from UM.i18n import i18nCatalog
 from UM.Scene.Platform import Platform
 from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
@@ -194,52 +193,51 @@ class BuildVolume(SceneNode):
 
         return True
 
-    ##  For every sliceable node, update node._outside_buildarea
-    #
-    def updateNodeBoundaryCheck(self):
-        root = Application.getInstance().getController().getScene().getRoot()
-        nodes = list(BreadthFirstIterator(root))
-        group_nodes = []
+    ##  For every sliceable node, update node._outside_buildarea.
+    def updateAllBoundaryChecks(self):
+        self.updateNodeBoundaryCheck(Application.getInstance().getController().getScene().getRoot())
 
-        build_volume_bounding_box = self.getBoundingBox()
-        if build_volume_bounding_box:
-            # It's over 9000!
-            build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
-        else:
-            # No bounding box. This is triggered when running Cura from command line with a model for the first time
-            # In that situation there is a model, but no machine (and therefore no build volume.
+    ##  For a single node, update _outside_buildarea.
+    #
+    #   If the node is a group node, the child nodes will also get updated.
+    #   \param node The node to update the boundary checks of.
+    def updateNodeBoundaryCheck(self, node: SceneNode):
+        if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
+            for child in node.getChildren(): #Still update the children! For instance, the root is not sliceable.
+                self.updateNodeBoundaryCheck(child)
+            return #Don't compute for non-sliceable nodes.
+
+        #Mark the node as outside the build volume if the bounding box test fails.
+        build_volume = self.getBoundingBox()
+        if build_volume is None:
+            #No bounding box. This is triggered when running Cura from command line with a model for the first time.
+            #In that situation there is a model, but no machine (and therefore no build volume).
             return
+        build_volume = build_volume.set(bottom = -999999) #Allow models to clip the build plate. This should allow printing but remove the bottom side of the model underneath the build plate.
+        bounding_box = node.getBoundingBox()
+        if build_volume.intersectsBox(bounding_box) != AxisAlignedBox.IntersectionResult.FullIntersection:
+            node._outside_buildarea = True
+        else:
 
-        for node in nodes:
-            # Need to check group nodes later
-            if node.callDecoration("isGroup"):
-                group_nodes.append(node)  # Keep list of affected group_nodes
-
-            if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
-                node._outside_buildarea = False
-                bbox = node.getBoundingBox()
-
-                # Mark the node as outside the build volume if the bounding box test fails.
-                if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
+            #Check for collisions between disallowed areas and the object.
+            convex_hull = node.callDecoration("getConvexHull")
+            if not convex_hull or not convex_hull.isValid():
+                return
+            for area in self.getDisallowedAreas():
+                overlap = convex_hull.intersectsPolygon(area)
+                if overlap is not None:
                     node._outside_buildarea = True
-                    continue
-
-                convex_hull = node.callDecoration("getConvexHull")
-                if convex_hull:
-                    if not convex_hull.isValid():
-                        return
-                    # Check for collisions between disallowed areas and the object
-                    for area in self.getDisallowedAreas():
-                        overlap = convex_hull.intersectsPolygon(area)
-                        if overlap is None:
-                            continue
-                        node._outside_buildarea = True
-                        continue
+                    break
+            else:
+                node._outside_buildarea = False
 
-        # Group nodes should override the _outside_buildarea property of their children.
-        for group_node in group_nodes:
-            for child_node in group_node.getAllChildren():
-                child_node._outside_buildarea = group_node._outside_buildarea
+        #Group nodes should override the _outside_buildarea property of their children.
+        if node.callDecoration("isGroup"):
+            for child in node.getAllChildren():
+                child._outside_buildarea = node._outside_buildarea
+        else:
+            for child in node.getChildren():
+                self.updateNodeBoundaryCheck(child)
 
     ##  Recalculates the build volume & disallowed areas.
     def rebuild(self):
@@ -424,7 +422,7 @@ class BuildVolume(SceneNode):
 
         Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
 
-        self.updateNodeBoundaryCheck()
+        self.updateAllBoundaryChecks()
 
     def getBoundingBox(self) -> AxisAlignedBox:
         return self._volume_aabb

+ 7 - 4
cura/PlatformPhysics.py

@@ -56,14 +56,17 @@ class PlatformPhysics:
         # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
         nodes = list(BreadthFirstIterator(root))
 
-        # Only check nodes inside build area.
-        nodes = [node for node in nodes if getattr(node, "_outside_buildarea", False)]
-
         random.shuffle(nodes)
         for node in nodes:
             if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
                 continue
 
+            #Only check nodes inside the build area.
+            if not hasattr(node, "_outside_buildarea"):
+                self._build_volume.updateNodeBoundaryCheck(node)
+            if getattr(node, "_outside_buildarea", True):
+                continue
+
             bbox = node.getBoundingBox()
 
             # Move it downwards if bottom is above platform
@@ -155,7 +158,7 @@ class PlatformPhysics:
 
         # After moving, we have to evaluate the boundary checks for nodes
         build_volume = Application.getInstance().getBuildVolume()
-        build_volume.updateNodeBoundaryCheck()
+        build_volume.updateAllBoundaryChecks()
 
     def _onToolOperationStarted(self, tool):
         self._enabled = False