diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6056745c75..98c682a8a3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1105,7 +1105,7 @@ class CuraApplication(QtApplication): continue if not node.getMeshData() and not node.callDecoration("isGroup"): continue # Node that doesnt have a mesh and is not a group. - if node.getParent() and node.getParent().callDecoration("isGroup"): + if node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent().callDecoration("isSliceable"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) if not node.isSelectable(): continue # i.e. node with layer data @@ -1383,6 +1383,12 @@ class CuraApplication(QtApplication): group_node.setPosition(center) group_node.setCenterPosition(center) + # Remove nodes that are directly parented to another selected node from the selection so they remain parented + selected_nodes = Selection.getAllSelectedObjects().copy() + for node in selected_nodes: + if node.getParent() in selected_nodes and not node.getParent().callDecoration("isGroup"): + Selection.remove(node) + # Move selected nodes into the group-node Selection.applyOperation(SetParentOperation, group_node) @@ -1401,6 +1407,10 @@ class CuraApplication(QtApplication): group_parent = node.getParent() children = node.getChildren().copy() for child in children: + # Ungroup only 1 level deep + if child.getParent() != node: + continue + # Set the parent of the children to the parent of the group-node op.addOperation(SetParentOperation(child, group_parent)) diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 441d4c96c3..b9f37ec6f8 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -33,6 +33,7 @@ class MultiplyObjectsJob(Job): root = scene.getRoot() arranger = Arrange.create(scene_root=root) nodes = [] + for node in self._objects: # If object is part of a group, multiply group current_node = node @@ -49,18 +50,20 @@ class MultiplyObjectsJob(Job): for i in range(self._count): # We do place the nodes one by one, as we want to yield in between. if not node_too_big: - node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) + new_node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) if node_too_big or not solution_found: found_solution_for_all = False - new_location = node.getPosition() + new_location = new_node.getPosition() new_location = new_location.set(z = 100 - i * 20) - node.setPosition(new_location) + new_node.setPosition(new_location) # Same build plate build_plate_number = current_node.callDecoration("getBuildPlateNumber") - node.callDecoration("setBuildPlateNumber", build_plate_number) + new_node.callDecoration("setBuildPlateNumber", build_plate_number) + for child in new_node.getChildren(): + child.callDecoration("setBuildPlateNumber", build_plate_number) - nodes.append(node) + nodes.append(new_node) current_progress += 1 status_message.setProgress((current_progress / total_progress) * 100) Job.yieldThread() diff --git a/cura/Scene/BuildPlateDecorator.py b/cura/Scene/BuildPlateDecorator.py index c2fd3145dd..dfb465b7ad 100644 --- a/cura/Scene/BuildPlateDecorator.py +++ b/cura/Scene/BuildPlateDecorator.py @@ -15,7 +15,7 @@ class BuildPlateDecorator(SceneNodeDecorator): self._build_plate_number = nr if isinstance(self._node, CuraSceneNode): self._node.transformChanged() # trigger refresh node without introducing a new signal - if self._node and self._node.callDecoration("isGroup"): + if self._node: for child in self._node.getChildren(): child.callDecoration("setBuildPlateNumber", nr) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 3a1298bdba..6c2fb9a59d 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -16,7 +16,6 @@ from UM.Mesh.MeshBuilder import MeshBuilder from UM.Mesh.MeshReader import MeshReader from UM.Scene.GroupDecorator import GroupDecorator -from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator from cura.Settings.ExtruderManager import ExtruderManager from cura.Scene.CuraSceneNode import CuraSceneNode from cura.Scene.BuildPlateDecorator import BuildPlateDecorator @@ -81,7 +80,7 @@ class ThreeMFReader(MeshReader): active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate - um_node = CuraSceneNode() + um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation()) @@ -110,8 +109,6 @@ class ThreeMFReader(MeshReader): # Add the setting override decorator, so we can add settings to this node. if settings: - um_node.addDecorator(SettingOverrideDecorator()) - global_container_stack = Application.getInstance().getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. @@ -140,7 +137,7 @@ class ThreeMFReader(MeshReader): continue setting_container.setProperty(key, "value", setting_value) - if len(um_node.getChildren()) > 0: + if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py index 3332b4181d..7884ca30c7 100644 --- a/plugins/SupportEraser/SupportEraser.py +++ b/plugins/SupportEraser/SupportEraser.py @@ -19,10 +19,8 @@ from cura.Scene.CuraSceneNode import CuraSceneNode from cura.PickingPass import PickingPass -from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation -from cura.Operations.SetParentOperation import SetParentOperation from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator from cura.Scene.BuildPlateDecorator import BuildPlateDecorator @@ -105,15 +103,12 @@ class SupportEraser(Tool): mesh = MeshBuilder() mesh.addCube(10,10,10) node.setMeshData(mesh.build()) - node.setPosition(position) active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate - - node.addDecorator(SettingOverrideDecorator()) node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) - stack = node.callDecoration("getStack") # created by SettingOverrideDecorator + stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") @@ -122,13 +117,9 @@ class SupportEraser(Tool): new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) - root = self._controller.getScene().getRoot() - - op = GroupedOperation() - # First add the node to the scene, so it gets the expected transform - op.addOperation(AddSceneNodeOperation(node, root)) - op.addOperation(SetParentOperation(node, parent)) + op = AddSceneNodeOperation(node, parent) op.push() + node.setPosition(position, CuraSceneNode.TransformSpace.World) Application.getInstance().getController().getScene().sceneChanged.emit(node)