Beispiel #1
0
    def setX(self, x: str) -> None:
        """Set the x-location of the selected object(s) by translating relative to

        the selection bounding box center.
        :param x: Location in mm.
        """
        parsed_x = self._parseFloat(x)
        bounding_box = Selection.getBoundingBox()

        if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE):
            selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors()
            if len(selected_nodes) > 1:
                op = GroupedOperation()
                for selected_node in self._getSelectedObjectsWithoutSelectedAncestors():
                    world_position = selected_node.getWorldPosition()
                    new_position = world_position.set(x = parsed_x + (world_position.x - bounding_box.center.x))
                    node_op = TranslateOperation(selected_node, new_position, set_position = True)
                    op.addOperation(node_op)
                op.push()
            else:
                for selected_node in self._getSelectedObjectsWithoutSelectedAncestors():
                    world_position = selected_node.getWorldPosition()
                    new_position = world_position.set(x = parsed_x + (world_position.x - bounding_box.center.x))
                    TranslateOperation(selected_node, new_position, set_position = True).push()

        self._controller.toolOperationStopped.emit(self)
Beispiel #2
0
    def mergeWith(self, other: Operation) -> GroupedOperation:
        group = GroupedOperation()

        group.addOperation(other)
        group.addOperation(self)

        return group
    def multiplyObject(self, object_id, count):
        node = self.getController().getScene().findObject(object_id)

        if not node and object_id != 0:  #Workaround for tool handles overlapping the selected object
            node = Selection.getSelectedObject(0)

        if node:
            op = GroupedOperation()
            for i in range(count):
                if node.getParent() and node.getParent().callDecoration(
                        "isGroup"):
                    new_node = copy.deepcopy(
                        node.getParent())  #Copy the group node.
                    new_node.callDecoration("setConvexHull", None)

                    op.addOperation(
                        AddSceneNodeOperation(new_node,
                                              node.getParent().getParent()))
                else:
                    new_node = copy.deepcopy(node)
                    new_node.callDecoration("setConvexHull", None)
                    op.addOperation(
                        AddSceneNodeOperation(new_node, node.getParent()))

            op.push()
Beispiel #4
0
    def deleteSelection(self) -> None:
        """Delete all selected objects."""

        if not cura.CuraApplication.CuraApplication.getInstance(
        ).getController().getToolsEnabled():
            return

        removed_group_nodes = []  #type: List[SceneNode]
        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            op.addOperation(RemoveSceneNodeOperation(node))
            group_node = node.getParent()
            if group_node and group_node.callDecoration(
                    "isGroup") and group_node not in removed_group_nodes:
                remaining_nodes_in_group = list(
                    set(group_node.getChildren()) - set(nodes))
                if len(remaining_nodes_in_group) == 1:
                    removed_group_nodes.append(group_node)
                    op.addOperation(
                        SetParentOperation(remaining_nodes_in_group[0],
                                           group_node.getParent()))
                    op.addOperation(RemoveSceneNodeOperation(group_node))

            # Reset the print information
            cura.CuraApplication.CuraApplication.getInstance().getController(
            ).getScene().sceneChanged.emit(node)

        op.push()
Beispiel #5
0
    def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
        Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
        operation = GroupedOperation()

        root = cura.CuraApplication.CuraApplication.getInstance(
        ).getController().getScene().getRoot()

        nodes_to_change = []  # type: List[SceneNode]
        for node in Selection.getAllSelectedObjects():
            parent_node = node  # Find the parent node to change instead
            while parent_node.getParent() != root:
                parent_node = cast(SceneNode, parent_node.getParent())

            for single_node in BreadthFirstIterator(
                    parent_node
            ):  # type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                nodes_to_change.append(single_node)

        if not nodes_to_change:
            Logger.log("d", "Nothing to change.")
            return

        for node in nodes_to_change:
            operation.addOperation(
                SetBuildPlateNumberOperation(node, build_plate_nr))
        operation.push()

        Selection.clear()
