diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index fdac63cd9d..7aa424c46f 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -110,18 +110,11 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV return found_solution_for_all, node_items -def arrange(nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: Optional[List["SceneNode"]] = None, factor = 10000, add_new_nodes_in_scene: bool = False) -> bool: - """ - Find placement for a set of scene nodes, and move them by using a single grouped operation. - :param nodes_to_arrange: The list of nodes that need to be moved. - :param build_volume: The build volume that we want to place the nodes in. It gets size & disallowed areas from this. - :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes - are placed. - :param factor: The library that we use is int based. This factor defines how accuracte we want it to be. - :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations - - :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects - """ +def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], + build_volume: "BuildVolume", + fixed_nodes: Optional[List["SceneNode"]] = None, + factor = 10000, + add_new_nodes_in_scene: bool = False): scene_root = Application.getInstance().getController().getScene().getRoot() found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor) @@ -141,8 +134,30 @@ def arrange(nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fi else: # We didn't find a spot grouped_operation.addOperation( - TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), set_position = True)) + TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), + set_position=True)) not_fit_count += 1 - grouped_operation.push() - return found_solution_for_all + return grouped_operation, not_fit_count + + +def arrange(nodes_to_arrange: List["SceneNode"], + build_volume: "BuildVolume", + fixed_nodes: Optional[List["SceneNode"]] = None, + factor = 10000, + add_new_nodes_in_scene: bool = False) -> bool: + """ + Find placement for a set of scene nodes, and move them by using a single grouped operation. + :param nodes_to_arrange: The list of nodes that need to be moved. + :param build_volume: The build volume that we want to place the nodes in. It gets size & disallowed areas from this. + :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes + are placed. + :param factor: The library that we use is int based. This factor defines how accuracte we want it to be. + :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations + + :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects + """ + + grouped_operation, not_fit_count = createGroupOperationForArrange(nodes_to_arrange, build_volume, fixed_nodes, factor, add_new_nodes_in_scene) + grouped_operation.redo() + return not_fit_count != 0 diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 4c1caf137c..e7890ce2eb 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -7,10 +7,12 @@ from typing import List from UM.Application import Application from UM.Job import Job from UM.Message import Message +from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation +from UM.Operations.GroupedOperation import GroupedOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog -from cura.Arranging.Nest2DArrange import arrange +from cura.Arranging.Nest2DArrange import arrange, createGroupOperationForArrange i18n_catalog = i18nCatalog("cura") @@ -43,11 +45,11 @@ class MultiplyObjectsJob(Job): # Only count sliceable objects if node_.callDecoration("isSliceable"): fixed_nodes.append(node_) - + nodes_to_add_without_arrange = [] for node in self._objects: # If object is part of a group, multiply group current_node = node - while current_node.getParent() and (current_node.getParent().callDecoration("isGroup") or current_node.getParent().callDecoration("isSliceable")): + while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): current_node = current_node.getParent() if current_node in processed_nodes: @@ -56,19 +58,33 @@ class MultiplyObjectsJob(Job): for _ in range(self._count): new_node = copy.deepcopy(node) - # Same build plate build_plate_number = current_node.callDecoration("getBuildPlateNumber") new_node.callDecoration("setBuildPlateNumber", build_plate_number) for child in new_node.getChildren(): child.callDecoration("setBuildPlateNumber", build_plate_number) - - nodes.append(new_node) + if not current_node.getParent().callDecoration("isSliceable"): + nodes.append(new_node) + else: + # The node we're trying to place has another node that is sliceable as a parent. + # As such, we shouldn't arrange it (but it should be added to the scene!) + nodes_to_add_without_arrange.append(new_node) + new_node.setParent(current_node.getParent()) found_solution_for_all = True if nodes: - found_solution_for_all = arrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, - factor = 10000, add_new_nodes_in_scene = True) + group_operation, not_fit_count = createGroupOperationForArrange(nodes, + Application.getInstance().getBuildVolume(), + fixed_nodes, + factor=10000, add_new_nodes_in_scene=True) + else: + group_operation = GroupedOperation() + + if nodes_to_add_without_arrange: + for nested_node in nodes_to_add_without_arrange: + group_operation.addOperation(AddSceneNodeOperation(nested_node, nested_node.getParent())) + + group_operation.redo() status_message.hide() if not found_solution_for_all: