diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index f938b44d1f..d122098565 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -8,6 +8,7 @@ from UM.Logger import Logger from UM.Message import Message from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog +from cura.Arranging.GridArrange import GridArrange from cura.Arranging.Nest2DArrange import arrange i18n_catalog = i18nCatalog("cura") @@ -15,12 +16,12 @@ i18n_catalog = i18nCatalog("cura") class ArrangeObjectsJob(Job): def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset=8, - lock_rotation: bool = False) -> None: + grid_arrange: bool = False) -> None: super().__init__() self._nodes = nodes self._fixed_nodes = fixed_nodes self._min_offset = min_offset - self._lock_rotation = lock_rotation + self._grid_arrange = grid_arrange def run(self): found_solution_for_all = False @@ -32,8 +33,14 @@ class ArrangeObjectsJob(Job): status_message.show() try: - found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, - lock_rotation=self._lock_rotation) + + if self._grid_arrange: + grid_arrange = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + found_solution_for_all = grid_arrange.arrange() + + else: + found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + except: # If the thread crashes, the message should still close Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 543971c5b2..0d76ecff52 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -26,7 +26,13 @@ class GridArrange: self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes - def arrange(self) -> Tuple[GroupedOperation, int]: + def arrange(self)-> bool: + + grouped_operation, not_fit_count = self.createGroupOperationForArrange() + grouped_operation.push() + return not_fit_count == 0 + + def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: self._grid_width = 0 self._grid_height = 0 for node in self._nodes_to_arrange: diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 3567c1532f..1b98bdddd8 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -18,6 +18,7 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.TranslateOperation import TranslateOperation import cura.CuraApplication +from cura.Arranging.GridArrange import GridArrange from cura.Operations.SetParentOperation import SetParentOperation from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation @@ -84,16 +85,16 @@ class CuraActions(QObject): operation.push() @pyqtSlot(int, bool) - def multiplySelection(self, count: int, lock_rotation: bool) -> None: + def multiplySelection(self, count: int, grid_placement: bool) -> None: """Multiply all objects in the selection :param count: The number of times to multiply the selection. - :param lock_rotation: If set to true the orientation of the object will remain the same + :param grid_placement: If set to true objects are placed in a grid """ min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset=max(min_offset, 8), - lock_rotation=lock_rotation) + grid_arrange=grid_placement) job.start() @pyqtSlot() @@ -231,9 +232,8 @@ class CuraActions(QObject): if node.callDecoration("isSliceable"): fixed_nodes.append(node) # Add the new nodes to the scene, and arrange them - group_operation, not_fit_count = createGroupOperationForArrange(nodes, application.getBuildVolume(), - fixed_nodes, factor=10000, - add_new_nodes_in_scene=True) + grid_arrange = GridArrange(nodes, application.getBuildVolume(), fixed_nodes) + group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() group_operation.push() # deselect currently selected nodes, and select the new nodes diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 82d009e436..c6c0ed3cd2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1441,7 +1441,7 @@ class CuraApplication(QtApplication): # Single build plate @pyqtSlot(bool) - def arrangeAll(self, lock_rotation: bool) -> None: + def arrangeAll(self, grid_arrangement: bool) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate locked_nodes = [] @@ -1471,18 +1471,18 @@ class CuraApplication(QtApplication): locked_nodes.append(node) else: nodes_to_arrange.append(node) - self.arrange(nodes_to_arrange, locked_nodes, lock_rotation) + self.arrange(nodes_to_arrange, locked_nodes, grid_arrangement) - def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], lock_rotation: bool = False) -> None: + def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], grid_arrangement: bool = False) -> None: """Arrange a set of nodes given a set of fixed nodes :param nodes: nodes that we have to place :param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes - :param lock_rotation: If set to true the orientation of the object will remain the same + :param grid_arrangement: If set to true if objects are to be placed in a grid """ min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors - job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), lock_rotation=lock_rotation) + job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), grid_arrange =grid_arrangement) job.start() @pyqtSlot() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index e1cead7557..6df80eb01c 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -21,12 +21,12 @@ i18n_catalog = i18nCatalog("cura") class MultiplyObjectsJob(Job): - def __init__(self, objects, count: int, min_offset: int = 8, lock_rotation: bool = False): + def __init__(self, objects, count: int, min_offset: int = 8, grid_arrange: bool = False): super().__init__() self._objects = objects self._count: int = count self._min_offset: int = min_offset - self._lock_rotation: bool = lock_rotation + self._grid_arrange: bool = grid_arrange def run(self) -> None: status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime = 0, @@ -78,18 +78,16 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), - fixed_nodes) + if(self._grid_arrange): + grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(),fixed_nodes) + group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() - group_operation, not_fit_count = grid_arrange.arrange() - print("group_operation", group_operation) - # group_operation, not_fit_count = createGroupOperationForArrange(nodes, - # Application.getInstance().getBuildVolume(), - # fixed_nodes, - # factor=10000, - # add_new_nodes_in_scene=True, - # lock_rotation=self._lock_rotation) - found_solution_for_all = not_fit_count == 0 + else: + group_operation, not_fit_count = createGroupOperationForArrange(nodes, + Application.getInstance().getBuildVolume(), + fixed_nodes, + factor=10000, + add_new_nodes_in_scene=True) if nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange: diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index e47f797195..1c84ec66b7 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -41,7 +41,7 @@ Item property alias deleteAll: deleteAllAction property alias reloadAll: reloadAllAction property alias arrangeAll: arrangeAllAction - property alias arrangeAllLock: arrangeAllLockAction + property alias arrangeAllGrid: arrangeAllGridAction property alias arrangeSelection: arrangeSelectionAction property alias arrangeSelectionLock: arrangeSelectionLockAction property alias resetAllTranslation: resetAllTranslationAction @@ -464,8 +464,8 @@ Item Action { - id: arrangeAllLockAction - text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models Without Rotation") + id: arrangeAllGridAction + text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models in a grid") onTriggered: Printer.arrangeAll(true) shortcut: "Shift+Ctrl+R" } diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index f07b4d9571..75d366e5db 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -66,7 +66,7 @@ Cura.Menu Cura.MenuSeparator {} Cura.MenuItem { action: Cura.Actions.selectAll } Cura.MenuItem { action: Cura.Actions.arrangeAll } - Cura.MenuItem { action: Cura.Actions.arrangeAllLock } + Cura.MenuItem { action: Cura.Actions.arrangeAllGrid } Cura.MenuItem { action: Cura.Actions.deleteAll } Cura.MenuItem { action: Cura.Actions.reloadAll } Cura.MenuItem { action: Cura.Actions.resetAllTranslation } @@ -110,7 +110,7 @@ Cura.Menu minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumHeight: UM.Theme.getSize("small_popup_dialog").height - onAccepted: CuraActions.multiplySelection(copiesField.value, lockRotationField.checked) + onAccepted: CuraActions.multiplySelection(copiesField.value, gridPlacementSelected.checked) buttonSpacing: UM.Theme.getSize("thin_margin").width @@ -158,8 +158,8 @@ Cura.Menu UM.CheckBox { - id: lockRotationField - text: catalog.i18nc("@label", "Lock Rotation") + id: gridPlacementSelected + text: catalog.i18nc("@label", "Grid Placement") } } }