Beispiel #6
0
    def centerSelection(self) -> None:
        operation = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            current_node = node
            while current_node.getParent() and current_node.getParent(
            ).callDecoration("isGroup"):
                current_node = current_node.getParent()

            vector = current_node._position
            print_mode_enabled = Application.getInstance(
            ).getGlobalContainerStack().getProperty("print_mode", "enabled")
            if print_mode_enabled:
                print_mode = Application.getInstance().getGlobalContainerStack(
                ).getProperty("print_mode", "value")
                if print_mode != "regular":
                    machine_width = Application.getInstance(
                    ).getGlobalContainerStack().getProperty(
                        "machine_width", "value")
                    center = -machine_width / 4
                    if print_mode == "mirror":
                        machine_head_with_fans_polygon = Application.getInstance(
                        ).getGlobalContainerStack().getProperty(
                            "machine_head_with_fans_polygon", "value")
                        machine_head_size = math.fabs(
                            machine_head_with_fans_polygon[0][0] -
                            machine_head_with_fans_polygon[2][0])
                        center -= machine_head_size / 4
                    vector = Vector(current_node._position.x - center,
                                    current_node._position.y,
                                    current_node._position.z)

            center_operation = TranslateOperation(current_node, -vector)
            operation.addOperation(center_operation)
            # node.setPosition(vector)
        operation.push()
Beispiel #7
0
    def mergeWith(self, other):
        group = GroupedOperation()

        group.addOperation(self)
        group.addOperation(other)

        return group
Beispiel #8
0
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)  -> Tuple[GroupedOperation, int]:
    scene_root = Application.getInstance().getController().getScene().getRoot()
    found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor)

    not_fit_count = 0
    grouped_operation = GroupedOperation()
    for node, node_item in zip(nodes_to_arrange, node_items):
        if add_new_nodes_in_scene:
            grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root))

        if node_item.binId() == 0:
            # We found a spot for it
            rotation_matrix = Matrix()
            rotation_matrix.setByRotationAxis(node_item.rotation(), Vector(0, -1, 0))
            grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix)))
            grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0,
                                                                           node_item.translation().y() / factor)))
        else:
            # We didn't find a spot
            grouped_operation.addOperation(
                TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), set_position = True))
            not_fit_count += 1

    return grouped_operation, not_fit_count
    def _replaceSceneNode(self, existing_node, trimeshes):
        name = existing_node.getName()
        file_name = existing_node.getMeshData().getFileName()
        transformation = existing_node.getWorldTransformation()
        parent = existing_node.getParent()
        extruder_id = existing_node.callDecoration("getActiveExtruder")
        build_plate = existing_node.callDecoration("getBuildPlateNumber")
        selected = Selection.isSelected(existing_node)

        op = GroupedOperation()
        op.addOperation(RemoveSceneNodeOperation(existing_node))

        for i, tri_node in enumerate(trimeshes):
            mesh_data = self._toMeshData(tri_node)

            new_node = CuraSceneNode()
            new_node.setSelectable(True)
            new_node.setMeshData(mesh_data)
            new_node.setName(name if i == 0 else "%s %d" % (name, i))
            new_node.callDecoration("setActiveExtruder", extruder_id)
            new_node.addDecorator(BuildPlateDecorator(build_plate))
            new_node.addDecorator(SliceableObjectDecorator())

            op.addOperation(AddSceneNodeOperation(new_node, parent))
            op.addOperation(
                SetTransformMatrixOperation(new_node, transformation))

            if selected:
                Selection.add(new_node)
        op.push()
Beispiel #10
0
    def run(self):
        status_message = Message(i18n_catalog.i18nc(
            "@info:status", "Multiplying and placing objects"),
                                 lifetime=0,
                                 dismissable=False,
                                 progress=0)
        status_message.show()
        scene = Application.getInstance().getController().getScene()
        node = scene.findObject(self._object_id)

        if not node and self._object_id != 0:  # Workaround for tool handles overlapping the selected object
            node = Selection.getSelectedObject(0)

        # If object is part of a group, multiply group
        current_node = node
        while current_node.getParent() and current_node.getParent(
        ).callDecoration("isGroup"):
            current_node = current_node.getParent()

        root = scene.getRoot()
        arranger = Arrange.create(scene_root=root)
        node_too_big = False
        if node.getBoundingBox().width < 300 or node.getBoundingBox(
        ).depth < 300:
            offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(
                current_node, min_offset=self._min_offset)
        else:
            node_too_big = True
        nodes = []
        found_solution_for_all = True
        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)
            if node_too_big or not solution_found:
                found_solution_for_all = False
                new_location = node.getPosition()
                new_location = new_location.set(z=100 - i * 20)
                node.setPosition(new_location)

            nodes.append(node)
            Job.yieldThread()
            status_message.setProgress((i + 1) / self._count * 100)

        if nodes:
            op = GroupedOperation()
            for new_node in nodes:
                op.addOperation(
                    AddSceneNodeOperation(new_node, current_node.getParent()))
            op.push()
        status_message.hide()

        if not found_solution_for_all:
            no_full_solution_message = Message(
                i18n_catalog.i18nc(
                    "@info:status",
                    "Unable to find a location within the build volume for all objects"
                ))
            no_full_solution_message.show()
