def _checkHit(self, a: SceneNode, b: SceneNode) -> bool: """Checks if a can be printed before b :param a: node :param b: node :return: true if a can be printed before b """ if a == b: return False a_hit_hull = a.callDecoration("getConvexHullBoundary") b_hit_hull = b.callDecoration("getConvexHullHeadFull") overlap = a_hit_hull.intersectsPolygon(b_hit_hull) if overlap: return True # Adhesion areas must never overlap, regardless of printing order # This would cause over-extrusion a_hit_hull = a.callDecoration("getAdhesionArea") b_hit_hull = b.callDecoration("getAdhesionArea") overlap = a_hit_hull.intersectsPolygon(b_hit_hull) if overlap: return True else: return False
def _onSceneChangedDelayed(self, scene_node: SceneNode) -> None: # Ignore any changes that are not related to sliceable objects if not isinstance(scene_node, SceneNode) \ or not scene_node.callDecoration("isSliceable") \ or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate: return self._change_timer.start()
def test_SceneNodeDecorator(): test_node = SceneNode() test_decorator = SceneNodeDecorator() amazing_decorator = TheAmazingTestDecorator() less_amazing_decorator = TheLessAmazingTestDecorator() not_amazing_decorator = TheNotSoAmazingTestDecorator() # Replace emit with mock object test_node.decoratorsChanged.emit = MagicMock() test_decorator.clear = MagicMock() # First actual change test_node.addDecorator(test_decorator) assert len(test_node.getDecorators()) == 1 assert test_node.decoratorsChanged.emit.call_count == 1 # Adding a decorator of the same type (SceneNodeDecorator) again should not do anything. test_node.addDecorator(test_decorator) assert len(test_node.getDecorators()) == 1 assert test_node.decoratorsChanged.emit.call_count == 1 assert test_node.getDecorator(type(test_decorator)) == test_decorator # Remove the decorator again! test_node.removeDecorator(SceneNodeDecorator) assert len(test_node.getDecorators()) == 0 assert test_node.decoratorsChanged.emit.call_count == 2 assert test_decorator.clear.call_count == 1 # Ensure that the clear of the test decorator is called. # Add a number of decorators! test_node.addDecorator(amazing_decorator) test_node.addDecorator(less_amazing_decorator) test_node.addDecorator(not_amazing_decorator) assert test_node.decoratorsChanged.emit.call_count == 5 assert len(test_node.getDecorators()) == 3 assert test_node.hasDecoration("zomg") == False assert test_node.hasDecoration("theOkayDecoration") assert test_node.hasDecoration("theAmazingDecoration") # Calling the decorations with args / kwargs assert test_node.callDecoration("theOkayDecoration", None) is None assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep") == ("beep", "Wow", "so wow") assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep", much_test="Wow") == ("beep", "Wow", "Wow") # Calling decoration that is "double" assert test_node.callDecoration("theAmazingDecoration") == "Amazing!" test_node.removeDecorator(TheAmazingTestDecorator) assert test_node.callDecoration("theAmazingDecoration") == "amazing" not_amazing_decorator.clear = MagicMock() test_node.removeDecorators() # Also assure that removing all decorators also triggers the clear assert not_amazing_decorator.clear.call_count == 1 assert len(test_node.getDecorators()) == 0 assert test_node.decoratorsChanged.emit.call_count == 7 assert test_node.getDecorator(type(test_decorator)) is None
def _onSceneChanged(self, scene_node: SceneNode) -> None: # Ignore any changes that are not related to sliceable objects if not isinstance(scene_node, SceneNode)\ or not scene_node.callDecoration("isSliceable")\ or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate: return self.setToZeroPrintInformation(self._active_build_plate)
def _updateNodeListeners(self, node: SceneNode): per_mesh_stack = node.callDecoration("getStack") if per_mesh_stack: per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") if active_extruder_changed is not None: active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) self._updateDisallowedAreasAndRebuild()
def _updateNodeListeners(self, node: SceneNode): per_mesh_stack = node.callDecoration("getStack") if per_mesh_stack: per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") if active_extruder_changed is not None: active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) self._updateDisallowedAreasAndRebuild()
def _onSceneChanged(self, source: SceneNode) -> None: if not isinstance(source, SceneNode): return # This case checks if the source node is a node that contains GCode. In this case the # current layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration( "getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration( "getBuildPlateNumber") if source == self._scene.getRoot(): # we got the root node num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list( self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[ build_plate_number] != self._last_num_objects[ build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[ build_plate_number] build_plate_changed.add(build_plate_number) else: # we got a single scenenode if not source.callDecoration("isGroup"): if source.getMeshData() is None: return if source.getMeshData().getVertices() is None: return build_plate_changed.add(source_build_plate_number) build_plate_changed.discard(None) build_plate_changed.discard(-1) # object not on build plate if not build_plate_changed: return if self._tool_active: # do it later, each source only has to be done once if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self.backendStateChange.emit(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) self._invokeSlice()
def test_SceneNodeDecorator(): test_node = SceneNode() test_decorator = SceneNodeDecorator() amazing_decorator = TheAmazingTestDecorator() less_amazing_decorator = TheLessAmazingTestDecorator() not_amazing_decorator = TheNotSoAmazingTestDecorator() # Replace emit with mock object test_node.decoratorsChanged.emit = MagicMock() test_decorator.clear = MagicMock() # First actual change test_node.addDecorator(test_decorator) assert len(test_node.getDecorators()) == 1 assert test_node.decoratorsChanged.emit.call_count == 1 # Adding a decorator of the same type (SceneNodeDecorator) again should not do anything. test_node.addDecorator(test_decorator) assert len(test_node.getDecorators()) == 1 assert test_node.decoratorsChanged.emit.call_count == 1 assert test_node.getDecorator(type(test_decorator)) == test_decorator # Remove the decorator again! test_node.removeDecorator(SceneNodeDecorator) assert len(test_node.getDecorators()) == 0 assert test_node.decoratorsChanged.emit.call_count == 2 assert test_decorator.clear.call_count == 1 # Ensure that the clear of the test decorator is called. # Add a number of decorators! test_node.addDecorator(amazing_decorator) test_node.addDecorator(less_amazing_decorator) test_node.addDecorator(not_amazing_decorator) assert test_node.decoratorsChanged.emit.call_count == 5 assert len(test_node.getDecorators()) == 3 assert test_node.hasDecoration("zomg") == False assert test_node.hasDecoration("theOkayDecoration") assert test_node.hasDecoration("theAmazingDecoration") # Calling the decorations with args / kwargs assert test_node.callDecoration("theOkayDecoration", None) is None assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep") == ("beep", "Wow", "so wow") assert test_node.callDecoration("theEvenMoreAmazingDecoration", "beep", much_test = "Wow") == ("beep", "Wow", "Wow") # Calling decoration that is "double" assert test_node.callDecoration("theAmazingDecoration") == "Amazing!" test_node.removeDecorator(TheAmazingTestDecorator) assert test_node.callDecoration("theAmazingDecoration") == "amazing" not_amazing_decorator.clear = MagicMock() test_node.removeDecorators() # Also assure that removing all decorators also triggers the clear assert not_amazing_decorator.clear.call_count == 1 assert len(test_node.getDecorators()) == 0 assert test_node.decoratorsChanged.emit.call_count == 7 assert test_node.getDecorator(type(test_decorator)) is None
def _onSceneChanged(self, source: SceneNode) -> None: """Listener for when the scene has changed. This should start a slice if the scene is now ready to slice. :param source: The scene node that was changed. """ if not source.callDecoration("isSliceable") and source != self._scene.getRoot(): return # This case checks if the source node is a node that contains GCode. In this case the # current layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration("getBuildPlateNumber") if source == self._scene.getRoot(): # we got the root node num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[build_plate_number] build_plate_changed.add(build_plate_number) else: # we got a single scenenode if not source.callDecoration("isGroup"): mesh_data = source.getMeshData() if mesh_data is None or mesh_data.getVertices() is None: return # There are some SceneNodes that do not have any build plate associated, then do not add to the list. if source_build_plate_number is not None: build_plate_changed.add(source_build_plate_number) if not build_plate_changed: return if self._tool_active: # do it later, each source only has to be done once if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self._clearLayerData(build_plate_changed) self._invokeSlice()
def _shouldNodeBeHandled(self, node: SceneNode) -> bool: is_group = bool(node.callDecoration("isGroup")) if not node.callDecoration("isSliceable") and not is_group: return False parent = node.getParent() if parent and parent.callDecoration("isGroup"): return False # Grouped nodes don't need resetting as their parent (the group) is resetted) node_build_plate_number = node.callDecoration("getBuildPlateNumber") if Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") and node_build_plate_number != self._build_plate_number: return False return True
def _shouldNodeBeHandled(self, node: SceneNode) -> bool: is_group = bool(node.callDecoration("isGroup")) if not node.callDecoration("isSliceable") and not is_group: return False parent = node.getParent() if parent and parent.callDecoration("isGroup"): return False # Grouped nodes don't need resetting as their parent (the group) is resetted) node_build_plate_number = node.callDecoration("getBuildPlateNumber") if Application.getInstance().getPreferences().getValue("view/filter_current_build_plate") and node_build_plate_number != self._build_plate_number: return False return True
def _onSceneChanged(self, source: SceneNode) -> None: if not isinstance(source, SceneNode): return # This case checks if the source node is a node that contains GCode. In this case the # current layer data is removed so the previous data is not rendered - CURA-4821 if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration("getBuildPlateNumber") if source == self._scene.getRoot(): # we got the root node num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list(self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != self._last_num_objects[build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[build_plate_number] build_plate_changed.add(build_plate_number) else: # we got a single scenenode if not source.callDecoration("isGroup"): mesh_data = source.getMeshData() if mesh_data is None or mesh_data.getVertices() is None: return # There are some SceneNodes that do not have any build plate associated, then do not add to the list. if source_build_plate_number is not None: build_plate_changed.add(source_build_plate_number) if not build_plate_changed: return if self._tool_active: # do it later, each source only has to be done once if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self.setState(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) self._invokeSlice()
def _onSceneChanged(self, source: SceneNode) -> None: if not isinstance(source, SceneNode): return if source.callDecoration("isBlockSlicing") and source.callDecoration( "getLayerData"): self._stored_optimized_layer_data = {} build_plate_changed = set() source_build_plate_number = source.callDecoration( "getBuildPlateNumber") if source == self._scene.getRoot(): num_objects = self._numObjectsPerBuildPlate() for build_plate_number in list( self._last_num_objects.keys()) + list(num_objects.keys()): if build_plate_number not in self._last_num_objects or num_objects[build_plate_number] != \ self._last_num_objects[build_plate_number]: self._last_num_objects[build_plate_number] = num_objects[ build_plate_number] build_plate_changed.add(build_plate_number) else: if not source.callDecoration("isGroup"): mesh_data = source.getMeshData() if mesh_data is None or mesh_data.getVertices() is None: return if source_build_plate_number is not None: build_plate_changed.add(source_build_plate_number) if not build_plate_changed: return if self._tool_active: if source not in self._postponed_scene_change_sources: self._postponed_scene_change_sources.append(source) return self.stopSlicing() for build_plate_number in build_plate_changed: if build_plate_number not in self._build_plates_to_be_sliced: self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) self.backendStateChange.emit(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) self._invokeSlice()
def getNodeActiveExtruder(node: SceneNode) -> ExtruderStack: active_extruder_position = node.callDecoration("getActiveExtruderPosition") if active_extruder_position is None: active_extruder_position = 0 else: active_extruder_position = int(active_extruder_position) # Only use the extruder that is active on our mesh_node # The back end only supports a single extruder, currently. # Ignore any extruder that is not the active extruder. machine_extruders = list(filter( lambda extruder: extruder.position == active_extruder_position, CuraApplication.getInstance().getMachineManager().activeMachine.extruderList )) if len(machine_extruders) > 0: return machine_extruders[0] return None
def _convertSavitarNodeToUMNode(self, savitar_node): um_node = SceneNode() transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # 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. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition = QualityManager.getInstance( ).getParentMachineDefinition( global_container_stack.getBottom()) um_node.callDecoration("getStack").getTop().setDefinition( definition.getId()) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _createNodeFromObject(self, object, name=""): node = SceneNode() node.setName(name) mesh_builder = MeshBuilder() vertex_list = [] components = object.find(".//3mf:components", self._namespaces) if components: for component in components: id = component.get("objectid") new_object = self._root.find( "./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) new_node = self._createNodeFromObject( new_object, self._base_name + "_" + str(id)) node.addChild(new_node) transform = component.get("transform") if transform is not None: new_node.setTransformation( self._createMatrixFromTransformationString(transform)) # for vertex in entry.mesh.vertices.vertex: for vertex in object.findall(".//3mf:vertex", self._namespaces): vertex_list.append( [vertex.get("x"), vertex.get("y"), vertex.get("z")]) Job.yieldThread() xml_settings = list(object.findall(".//cura:setting", self._namespaces)) # Add the setting override decorator, so we can add settings to this node. if xml_settings: node.addDecorator(SettingOverrideDecorator()) global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance( ).getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() node.callDecoration("setActiveExtruder", default_stack_id) # Get the definition & set it definition = QualityManager.getInstance( ).getParentMachineDefinition( global_container_stack.getBottom()) node.callDecoration("getStack").getTop().setDefinition( definition) setting_container = node.callDecoration("getStack").getTop() for setting in xml_settings: setting_key = setting.get("key") setting_value = setting.text # Extruder_nr is a special case. if setting_key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(setting_key, "value", setting_value) if len(node.getChildren()) > 0: group_decorator = GroupDecorator() node.addDecorator(group_decorator) triangles = object.findall(".//3mf:triangle", self._namespaces) mesh_builder.reserveFaceCount(len(triangles)) for triangle in triangles: v1 = int(triangle.get("v1")) v2 = int(triangle.get("v2")) v3 = int(triangle.get("v3")) mesh_builder.addFaceByPoints( vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2], vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2], vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2]) Job.yieldThread() # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals(fast=True) mesh_builder.setFileName(name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): node.setMeshData(mesh_data) node.setSelectable(True) return node
def _onSceneChanged(self, changed_object: SceneNode): if changed_object.callDecoration( "getLayerData"): # Any layer data has changed. self._switching_layers = True self._old_current_layer = 0 self._old_current_path = 0
def _convertSavitarNodeToUMNode(self, savitar_node): um_node = SceneNode() transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring(savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # 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. if global_container_stack: multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() um_node.callDecoration("setActiveExtruder", default_stack_id) # Get the definition & set it definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) um_node.callDecoration("getStack").getTop().setDefinition(definition) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key,"value", setting_value) if len(um_node.getChildren()) > 0: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _createNodeFromObject(self, object, name = ""): node = SceneNode() node.setName(name) mesh_builder = MeshBuilder() vertex_list = [] components = object.find(".//3mf:components", self._namespaces) if components: for component in components: id = component.get("objectid") new_object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) new_node = self._createNodeFromObject(new_object, self._base_name + "_" + str(id)) node.addChild(new_node) transform = component.get("transform") if transform is not None: new_node.setTransformation(self._createMatrixFromTransformationString(transform)) # for vertex in entry.mesh.vertices.vertex: for vertex in object.findall(".//3mf:vertex", self._namespaces): vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) Job.yieldThread() xml_settings = list(object.findall(".//cura:setting", self._namespaces)) # Add the setting override decorator, so we can add settings to this node. if xml_settings: node.addDecorator(SettingOverrideDecorator()) global_container_stack = Application.getInstance().getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() node.callDecoration("setActiveExtruder", default_stack_id) # Get the definition & set it definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) node.callDecoration("getStack").getTop().setDefinition(definition) setting_container = node.callDecoration("getStack").getTop() for setting in xml_settings: setting_key = setting.get("key") setting_value = setting.text # Extruder_nr is a special case. if setting_key == "extruder_nr": extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) if extruder_stack: node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(setting_key,"value", setting_value) if len(node.getChildren()) > 0: group_decorator = GroupDecorator() node.addDecorator(group_decorator) triangles = object.findall(".//3mf:triangle", self._namespaces) mesh_builder.reserveFaceCount(len(triangles)) for triangle in triangles: v1 = int(triangle.get("v1")) v2 = int(triangle.get("v2")) v3 = int(triangle.get("v3")) mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2], vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2], vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2]) Job.yieldThread() # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() mesh_builder.setFileName(name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): node.setMeshData(mesh_data) node.setSelectable(True) return node