From a3ac20172bcabf2baf297c651b215e25cce8caf1 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 23 May 2018 12:54:18 +0200 Subject: [PATCH] CURA-5370 The minimum offset between objects in the Arranger is now determined by the brim/skirt/... setting --- cura/BuildVolume.py | 8 +++--- cura/CuraActions.py | 3 ++- cura/CuraApplication.py | 4 +-- tests/TestArrange.py | 55 +++++++++++++++++++++++++++++++++-------- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index ea0d8e1565..487c5268fc 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -460,7 +460,7 @@ class BuildVolume(SceneNode): minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h - self._raft_thickness - self._extra_z_clearance, max_d)) - bed_adhesion_size = self._getEdgeDisallowedSize() + bed_adhesion_size = self.getEdgeDisallowedSize() # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! @@ -652,7 +652,7 @@ class BuildVolume(SceneNode): extruder_manager = ExtruderManager.getInstance() used_extruders = extruder_manager.getUsedExtruderStacks() - disallowed_border_size = self._getEdgeDisallowedSize() + disallowed_border_size = self.getEdgeDisallowedSize() if not used_extruders: # If no extruder is used, assume that the active extruder is used (else nothing is drawn) @@ -962,12 +962,12 @@ class BuildVolume(SceneNode): all_values[i] = 0 return all_values - ## Convenience function to calculate the disallowed radius around the edge. + ## Calculate the disallowed radius around the edge. # # This disallowed radius is to allow for space around the models that is # not part of the collision radius, such as bed adhesion (skirt/brim/raft) # and travel avoid distance. - def _getEdgeDisallowedSize(self): + def getEdgeDisallowedSize(self): if not self._global_container_stack or not self._global_container_stack.extruders: return 0 diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 75338f17b6..92c0e8ae1c 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -73,7 +73,8 @@ class CuraActions(QObject): # \param count The number of times to multiply the selection. @pyqtSlot(int) def multiplySelection(self, count: int) -> None: - job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = 8) + min_offset = Application.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors + job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = max(min_offset, 8)) job.start() ## Delete all selected objects. diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9c752cb7a2..0ac7dcf018 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1264,7 +1264,8 @@ class CuraApplication(QtApplication): # \param nodes nodes that we have to place # \param fixed_nodes nodes that are placed in the arranger before finding spots for nodes def arrange(self, nodes, fixed_nodes): - job = ArrangeObjectsJob(nodes, fixed_nodes) + min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors + job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset = max(min_offset, 8)) job.start() ## Reload all mesh data on the screen from file. @@ -1613,7 +1614,6 @@ class CuraApplication(QtApplication): if(original_node.getScale() != Vector(1.0, 1.0, 1.0)): node.scale(original_node.getScale()) - node.setSelectable(True) node.setName(os.path.basename(filename)) self.getBuildVolume().checkBoundsAndUpdate(node) diff --git a/tests/TestArrange.py b/tests/TestArrange.py index 354bbf4962..f383fc0cf3 100755 --- a/tests/TestArrange.py +++ b/tests/TestArrange.py @@ -4,16 +4,26 @@ from cura.Arranging.Arrange import Arrange from cura.Arranging.ShapeArray import ShapeArray +## Triangle of area 12 +def gimmeTriangle(): + return numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32) + + +## Boring square +def gimmeSquare(): + return numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32) + + ## Triangle of area 12 def gimmeShapeArray(scale = 1.0): - vertices = numpy.array([[-3, 1], [3, 1], [0, -3]], dtype=numpy.int32) + vertices = gimmeTriangle() shape_arr = ShapeArray.fromPolygon(vertices, scale = scale) return shape_arr ## Boring square def gimmeShapeArraySquare(scale = 1.0): - vertices = numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32) + vertices = gimmeSquare() shape_arr = ShapeArray.fromPolygon(vertices, scale = scale) return shape_arr @@ -69,7 +79,7 @@ def test_ShapeArray_scaling2(): ## Test centerFirst def test_centerFirst(): - ar = Arrange(300, 300, 150, 150) + ar = Arrange(300, 300, 150, 150, scale = 1) ar.centerFirst() assert ar._priority[150][150] < ar._priority[170][150] assert ar._priority[150][150] < ar._priority[150][170] @@ -81,7 +91,7 @@ def test_centerFirst(): ## Test centerFirst def test_centerFirst_rectangular(): - ar = Arrange(400, 300, 200, 150) + ar = Arrange(400, 300, 200, 150, scale = 1) ar.centerFirst() assert ar._priority[150][200] < ar._priority[150][220] assert ar._priority[150][200] < ar._priority[170][200] @@ -93,7 +103,7 @@ def test_centerFirst_rectangular(): ## Test centerFirst def test_centerFirst_rectangular(): - ar = Arrange(10, 20, 5, 10) + ar = Arrange(10, 20, 5, 10, scale = 1) ar.centerFirst() print(ar._priority) assert ar._priority[10][5] < ar._priority[10][7] @@ -101,7 +111,7 @@ def test_centerFirst_rectangular(): ## Test backFirst def test_backFirst(): - ar = Arrange(300, 300, 150, 150) + ar = Arrange(300, 300, 150, 150, scale = 1) ar.backFirst() assert ar._priority[150][150] < ar._priority[170][150] assert ar._priority[150][150] < ar._priority[170][170] @@ -111,7 +121,7 @@ def test_backFirst(): ## See if the result of bestSpot has the correct form def test_smoke_bestSpot(): - ar = Arrange(30, 30, 15, 15) + ar = Arrange(30, 30, 15, 15, scale = 1) ar.centerFirst() shape_arr = gimmeShapeArray() @@ -124,7 +134,7 @@ def test_smoke_bestSpot(): ## Real life test def test_bestSpot(): - ar = Arrange(16, 16, 8, 8) + ar = Arrange(16, 16, 8, 8, scale = 1) ar.centerFirst() shape_arr = gimmeShapeArray() @@ -144,7 +154,7 @@ def test_bestSpot(): ## Real life test rectangular build plate def test_bestSpot_rectangular_build_plate(): - ar = Arrange(16, 40, 8, 20) + ar = Arrange(16, 40, 8, 20, scale = 1) ar.centerFirst() shape_arr = gimmeShapeArray() @@ -283,7 +293,7 @@ def test_checkShape_place(): ## Test the whole sequence def test_smoke_place_objects(): - ar = Arrange(20, 20, 10, 10) + ar = Arrange(20, 20, 10, 10, scale = 1) ar.centerFirst() shape_arr = gimmeShapeArray() @@ -342,3 +352,28 @@ def test_check2(): assert check_array[3][4] +## Just adding some stuff to ensure fromNode works as expected. Some parts should actually be in UM +def test_parts_of_fromNode(): + from UM.Math.Polygon import Polygon + p = Polygon(numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32)) + offset = 1 + print(p._points) + p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset)) + print("--------------") + print(p_offset._points) + assert len(numpy.where(p_offset._points[:, 0] >= 2.9)) > 0 + assert len(numpy.where(p_offset._points[:, 0] <= -2.9)) > 0 + assert len(numpy.where(p_offset._points[:, 1] >= 2.9)) > 0 + assert len(numpy.where(p_offset._points[:, 1] <= -2.9)) > 0 + + +def test_parts_of_fromNode2(): + from UM.Math.Polygon import Polygon + p = Polygon(numpy.array([[-2, -2], [2, -2], [2, 2], [-2, 2]], dtype=numpy.int32) * 2) # 4x4 + offset = 13.3 + scale = 0.5 + p_offset = p.getMinkowskiHull(Polygon.approximatedCircle(offset)) + shape_arr1 = ShapeArray.fromPolygon(p._points, scale = scale) + shape_arr2 = ShapeArray.fromPolygon(p_offset._points, scale = scale) + assert shape_arr1.arr.shape[0] >= (4 * scale) - 1 # -1 is to account for rounding errors + assert shape_arr2.arr.shape[0] >= (2 * offset + 4) * scale - 1