Beispiel #11
0
    def _onSelectedFaceChanged(self):
        self._handle.setEnabled(not Selection.getFaceSelectMode())

        selected_face = Selection.getSelectedFace()
        if not Selection.getSelectedFace() or not (Selection.hasSelection() and Selection.getFaceSelectMode()):
            return

        original_node, face_id = selected_face
        meshdata = original_node.getMeshDataTransformed()
        if not meshdata or face_id < 0:
            return

        rotation_point, face_normal = meshdata.getFacePlane(face_id)
        rotation_point_vector = Vector(rotation_point[0], rotation_point[1], rotation_point[2])
        face_normal_vector = Vector(face_normal[0], face_normal[1], face_normal[2])
        rotation_quaternion = Quaternion.rotationTo(face_normal_vector.normalized(), Vector(0.0, -1.0, 0.0))

        operation = GroupedOperation()
        current_node = None  # type: Optional[SceneNode]
        for node in Selection.getAllSelectedObjects():
            current_node = node
            parent_node = current_node.getParent()
            while parent_node and parent_node.callDecoration("isGroup"):
                current_node = parent_node
                parent_node = current_node.getParent()
        if current_node is None:
            return

        rotate_operation = RotateOperation(current_node, rotation_quaternion, rotation_point_vector)
        operation.addOperation(rotate_operation)
        operation.push()
Beispiel #12
0
    def resetAllTranslation(self):
        Logger.log("i", "Resetting all scene translations")
        nodes = []
        for node in DepthFirstIterator(
                self.getController().getScene().getRoot()):
            if type(node) is not SceneNode:
                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"):
                continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)

            nodes.append(node)

        if nodes:
            op = GroupedOperation()
            for node in nodes:
                node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
                op.addOperation(
                    SetTransformOperation(
                        node,
                        Vector(
                            0,
                            node.getWorldPosition().y -
                            node.getBoundingBox().bottom, 0)))
            op.push()
Beispiel #13
0
    def applyOperation(cls, operation, *args, **kwargs):
        """Apply an operation to the entire selection

        This will create and push an operation onto the operation stack. Dependent
        on whether there is one item selected or multiple it will be just the
        operation or a grouped operation containing the operation for each selected
        node.

        :param operation: :type{Class} The operation to create and push. It should take a SceneNode as first positional parameter.
        :param args: The additional positional arguments passed along to the operation constructor.
        :param kwargs: The additional keyword arguments that will be passed along to the operation constructor.

        :return: list of instantiated operations
        """

        if not cls.__selection:
            return

        operations = []

        if len(cls.__selection) == 1:
            node = cls.__selection[0]
            op = operation(node, *args, **kwargs)
            operations.append(op)
        else:
            op = GroupedOperation()

            for node in Selection.getAllSelectedObjects():
                sub_op = operation(node, *args, **kwargs)
                op.addOperation(sub_op)
                operations.append(sub_op)

        op.push()
        return operations
