def _setUpFilePathForBlender(self): """Checks if the selection of objects is correct and allowed and calls the function to build the command. """ # Checks if path to this plugin and path to blender are correct. CuraBlender.verifyBlenderPath(manual=False) # Only continues if correct path to blender is set. if verified_blender_path and not self._checkGrouped(): # If no object is selected, check if the objects belong to more than one file. if len(Selection.getAllSelectedObjects()) == 0: open_files = set() for node in DepthFirstIterator(Application.getInstance( ).getController().getScene().getRoot()): if isinstance(node, CuraSceneNode) and node.getMeshData( ).getFileName(): if '_curasplit_' in node.getMeshData().getFileName(): file_path = node.getMeshData().getFileName() file_path = '{}.blend'.format( file_path[:file_path.index('_curasplit_')]) else: file_path = node.getMeshData().getFileName() open_files.add(file_path) # Opens the objects in blender, if they belong to only one file. if len(open_files) == 1: self._buildCommandForBlender(open_files.pop()) else: message = Message( text=catalog.i18nc('@info', 'Select Object first.'), title=catalog.i18nc( '@info:title', 'Please select the object you want to open.')) message.show() # If one object is selected, opens it's file reference (file name). elif len(Selection.getAllSelectedObjects()) == 1: for selection in Selection.getAllSelectedObjects(): file_path = selection.getMeshData().getFileName() if '_curasplit_' in file_path: file_path = '{}.blend'.format( file_path[:file_path.index('_curasplit_')]) self._buildCommandForBlender(file_path) # If multiple objects are selected, checks if they belong to more than one file. else: files = set() for selection in Selection.getAllSelectedObjects(): file_path = selection.getMeshData().getFileName() if '_curasplit_' in file_path: file_path = '{}.blend'.format( file_path[:file_path.index('_curasplit_')]) files.add(file_path) # Opens the objects in blender, if they belong to only one file. if len(files) == 1: self._buildCommandForBlender(file_path) else: message = Message( text=catalog.i18nc('@info', 'Please rethink your selection.'), title=catalog.i18nc( '@info:title', 'Select only objects from same file')) message.show()
def setSelected(self, key): for index in range(0,len(self.items)): if self.items[index]["key"] == key: for node in Application.getInstance().getController().getScene().getRoot().getAllChildren(): if id(node) == key: if node not in Selection.getAllSelectedObjects(): #node already selected Selection.add(node) if self.items[index]["depth"] == 1: #Its a group node for child_node in node.getChildren(): if child_node not in Selection.getAllSelectedObjects(): #Set all children to parent state (if they arent already) Selection.add(child_node) else: Selection.remove(node) if self.items[index]["depth"] == 1: #Its a group for child_node in node.getChildren(): if child_node in Selection.getAllSelectedObjects(): Selection.remove(child_node) all_children_selected = True #Check all group nodes to see if all their children are selected (if so, they also need to be selected!) for index in range(0,len(self.items)): if self.items[index]["depth"] == 1: for node in Application.getInstance().getController().getScene().getRoot().getAllChildren(): if node.hasChildren(): if id(node) == self.items[index]["key"] and id(node) != key: for index, child_node in enumerate(node.getChildren()): if not Selection.isSelected(child_node): all_children_selected = False #At least one of its children is not selected, dont change state break if all_children_selected: Selection.add(node) else: Selection.remove(node) #Force update self.updateList(Application.getInstance().getController().getScene().getRoot())
def test_addRemoveSelection(self): node_1 = SceneNode() Selection.add(node_1) assert Selection.getAllSelectedObjects() == [node_1] Selection.remove(node_1) assert Selection.getAllSelectedObjects() == []
def test_clearSelection(self): node_1 = SceneNode() node_2 = SceneNode() Selection.add(node_1) Selection.add(node_2) # Ensure that the objects we want selected are selected assert Selection.getAllSelectedObjects() == [node_1, node_2] Selection.clear() assert Selection.getAllSelectedObjects() == []
def test_addRemoveSelection(self): node_1 = SceneNode() Selection.add(node_1) Selection.setFace(node_1, 99) assert Selection.getAllSelectedObjects() == [node_1] Selection.remove(node_1) assert Selection.getAllSelectedObjects() == [] assert Selection.getSelectedFace() is None
def multiplySelection(self, count: int) -> None: 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()
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()
def _calculateMesh(self): scene = Application.getInstance().getController().getScene() nodes = Selection.getAllSelectedObjects() if len(nodes) > 0: sn = nodes[0] #self._handle._connector._proxy._activeExtruderStack = nodes[0].callDecoration("getExtruderStack") if self._scene_node_name is None or sn.getName() != self._scene_node_name: mesh_data = sn.getMeshData() if mesh_data: Logger.log('d', 'Compute interactive mesh from SceneNode {}'.format(sn.getName())) self._scene_node_name = sn.getName() self._interactive_mesh = makeInteractiveMesh(mesh_data) self._load_face = None self._anchor_face = None self._handle.clearSelection() self._handle._connector._proxy._anchorsApplied = 0 self._handle._connector._proxy._loadsApplied = 0 self.extension.cloud._onApplicationActivityChanged() controller = Application.getInstance().getController() camTool = controller.getCameraTool() aabb = sn.getBoundingBox() if aabb: camTool.setOrigin(aabb.center)
def _checkGrouped(self): """Checks if the selected objects are grouped. :return: The boolean value if objects are grouped. """ is_grouped = False for selection in Selection.getAllSelectedObjects(): if selection.callDecoration("isGroup"): is_grouped = True message = Message(text=catalog.i18nc( '@info', 'Ungroup selected group?'), title=catalog.i18nc( '@info:title', 'Only nodes without group are allowed.')) message.addAction( 'Ungroup', catalog.i18nc('@action:button', 'Ungroup'), '[no_icon]', '[no_description]', button_align=Message.ActionButtonAlignment.ALIGN_LEFT) message.addAction( 'Ignore', catalog.i18nc('@action:button', 'Ignore'), '[no_icon]', '[no_description]', button_style=Message.ActionButtonStyle.SECONDARY, button_align=Message.ActionButtonAlignment.ALIGN_RIGHT) message.actionTriggered.connect(self._ungroupTrigger) message.show() return is_grouped
def resetRotation(self): for node in Selection.getAllSelectedObjects(): node.setMirror(Vector(1, 1, 1)) Selection.applyOperation(SetTransformOperation, None, Quaternion(), None)
def selectedObjectExtruders(self) -> List[Union[str, "ExtruderStack"]]: if not self._selected_object_extruders: object_extruders = set() # First, build a list of the actual selected objects (including children of groups, excluding group nodes) selected_nodes = [] # type: List["SceneNode"] for node in Selection.getAllSelectedObjects(): if node.callDecoration("isGroup"): for grouped_node in BreadthFirstIterator(node): if grouped_node.callDecoration("isGroup"): continue selected_nodes.append(grouped_node) else: selected_nodes.append(node) # Then, figure out which nodes are used by those selected nodes. current_extruder_trains = self.getActiveExtruderStacks() for node in selected_nodes: extruder = node.callDecoration("getActiveExtruder") if extruder: object_extruders.add(extruder) elif current_extruder_trains: object_extruders.add(current_extruder_trains[0].getId()) self._selected_object_extruders = list(object_extruders) return self._selected_object_extruders
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)
def selectedObjectExtruders(self) -> List[str]: if not self._selected_object_extruders: object_extruders = set() # First, build a list of the actual selected objects (including children of groups, excluding group nodes) selected_nodes = [] for node in Selection.getAllSelectedObjects(): if node.callDecoration("isGroup"): for grouped_node in BreadthFirstIterator(node): if grouped_node.callDecoration("isGroup"): continue selected_nodes.append(grouped_node) else: selected_nodes.append(node) # Then, figure out which nodes are used by those selected nodes. global_stack = Application.getInstance().getGlobalContainerStack() current_extruder_trains = self._extruder_trains.get(global_stack.getId()) for node in selected_nodes: extruder = node.callDecoration("getActiveExtruder") if extruder: object_extruders.add(extruder) elif current_extruder_trains: object_extruders.add(current_extruder_trains["0"].getId()) self._selected_object_extruders = list(object_extruders) return self._selected_object_extruders
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 = Vector() 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(center, 0, 0) center_operation = SetTransformOperation(current_node, vector) operation.addOperation(center_operation) operation.push()
def ungroupSelected(self): ungrouped_nodes = [] selected_objects = Selection.getAllSelectedObjects( )[:] #clone the list for node in selected_objects: if node.callDecoration("isGroup"): children_to_move = [] for child in node.getChildren(): if type(child) is SceneNode: children_to_move.append(child) for child in children_to_move: child.setParent(node.getParent()) print(node.getPosition()) child.translate(node.getPosition()) child.setPosition(child.getPosition().scale( node.getScale())) child.scale(node.getScale()) child.rotate(node.getOrientation()) Selection.add(child) child.callDecoration("setConvexHull", None) node.setParent(None) ungrouped_nodes.append(node) for node in ungrouped_nodes: Selection.remove(node)
def selectedObjectExtruders(self) -> List[Union[str, "ExtruderStack"]]: if not self._selected_object_extruders: object_extruders = set() # First, build a list of the actual selected objects (including children of groups, excluding group nodes) selected_nodes = [] # type: List["SceneNode"] for node in Selection.getAllSelectedObjects(): if node.callDecoration("isGroup"): for grouped_node in BreadthFirstIterator(node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if grouped_node.callDecoration("isGroup"): continue selected_nodes.append(grouped_node) else: selected_nodes.append(node) # Then, figure out which nodes are used by those selected nodes. current_extruder_trains = self.getActiveExtruderStacks() for node in selected_nodes: extruder = node.callDecoration("getActiveExtruder") if extruder: object_extruders.add(extruder) elif current_extruder_trains: object_extruders.add(current_extruder_trains[0].getId()) self._selected_object_extruders = list(object_extruders) # type: List[Union[str, "ExtruderStack"]] return self._selected_object_extruders
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 layFlat(self): self.operationStarted.emit(self) self._progress_message = Message("Laying object flat on buildplate...", lifetime=0, dismissable=False) self._progress_message.setProgress(0) self._iterations = 0 self._total_iterations = 0 for selected_object in Selection.getAllSelectedObjects(): if selected_object.callDecoration( "isGroup"): # 2.1 hack. TODO: fix this properly self.operationStopped.emit(self) Logger.log("w", "Layflat is not supported for grouped objects") return self._total_iterations += len( selected_object.getMeshDataTransformed().getVertices()) * 2 self._progress_message.show() operations = Selection.applyOperation(LayFlatOperation) for op in operations: op.progress.connect(self._layFlatProgress) job = LayFlatJob(operations) job.finished.connect(self._layFlatFinished) job.start()
def setExtruderForSelection(self, extruder_id: str) -> None: operation = GroupedOperation() nodes_to_change = [] for node in Selection.getAllSelectedObjects(): # 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): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if grouped_node.callDecoration("getActiveExtruder") == extruder_id: continue if grouped_node.callDecoration("isGroup"): continue nodes_to_change.append(grouped_node) continue # Do not change any nodes that already have the right extruder set. if node.callDecoration("getActiveExtruder") == extruder_id: 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 setZ(self, Z): if float(Z) != self._Z_angle: self._angle = ((float(Z) % 360) - (self._Z_angle % 360)) % 360 self._Z_angle = float(Z) #rotation = Quaternion.fromAngleAxis(math.radians( self._angle ), Vector.Unit_Z) rotation = Quaternion() rotation.setByAngleAxis(math.radians(self._angle), Vector.Unit_Z) # Save the current positions of the node, as we want to rotate around their current centres self._saved_node_positions = [] for node in Selection.getAllSelectedObjects(): self._saved_node_positions.append((node, node.getPosition())) node._rotationZ = self._Z_angle # Rate-limit the angle change notification # This is done to prevent the UI from being flooded with property change notifications, # which in turn would trigger constant repaints. new_time = time.monotonic() if not self._angle_update_time or new_time - self._angle_update_time > 0.1: self._angle_update_time = new_time # Rotate around the saved centeres of all selected nodes op = GroupedOperation() for node, position in self._saved_node_positions: op.addOperation( RotateOperation(node, rotation, rotate_around_point=position)) op.push() self._angle = 0 self.propertyChanged.emit()
def doAutoOrientation(self, extended_mode): # If we still had a message open from last time, hide it. if self._message: self._message.hide() selected_nodes = Selection.getAllSelectedObjects() if len(selected_nodes) == 0: self._message = Message(i18n_catalog.i18nc( "@info:status", "No objects selected to orient. Please select one or more objects and try again." ), title=i18n_catalog.i18nc( "@title", "Auto-Orientation")) self._message.show() return message = Message(i18n_catalog.i18nc( "@info:status", "Calculating the optimal orientation..."), 0, False, -1, title=i18n_catalog.i18nc("@title", "Auto-Orientation")) message.show() job = CalculateOrientationJob(selected_nodes, extended_mode=extended_mode, message=message) job.finished.connect(self._onFinished) job.start()
def mergeSelected(self): self.groupSelected() try: group_node = Selection.getAllSelectedObjects()[0] except Exception as e: Logger.log("d", "mergeSelected: Exception:", e) return # Compute the center of the objects when their origins are aligned. object_centers = [ node.getBoundingBox().center for node in group_node.getChildren() ] if object_centers and len(object_centers) > 0: middle_x = sum([v.x for v in object_centers]) / len(object_centers) middle_y = sum([v.y for v in object_centers]) / len(object_centers) middle_z = sum([v.z for v in object_centers]) / len(object_centers) offset = Vector(middle_x, middle_y, middle_z) else: offset = Vector(0, 0, 0) # Move each node to the same position. for center, node in zip(object_centers, group_node.getChildren()): # Align the object and also apply the offset to center it inside the group. node.translate(-1 * (center - offset), SceneNode.TransformSpace.World) # Use the previously found center of the group bounding box as the new location of the group group_node.setPosition(group_node.getBoundingBox().center)
def deleteSelection(self) -> None: 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()
def layFlat(self): self.operationStarted.emit(self) self._progress_message = Message("Laying object flat on buildplate...", lifetime=0, dismissable=False) self._progress_message.setProgress(0) self._iterations = 0 self._total_iterations = 0 for selected_object in Selection.getAllSelectedObjects(): if not selected_object.callDecoration("isGroup"): self._total_iterations += len( selected_object.getMeshDataTransformed().getVertices()) * 2 else: for child in selected_object.getChildren(): self._total_iterations += len( child.getMeshDataTransformed().getVertices()) * 2 self._progress_message.show() operations = Selection.applyOperation(LayFlatOperation) for op in operations: op.progress.connect(self._layFlatProgress) job = LayFlatJob(operations) job.finished.connect(self._layFlatFinished) job.start()
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)) op.push()
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 = [] 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 ): #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()
def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None: """Request that the current selection is written to the output device. The output device to write with will be selected based on the device_id. A file format is chosen from the list of available file formats by the output device. :param device_id: The handle of the device to write to. :param file_name: A suggestion for the file name to write to. Can be freely ignored if providing a file name makes no sense. :param kwargs: Keyword arguments: limit_mimetypes: Limit the possible mimetypes to use for writing to these types. """ if not Selection.hasSelection(): return limit_mimetypes = kwargs.get("limit_mimetypes", False) preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. Application.getInstance().callLater( self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes=preferred_mimetypes)
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
def removeSelection(self): if not Selection.hasSelection(): return op = GroupedOperation() for node in Selection.getAllSelectedObjects(): op.addOperation(RemoveSceneNodeOperation(node)) op.push() Selection.clear()
def _updateEnabled(self): selected_objects = Selection.getAllSelectedObjects() if len(selected_objects)> 1: self._single_model_selected = False elif len(selected_objects) == 1 and selected_objects[0].callDecoration("isGroup"): self._single_model_selected = False # Group is selected, so tool needs to be disabled else: self._single_model_selected = True Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode and self._single_model_selected)
def setSelected(self, key): for index in range(0, len(self.items)): if self.items[index]["key"] == key: for node in Application.getInstance().getController().getScene( ).getRoot().getAllChildren(): if id(node) == key: if node not in Selection.getAllSelectedObjects( ): #node already selected Selection.add(node) if node.callDecoration( "isGroup"): #Its a group node for child_node in node.getChildren(): if child_node not in Selection.getAllSelectedObjects( ): #Set all children to parent state (if they arent already) Selection.add(child_node) else: Selection.remove(node) if node.callDecoration("isGroup"): #Its a group for child_node in node.getChildren(): if child_node in Selection.getAllSelectedObjects( ): Selection.remove(child_node) all_children_selected = True #Check all group nodes to see if all their children are selected (if so, they also need to be selected!) for index in range(0, len(self.items)): if self.items[index]["is_group"]: for node in Application.getInstance().getController().getScene( ).getRoot().getAllChildren(): if node.hasChildren(): if id(node) == self.items[index]["key"] and id( node) != key: for index, child_node in enumerate( node.getChildren()): if not Selection.isSelected(child_node): all_children_selected = False #At least one of its children is not selected, dont change state break if all_children_selected: Selection.add(node) else: Selection.remove(node) #Force update self.updateList( Application.getInstance().getController().getScene().getRoot())
def getSelectedNodes(self): self._message.hide() selection = Selection.getAllSelectedObjects()[:] if selection: return selection else: self._message.setText( catalog.i18nc("@info:status", "Please select one or more models first")) self._message.show()
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()
def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None: if not Selection.hasSelection(): return limit_mimetypes = kwargs.get("limit_mimetypes", False) preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes = preferred_mimetypes)
def requestWriteSelectionToDevice(self, device_id, file_name, kwargs): if not Selection.hasSelection(): return limit_mimetypes = kwargs.get("limit_mimetypes", False) preferred_mimetype = kwargs.get("preferred_mimetype", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetype = preferred_mimetype)
def setX(self, x): bounding_box = Selection.getBoundingBox() op = GroupedOperation() for selected_node in Selection.getAllSelectedObjects(): world_position = selected_node.getWorldPosition() new_position = world_position.set(x=float(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.operationStopped.emit(self)
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
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) return True if event.type == Event.MouseReleaseEvent: # 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
def setZ(self, z): bounding_box = Selection.getBoundingBox() op = GroupedOperation() for selected_node in Selection.getAllSelectedObjects(): # 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=float(z) + (world_position.y - bounding_box.bottom)) node_op = TranslateOperation(selected_node, new_position, set_position = True) op.addOperation(node_op) op.push() self.operationStopped.emit(self)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) for node in Selection.getAllSelectedObjects(): node.setParent(group_node) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
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)
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() # This was formerly done with SetTransformOperation but because of # unpredictable matrix deconstruction it was possible that mirrors # could manifest as rotations. Centering is therefore done by # moving the node to negative whatever its position is: center_operation = TranslateOperation(current_node, -current_node._position) operation.addOperation(center_operation) operation.push()
def _onToolOperationStopped(self, tool): if tool.getPluginId() == "TranslateTool": for node in Selection.getAllSelectedObjects(): if node.getBoundingBox().bottom < 0: if not node.getDecorator(ZOffsetDecorator.ZOffsetDecorator): node.addDecorator(ZOffsetDecorator.ZOffsetDecorator()) node.callDecoration("setZOffset", node.getBoundingBox().bottom) else: if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator): node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) self._enabled = True self._onChangeTimerFinished()
def event(self, event): super().event(event) if event.type == Event.MousePressEvent: if MouseEvent.LeftButton not in event.buttons: return False id = self._renderer.getIdAtCoordinate(event.x, event.y) if not id: return False if ToolHandle.isAxis(id): self.setLockedAxis(id) return True if event.type == Event.MouseReleaseEvent: if self.getLockedAxis(): op = None if Selection.getCount() == 1: node = Selection.getSelectedObject(0) scale = node.getScale() if self.getLockedAxis() == ToolHandle.XAxis: scale.setX(-scale.x) elif self.getLockedAxis() == ToolHandle.YAxis: scale.setY(-scale.y) elif self.getLockedAxis() == ToolHandle.ZAxis: scale.setZ(-scale.z) op = ScaleOperation(node, scale, set_scale=True) else: op = GroupedOperation() for node in Selection.getAllSelectedObjects(): scale = node.getScale() if self.getLockedAxis() == ToolHandle.XAxis: scale.setX(-scale.x) elif self.getLockedAxis() == ToolHandle.YAxis: scale.setY(-scale.y) elif self.getLockedAxis() == ToolHandle.ZAxis: scale.setZ(-scale.z) op.addOperation(ScaleOperation(node, scale, set_scale = True)) op.push() self.setLockedAxis(None) return True return False
def event(self, event): super().event(event) if event.type == Event.MousePressEvent: 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) return True if event.type == Event.MouseReleaseEvent: if self.getLockedAxis(): op = None if Selection.getCount() == 1: node = Selection.getSelectedObject(0) mirror = node.getMirror() if self.getLockedAxis() == ToolHandle.XAxis: mirror.setX(-mirror.x) elif self.getLockedAxis() == ToolHandle.YAxis: mirror.setY(-mirror.y) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror.setZ(-mirror.z) op = MirrorOperation(node, mirror, set_mirror=True) else: op = GroupedOperation() for node in Selection.getAllSelectedObjects(): mirror = node.getMirror() if self.getLockedAxis() == ToolHandle.XAxis: mirror.setX(-mirror.x) elif self.getLockedAxis() == ToolHandle.YAxis: mirror.setY(-mirror.y) elif self.getLockedAxis() == ToolHandle.ZAxis: mirror.setZ(-mirror.z) op.addOperation(MirrorOperation(node, mirror, set_mirror = True)) op.push() self.setLockedAxis(None) return True return False
def setZ(self, z): parsed_z = self._parseInt(z) bounding_box = Selection.getBoundingBox() op = GroupedOperation() if not Float.fuzzyCompare(parsed_z, float(bounding_box.center.y), DIMENSION_TOLERANCE): for selected_node in Selection.getAllSelectedObjects(): # 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() self._controller.toolOperationStopped.emit(self)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) for node in Selection.getAllSelectedObjects(): node.setParent(group_node) group_node.setCenterPosition(group_node.getBoundingBox().center) #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0)) group_node.translate(group_node.getBoundingBox().center) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
def mergeSelected(self): self.groupSelected() try: group_node = Selection.getAllSelectedObjects()[0] except Exception as e: return multi_material_decorator = MultiMaterialDecorator.MultiMaterialDecorator() group_node.addDecorator(multi_material_decorator) # Reset the position of each node for node in group_node.getChildren(): new_position = node.getMeshData().getCenterPosition() new_position.setY(0) node.setPosition(new_position) # Use the previously found center of the group bounding box as the new location of the group group_node.setPosition(group_node.getBoundingBox().center)
def deleteSelection(self): if not self.getController().getToolsEnabled(): return removed_group_nodes = [] 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)) op.push()
def ungroupSelected(self): selected_objects = Selection.getAllSelectedObjects().copy() for node in selected_objects: if node.callDecoration("isGroup"): op = GroupedOperation() group_parent = node.getParent() children = node.getChildren().copy() for child in children: # Set the parent of the children to the parent of the group-node op.addOperation(SetParentOperation(child, group_parent)) # Add all individual nodes to the selection Selection.add(child) op.push()
def _getSelectedObjectsWithoutSelectedAncestors(self) -> List[SceneNode]: if not isinstance(self._selected_objects_without_selected_ancestors, list): nodes = Selection.getAllSelectedObjects() self._selected_objects_without_selected_ancestors = [] for node in nodes: has_selected_ancestor = False ancestor = node.getParent() while ancestor: if Selection.isSelected(ancestor): has_selected_ancestor = True break ancestor = ancestor.getParent() if not has_selected_ancestor: self._selected_objects_without_selected_ancestors.append(node) return self._selected_objects_without_selected_ancestors
def ungroupSelected(self): ungrouped_nodes = [] selected_objects = Selection.getAllSelectedObjects()[:] #clone the list for node in selected_objects: if node.callDecoration("isGroup" ): children_to_move = [] for child in node.getChildren(): if type(child) is SceneNode: children_to_move.append(child) for child in children_to_move: child.setParent(node.getParent()) Selection.add(child) child.callDecoration("setConvexHull",None) node.setParent(None) ungrouped_nodes.append(node) for node in ungrouped_nodes: Selection.remove(node)
def groupSelected(self): group_node = SceneNode() group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) center = Selection.getSelectionCenter() group_node.setPosition(center) group_node.setCenterPosition(center) for node in Selection.getAllSelectedObjects(): world = node.getWorldPosition() node.setParent(group_node) node.setPosition(world - center) for node in group_node.getChildren(): Selection.remove(node) Selection.add(group_node)
def layFlat(self): self.operationStarted.emit(self) self._progress_message = Message("Laying object flat on buildplate...", lifetime = 0, dismissable = False) self._progress_message.setProgress(0) self._iterations = 0 self._total_iterations = 0 for selected_object in Selection.getAllSelectedObjects(): self._total_iterations += len(selected_object.getMeshDataTransformed().getVertices()) * 2 self._progress_message.show() operations = Selection.applyOperation(LayFlatOperation) for op in operations: op.progress.connect(self._layFlatProgress) job = LayFlatJob(operations) job.finished.connect(self._layFlatFinished) job.start()
def deleteSelection(self) -> None: if not Application.getInstance().getController().getToolsEnabled(): return removed_group_nodes = [] 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 Application.getInstance().getController().getScene().sceneChanged.emit(node) op.push()