def slice(self, **kwargs): if not self._enabled: return if self._slicing: if not kwargs.get("force_restart", True): return self._slicing = False self._restart = True if self._process is not None: Logger.log("d", "Killing engine process") try: self._process.terminate() except: # terminating a process that is already terminating causes an exception, silently ignore this. pass self.slicingCancelled.emit() return object_groups = [] if self._settings.getSettingValueByKey("print_sequence") == "One at a time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] children = node.getAllChildren() children.append(node) for child_node in children: if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: temp_list.append(child_node) object_groups.append(temp_list) else: temp_list = [] for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): temp_list.append(node) if len(temp_list) == 0: return object_groups.append(temp_list) #for node in DepthFirstIterator(self._scene.getRoot()): # if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: # if not getattr(node, "_outside_buildarea", False): # objects.append(node) if len(object_groups) == 0: return #No point in slicing an empty build plate if kwargs.get("settings", self._settings).hasErrorValue(): return #No slicing if we have error values since those are by definition illegal values. self._slicing = True self.slicingStarted.emit() self._report_progress = kwargs.get("report_progress", True) if self._report_progress: self.processingProgress.emit(0.0) self._sendSettings(kwargs.get("settings", self._settings)) self._scene.acquireLock() # Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages. # This is done so the gcode can be fragmented in memory and does not need a continues memory space. # (AKA. This prevents MemoryErrors) self._save_gcode = kwargs.get("save_gcode", True) if self._save_gcode: setattr(self._scene, "gcode_list", []) self._save_polygons = kwargs.get("save_polygons", True) slice_message = Cura_pb2.Slice() for group in object_groups: group_message = slice_message.object_lists.add() for object in group: mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) obj = group_message.objects.add() obj.id = id(object) verts = numpy.array(mesh_data.getVertices()) verts[:,[1,2]] = verts[:,[2,1]] verts[:,1] *= -1 obj.vertices = verts.tostring() self._scene.releaseLock() self._socket.sendMessage(slice_message)
def run(self) -> None: if self._build_plate_number is None: self.setResult(StartJobResult.Error) return stack = CuraApplication.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if CuraApplication.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if CuraApplication.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return # Don't slice if the buildplate or the nozzle type is incompatible with the materials if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \ not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable: self.setResult(StartJobResult.MaterialIncompatible) return for position, extruder_stack in stack.extruders.items(): material = extruder_stack.findContainer({"type": "material"}) if not extruder_stack.isEnabled: continue if material: if material.getMetaDataEntry("compatible") == False: self.setResult(StartJobResult.MaterialIncompatible) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData") and node.callDecoration( "getBuildPlateNumber") == self._build_plate_number: node.getParent().removeChild(node) break # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": for node in OneAtATimeIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue # Filter on current build plate build_plate_number = node.callDecoration( "getBuildPlateNumber") if build_plate_number is not None and build_plate_number != self._build_plate_number: continue children = node.getAllChildren() children.append(node) for child_node in children: if child_node.getMeshData() and child_node.getMeshData( ).getVertices() is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] has_printing_mesh = False for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("isSliceable") and node.getMeshData( ) and node.getMeshData().getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) # Find a reason not to add the node if node.callDecoration("getBuildPlateNumber" ) != self._build_plate_number: continue if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh: continue temp_list.append(node) if not is_non_printing_mesh: has_printing_mesh = True Job.yieldThread() #If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() if temp_list: object_groups.append(temp_list) global_stack = CuraApplication.getInstance( ).getGlobalContainerStack() if not global_stack: return extruders_enabled = { position: stack.isEnabled for position, stack in global_stack.extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = global_stack skip_group = False for node in group: # Only check if the printing extruder is enabled for printing meshes is_non_printing_mesh = node.callDecoration( "evaluateIsNonPrintingMesh") extruder_position = node.callDecoration( "getActiveExtruderPosition") if not is_non_printing_mesh and not extruders_enabled[ extruder_position]: skip_group = True has_model_with_disabled_extruders = True associated_disabled_extruders.add(extruder_position) if not skip_group: filtered_object_groups.append(group) if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) associated_disabled_extruders = { str(c) for c in sorted( [int(p) + 1 for p in associated_disabled_extruders]) } self.setMessage(", ".join(associated_disabled_extruders)) return # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not filtered_object_groups: self.setResult(StartJobResult.NothingToSlice) return self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks for extruder_stack in ExtruderManager.getInstance( ).getMachineExtruders(stack.getId()): self._buildExtruderMessage(extruder_stack) for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") if group[0].getParent() is not None and group[0].getParent( ).callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) for object in group: mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed( ).getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) verts += translate # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj = group_message.addRepeatedMessage("objects") obj.id = id(object) indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) else: flat_verts = numpy.array(verts) obj.vertices = flat_verts self._handlePerObjectSettings(object, obj) Job.yieldThread() self.setResult(StartJobResult.Finished)
def run(self) -> None: if self._build_plate_number is None: self.setResult(StartJobResult.Error) return stack = CuraApplication.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if CuraApplication.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if CuraApplication.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return # Don't slice if the buildplate or the nozzle type is incompatible with the materials if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \ not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable: self.setResult(StartJobResult.MaterialIncompatible) return for position, extruder_stack in stack.extruders.items(): material = extruder_stack.findContainer({"type": "material"}) if not extruder_stack.isEnabled: continue if material: if material.getMetaDataEntry("compatible") == False: self.setResult(StartJobResult.MaterialIncompatible) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData") and node.callDecoration( "getBuildPlateNumber") == self._build_plate_number: node.getParent().removeChild(node) break global_enable_support = stack.getProperty("support_enable", "value") # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": # note that one_at_a_time printing is disabled on belt printers due to collission risk for node in OneAtATimeIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue # Filter on current build plate build_plate_number = node.callDecoration( "getBuildPlateNumber") if build_plate_number is not None and build_plate_number != self._build_plate_number: continue children = node.getAllChildren() children.append(node) for child_node in children: if child_node.getMeshData() and child_node.getMeshData( ).getVertices() is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] has_printing_mesh = False # print convex hull nodes as "faux-raft" print_convex_hulls = stack.getProperty("blackbelt_raft", "value") for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. slice_node = (print_convex_hulls and type(node) is ConvexHullNode ) or node.callDecoration("isSliceable") if slice_node and node.getMeshData() and node.getMeshData( ).getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) # Find a reason not to add the node if node.callDecoration( "getBuildPlateNumber" ) != self._build_plate_number and type( node) is not ConvexHullNode: # NB: ConvexHullNodes get none of the usual decorators, so skip checking for them continue if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh: continue temp_list.append(node) if not is_non_printing_mesh: has_printing_mesh = True Job.yieldThread() #If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() if temp_list: object_groups.append(temp_list) global_stack = CuraApplication.getInstance( ).getGlobalContainerStack() if not global_stack: return extruders_enabled = { position: stack.isEnabled for position, stack in global_stack.extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = global_stack skip_group = False for node in group: # Only check if the printing extruder is enabled for printing meshes is_non_printing_mesh = node.callDecoration( "evaluateIsNonPrintingMesh") extruder_position = node.callDecoration( "getActiveExtruderPosition") if extruder_position is None: # raft meshes may not have an extruder position (yet) extruder_position = "0" if not is_non_printing_mesh and not extruders_enabled[ extruder_position]: skip_group = True has_model_with_disabled_extruders = True associated_disabled_extruders.add(extruder_position) if not skip_group: filtered_object_groups.append(group) if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) associated_disabled_extruders = { str(c) for c in sorted( [int(p) + 1 for p in associated_disabled_extruders]) } self.setMessage(", ".join(associated_disabled_extruders)) return # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not filtered_object_groups: self.setResult(StartJobResult.NothingToSlice) return container_registry = ContainerRegistry.getInstance() stack_id = stack.getId() # Adapt layer_height and material_flow for a slanted gantry gantry_angle = self._scene.getRoot().callDecoration( "getGantryAngle") if gantry_angle: # not 0 or None # Act on a copy of the stack, so these changes don't cause a reslice _stack = CuraContainerStack(stack_id + "_temp") for index, container in enumerate(stack.getContainers()): if container_registry.isReadOnly(container.getId()): _stack.replaceContainer(index, container) else: _stack.replaceContainer(index, copy.deepcopy(container)) stack = _stack # Make sure CuraEngine does not create any supports # support_enable is set in the frontend so support options are settable, # but CuraEngine support structures don't work for slanted gantry stack.setProperty("support_enable", "value", False) # Make sure CuraEngine does not create a raft (we create one manually) # Adhesion type is used in the frontend to show the raft in the viewport stack.setProperty("adhesion_type", "value", "none") for key in ["layer_height", "layer_height_0"]: current_value = stack.getProperty(key, "value") stack.setProperty(key, "value", current_value / math.sin(gantry_angle)) self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks # Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first, # then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well. extruder_stack_list = sorted(list(global_stack.extruders.items()), key=lambda item: int(item[0])) for _, extruder_stack in extruder_stack_list: if gantry_angle: # not 0 or None # Act on a copy of the stack, so these changes don't cause a reslice _extruder_stack = CuraContainerStack( extruder_stack.getId() + "_temp") for index, container in enumerate( extruder_stack.getContainers()): if container_registry.isReadOnly(container.getId()): _extruder_stack.replaceContainer(index, container) else: _extruder_stack.replaceContainer( index, copy.deepcopy(container)) extruder_stack = _extruder_stack extruder_stack.setNextStack(stack) for key in [ "material_flow", "prime_tower_flow", "spaghetti_flow" ]: if extruder_stack.hasProperty(key, "value"): current_value = extruder_stack.getProperty( key, "value") extruder_stack.setProperty( key, "value", current_value * math.sin(gantry_angle)) self._buildExtruderMessage(extruder_stack) bottom_cutting_meshes = [] raft_meshes = [] support_meshes = [] if gantry_angle: # not 0 or None for group in filtered_object_groups: added_meshes = [] for object in group: is_non_printing_mesh = False per_object_stack = object.callDecoration("getStack") # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed if type(object) is ConvexHullNode: raft_thickness = stack.getProperty( "blackbelt_raft_thickness", "value") raft_margin = stack.getProperty( "blackbelt_raft_margin", "value") mb = MeshBuilder() hull_polygon = object.getHull() if raft_margin > 0: hull_polygon = hull_polygon.getMinkowskiHull( Polygon.approximatedCircle(raft_margin)) mb.addConvexPolygonExtrusion( hull_polygon.getPoints()[::-1], 0, raft_thickness) new_node = self._addMeshFromBuilder(mb, "raftMesh") added_meshes.append(new_node) raft_meshes.append(new_node.getName()) elif not is_non_printing_mesh: # add support mesh if needed blackbelt_support_gantry_angle_bias = None blackbelt_support_minimum_island_area = None if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) node_enable_support = per_object_stack.getProperty( "support_enable", "value") if per_object_stack.getProperty( "support_mesh", "value"): node_enable_support = node_enable_support or per_object_stack.getProperty( "support_mesh_drop_down", "value") add_support_mesh = node_enable_support if node_enable_support is not None else global_enable_support blackbelt_support_gantry_angle_bias = per_object_stack.getProperty( "blackbelt_support_gantry_angle_bias", "value") blackbelt_support_minimum_island_area = per_object_stack.getProperty( "blackbelt_support_minimum_island_area", "value") else: add_support_mesh = global_enable_support if add_support_mesh: if blackbelt_support_gantry_angle_bias is None: blackbelt_support_gantry_angle_bias = global_stack.getProperty( "blackbelt_support_gantry_angle_bias", "value") biased_down_angle = math.radians( blackbelt_support_gantry_angle_bias) if blackbelt_support_minimum_island_area is None: blackbelt_support_minimum_island_area = global_stack.getProperty( "blackbelt_support_minimum_island_area", "value") support_mesh_data = SupportMeshCreator( down_vector=numpy.array([ 0, -math.cos( math.radians(biased_down_angle)), -math.sin(biased_down_angle) ]), bottom_cut_off=stack.getProperty( "wall_line_width_0", "value") / 2, minimum_island_area= blackbelt_support_minimum_island_area ).createSupportMeshForNode(object) if support_mesh_data: new_node = self._addMeshFromData( support_mesh_data, "generatedSupportMesh") added_meshes.append(new_node) support_meshes.append(new_node.getName()) # check if the bottom needs to be cut off aabb = object.getBoundingBox() if aabb.bottom < 0: # mesh extends below the belt; add a cutting mesh to cut off the part below the bottom height = -aabb.bottom center = Vector(aabb.center.x, -height / 2, aabb.center.z) mb = MeshBuilder() mb.addCube(width=aabb.width, height=height, depth=aabb.depth, center=center) new_node = self._addMeshFromBuilder( mb, "bottomCuttingMesh") added_meshes.append(new_node) bottom_cutting_meshes.append( new_node.getName()) if added_meshes: group += added_meshes transform_matrix = self._scene.getRoot().callDecoration( "getTransformMatrix") front_offset = None raft_offset = 0 raft_speed = None raft_flow = 1.0 if stack.getProperty("blackbelt_raft", "value"): raft_offset = stack.getProperty( "blackbelt_raft_thickness", "value") + stack.getProperty( "blackbelt_raft_gap", "value") raft_speed = stack.getProperty("blackbelt_raft_speed", "value") raft_flow = stack.getProperty("blackbelt_raft_flow", "value") * math.sin(gantry_angle) adhesion_extruder_nr = stack.getProperty("adhesion_extruder_nr", "value") support_extruder_nr = stack.getProperty("support_extruder_nr", "value") for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") if group[0].getParent() is not None and group[0].getParent( ).callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) if transform_matrix: scene_front = None for object in group: if type(object) is ConvexHullNode: continue is_non_printing_mesh = object.getName( ) in bottom_cutting_meshes or object.getName( ) in raft_meshes if not is_non_printing_mesh: per_object_stack = object.callDecoration( "getStack") if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) if not is_non_printing_mesh: _front = object.getBoundingBox().back if scene_front is None or _front < scene_front: scene_front = _front if scene_front is not None: front_offset = transformVertices( numpy.array([[0, 0, scene_front]]), transform_matrix)[0][1] for object in group: if type(object) is ConvexHullNode: continue mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed( ).getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] # offset all non-raft objects if rafts are enabled # air gap is applied here to vertically offset objects from the raft if object.getName() not in raft_meshes: translate[1] += raft_offset if front_offset: translate[2] -= front_offset # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) verts += translate if transform_matrix: verts = transformVertices(verts, transform_matrix) # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj = group_message.addRepeatedMessage("objects") obj.id = id(object) obj.name = object.getName() indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) else: flat_verts = numpy.array(verts) obj.vertices = flat_verts if object.getName() in raft_meshes: self._addSettingsMessage( obj, { "wall_line_count": 99999999, "speed_wall_0": raft_speed, "speed_wall_x": raft_speed, "material_flow": raft_flow, "extruder_nr": adhesion_extruder_nr }) elif object.getName() in support_meshes: self._addSettingsMessage( obj, { "support_mesh": "True", "support_mesh_drop_down": "False", "extruder_nr": support_extruder_nr }) elif object.getName() in bottom_cutting_meshes: self._addSettingsMessage( obj, { "cutting_mesh": True, "wall_line_count": 0, "top_layers": 0, "bottom_layers": 0, "infill_line_distance": 0, "extruder_nr": 0 }) else: self._handlePerObjectSettings(object, obj) Job.yieldThread() # Store the front-most coordinate of the scene so the scene can be moved back into place post slicing # TODO: this should be handled per mesh-group instead of per scene # One-at-a-time printing should be disabled for slanted gantry printers for now self._scene.getRoot().callDecoration("setSceneFrontOffset", front_offset) self.setResult(StartJobResult.Finished)
def run(self): stack = Application.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if not Application.getInstance().getMachineManager( ).isActiveStackValid: self.setResult(StartJobResult.SettingError) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is not SceneNode or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.SettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): node.getParent().removeChild(node) break # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue children = node.getAllChildren() children.append(node) for child_node in children: if type(child_node ) is SceneNode and child_node.getMeshData( ) and child_node.getMeshData().getVertices( ) is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData( ) and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): temp_list.append(node) Job.yieldThread() if temp_list: object_groups.append(temp_list) if not object_groups: self.setResult(StartJobResult.NothingToSlice) return self._buildGlobalSettingsMessage(stack) for extruder_stack in cura.Settings.ExtruderManager.getInstance( ).getMachineExtruders(stack.getId()): self._buildExtruderMessage(extruder_stack) for group in object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") if group[0].getParent().callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) for object in group: mesh_data = object.getMeshData().getTransformed( object.getWorldTransformation()) obj = group_message.addRepeatedMessage("objects") obj.id = id(object) verts = numpy.array(mesh_data.getVertices()) # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj.vertices = verts self._handlePerObjectSettings(object, obj) Job.yieldThread() self.setResult(StartJobResult.Finished)
def run(self): with self._scene.getSceneLock(): for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): node.getParent().removeChild(node) break object_groups = [] if self._profile.getSettingValue( "print_sequence") == "one_at_a_time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] if getattr(node, "_outside_buildarea", False): continue children = node.getAllChildren() children.append(node) for child_node in children: if type(child_node ) is SceneNode and child_node.getMeshData( ) and child_node.getMeshData().getVertices( ) is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData( ) and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): temp_list.append(node) Job.yieldThread() if temp_list: object_groups.append(temp_list) if not object_groups: return self._buildSettingsMessage(self._profile) for group in object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") if group[0].getParent().callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) for object in group: mesh_data = object.getMeshData().getTransformed( object.getWorldTransformation()) obj = group_message.addRepeatedMessage("objects") obj.id = id(object) verts = numpy.array(mesh_data.getVertices()) verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj.vertices = verts self._handlePerObjectSettings(object, obj) Job.yieldThread() self.setResult(True)
def slice(self, **kwargs): if not self._enabled: return if self._slicing: if not kwargs.get("force_restart", True): return self._slicing = False self._restart = True if self._process is not None: Logger.log("d", "Killing engine process") try: self._process.terminate() except: # terminating a process that is already terminating causes an exception, silently ignore this. pass self.slicingCancelled.emit() return Logger.log("d", "Preparing to send slice data to engine.") object_groups = [] if self._profile.getSettingValue("print_sequence") == "one_at_a_time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] children = node.getAllChildren() children.append(node) for child_node in children: if type(child_node) is SceneNode and child_node.getMeshData( ) and child_node.getMeshData().getVertices() is not None: temp_list.append(child_node) object_groups.append(temp_list) else: temp_list = [] for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData( ) and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): temp_list.append(node) if len(temp_list) == 0: self.processingProgress.emit(0.0) return object_groups.append(temp_list) #for node in DepthFirstIterator(self._scene.getRoot()): # if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: # if not getattr(node, "_outside_buildarea", False): # objects.append(node) if len(object_groups) == 0: if self._message: self._message.hide() self._message = None return #No point in slicing an empty build plate if kwargs.get("profile", self._profile).hasErrorValue(): Logger.log('w', "Profile has error values. Aborting slicing") if self._message: self._message.hide() self._message = None self._message = Message( catalog.i18nc( "@info:status", "Unable to slice. Please check your setting values for errors." )) self._message.show() return #No slicing if we have error values since those are by definition illegal values. # Remove existing layer data (if any) for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData(): if node.callDecoration("getLayerData"): Application.getInstance().getController().getScene( ).getRoot().removeChild(node) break Application.getInstance().getController().getScene().gcode_list = None self._slicing = True self.slicingStarted.emit() self._report_progress = kwargs.get("report_progress", True) if self._report_progress: self.processingProgress.emit(0.0) if not self._message: self._message = Message( catalog.i18nc("@info:status", "Slicing..."), 0, False, -1) self._message.show() else: self._message.setProgress(-1) self._sendSettings(kwargs.get("profile", self._profile)) self._scene.acquireLock() # Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages. # This is done so the gcode can be fragmented in memory and does not need a continues memory space. # (AKA. This prevents MemoryErrors) self._save_gcode = kwargs.get("save_gcode", True) if self._save_gcode: setattr(self._scene, "gcode_list", []) self._save_polygons = kwargs.get("save_polygons", True) slice_message = Cura_pb2.Slice() for group in object_groups: group_message = slice_message.object_lists.add() for object in group: mesh_data = object.getMeshData().getTransformed( object.getWorldTransformation()) obj = group_message.objects.add() obj.id = id(object) verts = numpy.array(mesh_data.getVertices()) verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj.vertices = verts.tostring() self._handlePerObjectSettings(object, obj) # Hack to add per-object settings also to the "MeshGroup" in CuraEngine # We really should come up with a better solution for this. self._handlePerObjectSettings(group[0], group_message) self._scene.releaseLock() Logger.log("d", "Sending data to engine for slicing.") self._socket.sendMessage(slice_message)
def run(self): if self._build_plate_number is None: self.setResult(StartJobResult.Error) return stack = Application.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if Application.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if Application.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): material = extruder_stack.findContainer({"type": "material"}) if material: if material.getMetaDataEntry("compatible") == False: self.setResult(StartJobResult.MaterialIncompatible) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is not CuraSceneNode or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number: node.getParent().removeChild(node) break # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue # Filter on current build plate build_plate_number = node.callDecoration("getBuildPlateNumber") if build_plate_number is not None and build_plate_number != self._build_plate_number: continue children = node.getAllChildren() children.append(node) for child_node in children: if type(child_node) is CuraSceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log("w", "No objects suitable for one at a time found, or no correct order found") else: temp_list = [] has_printing_mesh = False for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("isSliceable") and type(node) is CuraSceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False if per_object_stack: is_non_printing_mesh = any(per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) if (node.callDecoration("getBuildPlateNumber") == self._build_plate_number): if not getattr(node, "_outside_buildarea", False) or is_non_printing_mesh: temp_list.append(node) if not is_non_printing_mesh: has_printing_mesh = True Job.yieldThread() #If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() if temp_list: object_groups.append(temp_list) # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not object_groups: self.setResult(StartJobResult.NothingToSlice) return self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): print_mode = Application.getInstance().getGlobalContainerStack().getProperty("print_mode", "value") if print_mode == "regular": for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): self._buildExtruderMessage(extruder_stack) else: main_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack() for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): self._buildExtruderMessage(main_extruder_stack, int(extruder_stack.getMetaDataEntry("position"))) for group in object_groups: group_message = self._slice_message.addRepeatedMessage("object_lists") if group[0].getParent().callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) for object in group: mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed().getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) verts += translate # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj = group_message.addRepeatedMessage("objects") obj.id = id(object) indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) else: flat_verts = numpy.array(verts) obj.vertices = flat_verts self._handlePerObjectSettings(object, obj) Job.yieldThread() self.setResult(StartJobResult.Finished)