Beispiel #14
0
    def multiplySelection(self, count: int) -> None:
        application = cura.CuraApplication.CuraApplication.getInstance()
        global_container_stack = application.getGlobalContainerStack()

        if not global_container_stack:
            return

        definition_container = global_container_stack.getBottom()
        if definition_container and definition_container.getId() not in [
                "blackbelt", "blackbeltvd"
        ]:
            # for all other printers do the normal multiply/arrange
            super().multiplySelection(count)
            return

        scene_root = application.getController().getScene().getRoot()

        current_nodes = []
        for node in DepthFirstIterator(
                scene_root
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if node.callDecoration("isSliceable") or node.callDecoration(
                    "isGroup"):
                current_nodes.append(node)

        new_nodes = []  # type: List[CuraSceneNode]
        processed_nodes = []  # type: List[CuraSceneNode]

        active_build_plate = application.getMultiBuildPlateModel(
        ).activeBuildPlate

        for node in Selection.getAllSelectedObjects()[:]:
            # If object is part of a group, multiply group
            while node.getParent() and (
                    node.getParent().callDecoration("isGroup")
                    or node.getParent().callDecoration("isSliceable")):
                node = node.getParent()

            if node in processed_nodes:
                continue
            processed_nodes.append(node)

            for i in range(count):
                new_node = copy.deepcopy(node)

                new_node.callDecoration("setBuildPlateNumber",
                                        active_build_plate)
                for child in new_node.getChildren():
                    child.callDecoration("setBuildPlateNumber",
                                         active_build_plate)

                new_nodes.append(new_node)

        if new_nodes:
            op = GroupedOperation()
            for new_node in new_nodes:
                op.addOperation(AddSceneNodeOperation(new_node, scene_root))
            op.push()

        application.arrange(new_nodes, current_nodes)
Beispiel #15
0
    def setExtruderForSelection(self, extruder_id: str) -> None:
        operation = GroupedOperation()

        nodes_to_change = []
        for node in Selection.getAllSelectedObjects():
            # Do not change any nodes that already have the right extruder set.
            if node.callDecoration("getActiveExtruder") == extruder_id:
                continue

            # If the node is a group, apply the active extruder to all children of the group.
            if node.callDecoration("isGroup"):
                for grouped_node in BreadthFirstIterator(node):
                    if grouped_node.callDecoration(
                            "getActiveExtruder") == extruder_id:
                        continue

                    if grouped_node.callDecoration("isGroup"):
                        continue

                    nodes_to_change.append(grouped_node)
                continue

            nodes_to_change.append(node)

        if not nodes_to_change:
            # If there are no changes to make, we still need to reset the selected extruders.
            # This is a workaround for checked menu items being deselected while still being
            # selected.
            ExtruderManager.getInstance().resetSelectedObjectExtruders()
            return

        for node in nodes_to_change:
            operation.addOperation(
                SetObjectExtruderOperation(node, extruder_id))
        operation.push()
    def resetAll(self):
        Logger.log("i", "Resetting all scene transformations")
        nodes = []
        for node in DepthFirstIterator(
                self.getController().getScene().getRoot()):
            if type(node) is not SceneNode:
                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"):
                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
            nodes.append(node)

        if nodes:
            op = GroupedOperation()
            for node in nodes:
                # Ensure that the object is above the build platform
                node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
                if node.getBoundingBox():
                    center_y = node.getWorldPosition().y - node.getBoundingBox(
                    ).bottom
                else:
                    center_y = 0
                op.addOperation(
                    SetTransformOperation(node, Vector(0, center_y, 0),
                                          Quaternion(), Vector(1, 1, 1)))
            op.push()
Beispiel #17
0
    def setBuildPlateForSelection(self, build_plate_nr: int) -> None:
        Logger.log("d", "Setting build plate number... %d" % build_plate_nr)
        operation = GroupedOperation()

        root = Application.getInstance().getController().getScene().getRoot()

        nodes_to_change = []
        for node in Selection.getAllSelectedObjects():
            parent_node = node  # Find the parent node to change instead
            while parent_node.getParent() != root:
                parent_node = parent_node.getParent()

            for single_node in BreadthFirstIterator(parent_node):
                nodes_to_change.append(single_node)

        if not nodes_to_change:
            Logger.log("d", "Nothing to change.")
            return

        for node in nodes_to_change:
            operation.addOperation(
                SetBuildPlateNumberOperation(node, build_plate_nr))
        operation.push()

        Selection.clear()
Beispiel #18
0
    def setZ(self, z: str) -> None:
        """Set the y-location of the selected object(s) by translating relative to

        the selection bounding box bottom.
        :param z: Location in mm.
        """
        parsed_z = self._parseFloat(z)
        bounding_box = Selection.getBoundingBox()

        if not Float.fuzzyCompare(parsed_z, float(bounding_box.bottom),
                                  DIMENSION_TOLERANCE):
            selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors()
            if len(selected_nodes) > 1:
                op = GroupedOperation()
                for selected_node in selected_nodes:
                    # Note: The switching of z & y is intentional. We display z as up for the user,
                    # But store the data in openGL space.
                    world_position = selected_node.getWorldPosition()
                    new_position = world_position.set(
                        y=parsed_z + (world_position.y - bounding_box.bottom))
                    node_op = TranslateOperation(selected_node,
                                                 new_position,
                                                 set_position=True)
                    op.addOperation(node_op)
                op.push()
            else:
                for selected_node in selected_nodes:
                    world_position = selected_node.getWorldPosition()
                    new_position = world_position.set(
                        y=parsed_z + (world_position.y - bounding_box.bottom))
                    TranslateOperation(selected_node,
                                       new_position,
                                       set_position=True).push()
        self._controller.toolOperationStopped.emit(self)
Beispiel #19
0
    def deleteAllNodesWithMeshData(self, only_selectable:bool = True) -> None:
        Logger.log("i", "Clearing scene")
        if not self.getToolsEnabled():
            return

        nodes = []
        for node in DepthFirstIterator(self.getScene().getRoot()):
            if not isinstance(node, SceneNode):
                continue
            if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration(
                    "isGroup"):
                continue  # Node that doesnt have a mesh and is not a group.
            if only_selectable and not node.isSelectable():
                continue
            if not node.callDecoration("isSliceable") and not node.callDecoration(
                    "getLayerData") and not node.callDecoration("isGroup"):
                continue  # Only remove nodes that are selectable.
            if node.getParent() and cast(SceneNode, node.getParent()).callDecoration("isGroup"):
                continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)
            nodes.append(node)
        if nodes:
            from UM.Operations.GroupedOperation import GroupedOperation
            op = GroupedOperation()

            for node in nodes:
                from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
                op.addOperation(RemoveSceneNodeOperation(node))

                # Reset the print information
                self.getScene().sceneChanged.emit(node)

            op.push()
            from UM.Scene.Selection import Selection
            Selection.clear()
Beispiel #20
0
    def deleteSelection(self) -> None:
        if not Application.getInstance().getController().getToolsEnabled():
            return

        removed_group_nodes = []
        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            print_mode_enabled = Application.getInstance(
            ).getGlobalContainerStack().getProperty("print_mode", "enabled")
            if print_mode_enabled:
                node_dup = PrintModeManager.getInstance().getDuplicatedNode(
                    node)
                op.addOperation(RemoveNodesOperation(node_dup))
            else:
                op.addOperation(RemoveSceneNodeOperation(node))
            group_node = node.getParent()
            if group_node and group_node.callDecoration(
                    "isGroup") and group_node not in removed_group_nodes:
                remaining_nodes_in_group = list(
                    set(group_node.getChildren()) - set(nodes))
                if len(remaining_nodes_in_group) == 1:
                    removed_group_nodes.append(group_node)
                    op.addOperation(
                        SetParentOperation(remaining_nodes_in_group[0],
                                           group_node.getParent()))
                    op.addOperation(RemoveSceneNodeOperation(group_node))

            # Reset the print information
            Application.getInstance().getController().getScene(
            ).sceneChanged.emit(node)

        op.push()
Beispiel #21
0
    def deleteAll(self, only_selectable = True) -> None:
        Logger.log("i", "Clearing scene")
        if not self.getController().getToolsEnabled():
            return

        nodes = []
        for node in DepthFirstIterator(self.getController().getScene().getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if not isinstance(node, SceneNode):
                continue
            if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
                continue  # Node that doesnt have a mesh and is not a group.
            if only_selectable and not node.isSelectable():
                continue
            if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
                continue  # Only remove nodes that are selectable.
            if node.getParent() and cast(SceneNode, node.getParent()).callDecoration("isGroup"):
                continue  # Grouped nodes don't need resetting as their parent (the group) is resetted)
            nodes.append(node)
        if nodes:
            op = GroupedOperation()

            for node in nodes:
                op.addOperation(RemoveSceneNodeOperation(node))

                # Reset the print information
                self.getController().getScene().sceneChanged.emit(node)

            op.push()
            Selection.clear()
Beispiel #22
0
    def event(self, event):
        super().event(event)

        if event.type == Event.MousePressEvent and self._controller.getToolsEnabled():
            # Initialise a mirror operation
            if MouseEvent.LeftButton not in event.buttons:
                return False

            id = self._selection_pass.getIdAtPosition(event.x, event.y)
            if not id:
                return False

            if ToolHandle.isAxis(id):
                self.setLockedAxis(id)
                self._operation_started = True
                self.operationStarted.emit(self)
                return True

        if event.type == Event.MouseReleaseEvent:
            if self._operation_started:
                self._operation_started = False
                self.operationStopped.emit(self)

            # Perform a mirror operation
            if self.getLockedAxis():
                op = None
                if Selection.getCount() == 1:
                    node = Selection.getSelectedObject(0)
                    if self.getLockedAxis() == ToolHandle.XAxis:
                        mirror = Vector(-1, 1, 1)
                    elif self.getLockedAxis() == ToolHandle.YAxis:
                        mirror = Vector(1, -1, 1)
                    elif self.getLockedAxis() == ToolHandle.ZAxis:
                        mirror = Vector(1, 1, -1)
                    else:
                        mirror = Vector(1, 1, 1)
                    op = MirrorOperation(node, mirror, mirror_around_center = True)
                else:
                    op = GroupedOperation()

                    for node in Selection.getAllSelectedObjects():
                        if self.getLockedAxis() == ToolHandle.XAxis:
                            mirror = Vector(-1, 1, 1)
                        elif self.getLockedAxis() == ToolHandle.YAxis:
                            mirror = Vector(1, -1, 1)
                        elif self.getLockedAxis() == ToolHandle.ZAxis:
                            mirror = Vector(1, 1, -1)
                        else:
                            mirror = Vector(1, 1, 1)

                        op.addOperation(MirrorOperation(node, mirror, mirror_around_center = True))

                op.push()

                self.setLockedAxis(None)
                return True

        return False
Beispiel #23
0
    def run(self):
        status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0)
        status_message.show()
        arranger = Arrange.create(fixed_nodes = self._fixed_nodes)

        # Collect nodes to be placed
        nodes_arr = []  # fill with (size, node, offset_shape_arr, hull_shape_arr)
        for node in self._nodes:
            offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
            nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))

        # Sort the nodes with the biggest area first.
        nodes_arr.sort(key=lambda item: item[0])
        nodes_arr.reverse()

        # Place nodes one at a time
        start_priority = 0
        last_priority = start_priority
        last_size = None
        grouped_operation = GroupedOperation()
        found_solution_for_all = True
        for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr):
            # For performance reasons, we assume that when a location does not fit,
            # it will also not fit for the next object (while what can be untrue).
            # We also skip possibilities by slicing through the possibilities (step = 10)
            if last_size == size:  # This optimization works if many of the objects have the same size
                start_priority = last_priority
            else:
                start_priority = 0
            best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=1)
            x, y = best_spot.x, best_spot.y
            node.removeDecorator(ZOffsetDecorator)
            if node.getBoundingBox():
                center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
            else:
                center_y = 0
            if x is not None:  # We could find a place
                last_size = size
                last_priority = best_spot.priority

                arranger.place(x, y, hull_shape_arr)  # take place before the next one

                grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
            else:
                Logger.log("d", "Arrange all: could not find spot!")
                found_solution_for_all = False
                grouped_operation.addOperation(TranslateOperation(node, Vector(200, center_y, - idx * 20), set_position = True))

            status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
            Job.yieldThread()

        grouped_operation.push()

        status_message.hide()

        if not found_solution_for_all:
            no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"))
            no_full_solution_message.show()
Beispiel #24
0
    def _replaceSceneNode(self, existing_node, trimeshes) -> None:
        name = existing_node.getName()
        file_name = existing_node.getMeshData().getFileName()
        transformation = existing_node.getWorldTransformation()
        parent = existing_node.getParent()
        extruder_id = existing_node.callDecoration("getActiveExtruder")
        build_plate = existing_node.callDecoration("getBuildPlateNumber")
        selected = Selection.isSelected(existing_node)

        children = existing_node.getChildren()
        new_nodes = []

        op = GroupedOperation()
        op.addOperation(RemoveSceneNodeOperation(existing_node))

        for i, tri_node in enumerate(trimeshes):
            mesh_data = self._toMeshData(tri_node, file_name)

            new_node = CuraSceneNode()
            new_node.setSelectable(True)
            new_node.setMeshData(mesh_data)
            new_node.setName(name if i==0 else "%s %d" % (name, i))
            new_node.callDecoration("setActiveExtruder", extruder_id)
            new_node.addDecorator(BuildPlateDecorator(build_plate))
            new_node.addDecorator(SliceableObjectDecorator())

            op.addOperation(AddSceneNodeOperation(new_node, parent))
            op.addOperation(SetTransformMatrixOperation(new_node, transformation))

            new_nodes.append(new_node)

            if selected:
                Selection.add(new_node)

        for child in children:
            mesh_data = child.getMeshData()
            if not mesh_data:
                continue
            child_bounding_box = mesh_data.getTransformed(child.getWorldTransformation()).getExtents()
            if not child_bounding_box:
                continue
            new_parent = None
            for potential_parent in new_nodes:
                parent_mesh_data = potential_parent.getMeshData()
                if not parent_mesh_data:
                    continue
                parent_bounding_box = parent_mesh_data.getTransformed(potential_parent.getWorldTransformation()).getExtents()
                if not parent_bounding_box:
                    continue
                intersection = child_bounding_box.intersectsBox(parent_bounding_box)
                if intersection != AxisAlignedBox.IntersectionResult.NoIntersection:
                    new_parent = potential_parent
                    break
            if not new_parent:
                new_parent = new_nodes[0]
            op.addOperation(SetParentOperationSimplified(child, new_parent))

        op.push()
Beispiel #25
0
    def removeSelection(self):
        if not Selection.hasSelection():
            return

        op = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            op.addOperation(RemoveSceneNodeOperation(node))
        op.push()
        Selection.clear()
Beispiel #26
0
 def _scaleSelectedNodes(self, scale_vector: Vector) -> None:
     selected_nodes = self._getSelectedObjectsWithoutSelectedAncestors()
     if len(selected_nodes) > 1:
         op = GroupedOperation()
         for node in selected_nodes:
             op.addOperation(ScaleOperation(node, scale_vector, scale_around_point=node.getWorldPosition()))
         op.push()
     else:
         for node in selected_nodes:
             ScaleOperation(node, scale_vector, scale_around_point=node.getWorldPosition()).push()
Beispiel #27
0
    def centerSelection(self) -> None:
        operation = GroupedOperation()
        for node in Selection.getAllSelectedObjects():
            current_node = node
            while current_node.getParent() and current_node.getParent(
            ).callDecoration("isGroup"):
                current_node = current_node.getParent()

            center_operation = SetTransformOperation(current_node, Vector())
            operation.addOperation(center_operation)
        operation.push()
Beispiel #28
0
    def deleteSelection(self):
        if not self.getController().getToolsEnabled():
            return

        op = GroupedOperation()
        nodes = Selection.getAllSelectedObjects()
        for node in nodes:
            op.addOperation(RemoveSceneNodeOperation(node))

        op.push()

        pass
Beispiel #29
0
    def setX(self, x):
        parsed_x = self._parseInt(x)
        bounding_box = Selection.getBoundingBox()

        op = GroupedOperation()
        if not Float.fuzzyCompare(parsed_x, float(bounding_box.center.x), DIMENSION_TOLERANCE):
            for selected_node in Selection.getAllSelectedObjects():
                world_position = selected_node.getWorldPosition()
                new_position = world_position.set(x=parsed_x + (world_position.x - bounding_box.center.x))
                node_op = TranslateOperation(selected_node, new_position, set_position = True)
                op.addOperation(node_op)
            op.push()
        self._controller.toolOperationStopped.emit(self)
Beispiel #30
0
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
    """
    scene_root = Application.getInstance().getController().getScene().getRoot()
    found_solution_for_all, node_items = findNodePlacement(
        nodes_to_arrange, build_volume, fixed_nodes, factor)

    not_fit_count = 0
    grouped_operation = GroupedOperation()
    for node, node_item in zip(nodes_to_arrange, node_items):
        if add_new_nodes_in_scene:
            grouped_operation.addOperation(
                AddSceneNodeOperation(node, scene_root))

        if node_item.binId() == 0:
            # We found a spot for it
            rotation_matrix = Matrix()
            rotation_matrix.setByRotationAxis(node_item.rotation(),
                                              Vector(0, -1, 0))
            grouped_operation.addOperation(
                RotateOperation(node, Quaternion.fromMatrix(rotation_matrix)))
            grouped_operation.addOperation(
                TranslateOperation(
                    node,
                    Vector(node_item.translation().x() / factor, 0,
                           node_item.translation().y() / factor)))
        else:
            # We didn't find a spot
            grouped_operation.addOperation(
                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