def getUsedExtruderStacks(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() container_registry = UM.Settings.ContainerRegistry.getInstance() if global_stack.getProperty("machine_extruder_count", "value") <= 1: #For single extrusion. return [global_stack] used_extruder_stack_ids = set() #Get the extruders of all meshes in the scene. support_enabled = False support_interface_enabled = False scene_root = UM.Application.getInstance().getController().getScene( ).getRoot() meshes = [ node for node in DepthFirstIterator(scene_root) if type(node) is SceneNode and node.isSelectable() ] #Only use the nodes that will be printed. for mesh in meshes: extruder_stack_id = mesh.callDecoration("getActiveExtruder") if not extruder_stack_id: #No per-object settings for this node. extruder_stack_id = self.extruderIds["0"] used_extruder_stack_ids.add(extruder_stack_id) #Get whether any of them use support. per_mesh_stack = mesh.callDecoration("getStack") if per_mesh_stack: support_enabled |= per_mesh_stack.getProperty( "support_enable", "value") support_interface_enabled |= per_mesh_stack.getProperty( "support_interface_enable", "value") else: #Take the setting from the build extruder stack. extruder_stack = container_registry.findContainerStacks( id=extruder_stack_id)[0] support_enabled |= extruder_stack.getProperty( "support_enable", "value") support_interface_enabled |= extruder_stack.getProperty( "support_enable", "value") #The support extruders. if support_enabled: used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_infill_extruder_nr", "value"))]) used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_extruder_nr_layer_0", "value"))]) if support_interface_enabled: used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_interface_extruder_nr", "value"))]) #The platform adhesion extruder. Not used if using none. if global_stack.getProperty("adhesion_type", "value") != "none": used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("adhesion_extruder_nr", "value"))]) try: return [ container_registry.findContainerStacks(id=stack_id)[0] for stack_id in used_extruder_stack_ids ] except IndexError: # One or more of the extruders was not found. UM.Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids) return []
def render(self) -> None: if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) if self._shader: self._shader.setUniformValue("u_overhangAngle", 1.0) self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0]) self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0]) self._shader.setUniformValue("u_shininess", 20.0) self._shader.setUniformValue("u_faceId", -1) # Don't render any selected faces in the preview. if not self._non_printing_shader: if self._non_printing_shader: self._non_printing_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader")) self._non_printing_shader.setUniformValue("u_diffuseColor", [0.5, 0.5, 0.5, 0.5]) self._non_printing_shader.setUniformValue("u_opacity", 0.6) if not self._support_mesh_shader: self._support_mesh_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) if self._support_mesh_shader: self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) self._support_mesh_shader.setUniformValue("u_width", 5.0) self._gl.glClearColor(0.0, 0.0, 0.0, 0.0) self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT) # Create batches to be rendered batch = RenderBatch(self._shader) batch_support_mesh = RenderBatch(self._support_mesh_shader) # Fill up the batch with objects that can be sliced. for node in DepthFirstIterator(self._scene.getRoot()): if hasattr(node, "_outside_buildarea") and not getattr(node, "_outside_buildarea"): if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible(): per_mesh_stack = node.callDecoration("getStack") if node.callDecoration("isNonThumbnailVisibleMesh"): # Non printing mesh continue elif per_mesh_stack is not None and per_mesh_stack.getProperty("support_mesh", "value"): # Support mesh uniforms = {} shade_factor = 0.6 diffuse_color = cast(CuraSceneNode, node).getDiffuseColor() diffuse_color2 = [ diffuse_color[0] * shade_factor, diffuse_color[1] * shade_factor, diffuse_color[2] * shade_factor, 1.0] uniforms["diffuse_color"] = prettier_color(diffuse_color) uniforms["diffuse_color_2"] = diffuse_color2 batch_support_mesh.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms) else: # Normal scene node uniforms = {} uniforms["diffuse_color"] = prettier_color(cast(CuraSceneNode, node).getDiffuseColor()) batch.addItem(node.getWorldTransformation(), node.getMeshData(), uniforms = uniforms) self.bind() if self._camera is None: render_camera = Application.getInstance().getController().getScene().getActiveCamera() else: render_camera = self._camera batch.render(render_camera) batch_support_mesh.render(render_camera) self.release()
def _onWriteStarted(self, output_device): try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data global_container_stack = Application.getInstance( ).getGlobalContainerStack() print_information = Application.getInstance().getPrintInformation() data = dict() # The data that we're going to submit. data["time_stamp"] = time.time() data["schema_version"] = 0 data["cura_version"] = Application.getInstance().getVersion() active_mode = Preferences.getInstance().getValue( "cura/active_mode") if active_mode == 0: data["active_mode"] = "recommended" else: data["active_mode"] = "custom" definition_changes = global_container_stack.definitionChanges machine_settings_changed_by_user = False if definition_changes.getId() != "empty": # Now a definition_changes container will always be created for a stack, # so we also need to check if there is any instance in the definition_changes container if definition_changes.getAllKeys(): machine_settings_changed_by_user = True data[ "machine_settings_changed_by_user"] = machine_settings_changed_by_user data["language"] = Preferences.getInstance().getValue( "general/language") data["os"] = { "type": platform.system(), "version": platform.version() } data["active_machine"] = { "definition_id": global_container_stack.definition.getId(), "manufacturer": global_container_stack.definition.getMetaData().get( "manufacturer", "") } # add extruder specific data to slice info data["extruders"] = [] extruders = list(ExtruderManager.getInstance().getMachineExtruders( global_container_stack.getId())) extruders = sorted( extruders, key=lambda extruder: extruder.getMetaDataEntry("position")) for extruder in extruders: extruder_dict = dict() extruder_dict["active"] = ExtruderManager.getInstance( ).getActiveExtruderStack() == extruder extruder_dict["material"] = { "GUID": extruder.material.getMetaData().get("GUID", ""), "type": extruder.material.getMetaData().get("material", ""), "brand": extruder.material.getMetaData().get("brand", "") } extruder_position = int( extruder.getMetaDataEntry("position", "0")) if extruder_position in print_information.materialLengths: extruder_dict[ "material_used"] = print_information.materialLengths[ extruder_position] extruder_dict["variant"] = extruder.variant.getName() extruder_dict["nozzle_size"] = extruder.getProperty( "machine_nozzle_size", "value") extruder_settings = dict() extruder_settings["wall_line_count"] = extruder.getProperty( "wall_line_count", "value") extruder_settings["retraction_enable"] = extruder.getProperty( "retraction_enable", "value") extruder_settings[ "infill_sparse_density"] = extruder.getProperty( "infill_sparse_density", "value") extruder_settings["infill_pattern"] = extruder.getProperty( "infill_pattern", "value") extruder_settings[ "gradual_infill_steps"] = extruder.getProperty( "gradual_infill_steps", "value") extruder_settings[ "default_material_print_temperature"] = extruder.getProperty( "default_material_print_temperature", "value") extruder_settings[ "material_print_temperature"] = extruder.getProperty( "material_print_temperature", "value") extruder_dict["extruder_settings"] = extruder_settings data["extruders"].append(extruder_dict) data[ "quality_profile"] = global_container_stack.quality.getMetaData( ).get("quality_type") data["models"] = [] # Listing all files placed on the build plate for node in DepthFirstIterator(CuraApplication.getInstance( ).getController().getScene().getRoot()): if node.callDecoration("isSliceable"): model = dict() model["hash"] = node.getMeshData().getHash() bounding_box = node.getBoundingBox() model["bounding_box"] = { "minimum": { "x": bounding_box.minimum.x, "y": bounding_box.minimum.y, "z": bounding_box.minimum.z }, "maximum": { "x": bounding_box.maximum.x, "y": bounding_box.maximum.y, "z": bounding_box.maximum.z } } model["transformation"] = { "data": str(node.getWorldTransformation().getData()).replace( "\n", "") } extruder_position = node.callDecoration( "getActiveExtruderPosition") model[ "extruder"] = 0 if extruder_position is None else int( extruder_position) model_settings = dict() model_stack = node.callDecoration("getStack") if model_stack: model_settings[ "support_enabled"] = model_stack.getProperty( "support_enable", "value") model_settings["support_extruder_nr"] = int( model_stack.getProperty("support_extruder_nr", "value")) # Mesh modifiers; model_settings[ "infill_mesh"] = model_stack.getProperty( "infill_mesh", "value") model_settings[ "cutting_mesh"] = model_stack.getProperty( "cutting_mesh", "value") model_settings[ "support_mesh"] = model_stack.getProperty( "support_mesh", "value") model_settings[ "anti_overhang_mesh"] = model_stack.getProperty( "anti_overhang_mesh", "value") model_settings[ "wall_line_count"] = model_stack.getProperty( "wall_line_count", "value") model_settings[ "retraction_enable"] = model_stack.getProperty( "retraction_enable", "value") # Infill settings model_settings[ "infill_sparse_density"] = model_stack.getProperty( "infill_sparse_density", "value") model_settings[ "infill_pattern"] = model_stack.getProperty( "infill_pattern", "value") model_settings[ "gradual_infill_steps"] = model_stack.getProperty( "gradual_infill_steps", "value") model["model_settings"] = model_settings data["models"].append(model) print_times = print_information._print_time_message_values data["print_times"] = { "travel": int(print_times["travel"].getDisplayString( DurationFormat.Format.Seconds)), "support": int(print_times["support"].getDisplayString( DurationFormat.Format.Seconds)), "infill": int(print_times["infill"].getDisplayString( DurationFormat.Format.Seconds)), "total": int( print_information.currentPrintTime.getDisplayString( DurationFormat.Format.Seconds)) } print_settings = dict() print_settings[ "layer_height"] = global_container_stack.getProperty( "layer_height", "value") # Support settings print_settings[ "support_enabled"] = global_container_stack.getProperty( "support_enable", "value") print_settings["support_extruder_nr"] = int( global_container_stack.getProperty("support_extruder_nr", "value")) # Platform adhesion settings print_settings[ "adhesion_type"] = global_container_stack.getProperty( "adhesion_type", "value") # Shell settings print_settings[ "wall_line_count"] = global_container_stack.getProperty( "wall_line_count", "value") print_settings[ "retraction_enable"] = global_container_stack.getProperty( "retraction_enable", "value") # Prime tower settings print_settings[ "prime_tower_enable"] = global_container_stack.getProperty( "prime_tower_enable", "value") # Infill settings print_settings[ "infill_sparse_density"] = global_container_stack.getProperty( "infill_sparse_density", "value") print_settings[ "infill_pattern"] = global_container_stack.getProperty( "infill_pattern", "value") print_settings[ "gradual_infill_steps"] = global_container_stack.getProperty( "gradual_infill_steps", "value") print_settings[ "print_sequence"] = global_container_stack.getProperty( "print_sequence", "value") data["print_settings"] = print_settings # Send the name of the output device type that is used. data["output_to"] = type(output_device).__name__ # Convert data to bytes binary_data = json.dumps(data).encode("utf-8") # Sending slice info non-blocking reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() except Exception: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. Logger.logException( "e", "Exception raised while sending slice info." ) # But we should be notified about these problems of course.
def run(self): if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView": self._progress = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1) self._progress.show() Job.yieldThread() if self._abort_requested: if self._progress: self._progress.hide() return Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged) new_node = SceneNode() ## Remove old layer data (if any) for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData(): if node.callDecoration("getLayerData"): self._scene.getRoot().removeChild(node) Job.yieldThread() if self._abort_requested: if self._progress: self._progress.hide() return settings = Application.getInstance().getMachineManager().getWorkingProfile() mesh = MeshData() layer_data = LayerData.LayerData() layer_count = len(self._layers) # Find the minimum layer number # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we # instead simply offset all other layers so the lowest layer is always 0. min_layer_number = 0 for layer in self._layers: if(layer.id < min_layer_number): min_layer_number = layer.id current_layer = 0 for layer in self._layers: abs_layer_number = layer.id + abs(min_layer_number) layer_data.addLayer(abs_layer_number) layer_data.setLayerHeight(abs_layer_number, layer.height) layer_data.setLayerThickness(abs_layer_number, layer.thickness) for p in range(layer.repeatedMessageCount("polygons")): polygon = layer.getRepeatedMessage("polygons", p) points = numpy.fromstring(polygon.points, dtype="i8") # Convert bytearray to numpy array points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. # Create a new 3D-array, copy the 2D points over and insert the right height. # This uses manual array creation + copy rather than numpy.insert since this is # faster. new_points = numpy.empty((len(points), 3), numpy.float32) new_points[:,0] = points[:,0] new_points[:,1] = layer.height new_points[:,2] = -points[:,1] new_points /= 1000 layer_data.addPolygon(abs_layer_number, polygon.type, new_points, polygon.line_width) Job.yieldThread() Job.yieldThread() current_layer += 1 progress = (current_layer / layer_count) * 100 # TODO: Rebuild the layer data mesh once the layer has been processed. # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh. if self._abort_requested: if self._progress: self._progress.hide() return if self._progress: self._progress.setProgress(progress) # We are done processing all the layers we got from the engine, now create a mesh out of the data layer_data.build() if self._abort_requested: if self._progress: self._progress.hide() return # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_data) new_node.addDecorator(decorator) new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot()) # Note: After this we can no longer abort! if not settings.getSettingValue("machine_center_is_zero"): new_node.setPosition(Vector(-settings.getSettingValue("machine_width") / 2, 0.0, settings.getSettingValue("machine_depth") / 2)) if self._progress: self._progress.setProgress(100) view = Application.getInstance().getController().getActiveView() if view.getPluginId() == "LayerView": view.resetLayerData() if self._progress: self._progress.hide() # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed. self._layers = None
def setMachineExtruderCount(self, extruder_count): machine_manager = Application.getInstance().getMachineManager() extruder_manager = ExtruderManager.getInstance() definition_changes_container = self._global_container_stack.findContainer({"type": "definition_changes"}) if not self._global_container_stack or not definition_changes_container: return previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") if extruder_count == previous_extruder_count: return extruder_material_id = None extruder_variant_id = None if extruder_count == 1: # Get the material and variant of the first extruder before setting the number extruders to 1 if machine_manager.hasMaterials: extruder_material_id = machine_manager.allActiveMaterialIds[extruder_manager.extruderIds["0"]] if machine_manager.hasVariants: extruder_variant_id = machine_manager.activeVariantIds[0] # Copy any settable_per_extruder setting value from the extruders to the global stack extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() extruder_stacks.reverse() # make sure the first extruder is done last, so its settings override any higher extruder settings global_user_container = self._global_container_stack.getTop() for extruder_stack in extruder_stacks: extruder_index = extruder_stack.getMetaDataEntry("position") extruder_user_container = extruder_stack.getTop() for setting_instance in extruder_user_container.findInstances(): setting_key = setting_instance.definition.key settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder") if settable_per_extruder: limit_to_extruder = self._global_container_stack.getProperty(setting_key, "limit_to_extruder") if limit_to_extruder == "-1" or limit_to_extruder == extruder_index: global_user_container.setProperty(setting_key, "value", extruder_user_container.getProperty(setting_key, "value")) extruder_user_container.removeInstance(setting_key) # Check to see if any features are set to print with an extruder that will no longer exist for setting_key in ["adhesion_extruder_nr", "support_extruder_nr", "support_extruder_nr_layer_0", "support_infill_extruder_nr", "support_interface_extruder_nr"]: if int(self._global_container_stack.getProperty(setting_key, "value")) > extruder_count - 1: Logger.log("i", "Lowering %s setting to match number of extruders", setting_key) self._global_container_stack.getTop().setProperty(setting_key, "value", extruder_count - 1) # Check to see if any objects are set to print with an extruder that will no longer exist root_node = Application.getInstance().getController().getScene().getRoot() for node in DepthFirstIterator(root_node): if node.getMeshData(): extruder_nr = node.callDecoration("getActiveExtruderPosition") if extruder_nr is not None and int(extruder_nr) > extruder_count - 1: node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId()) definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count) self.forceUpdate() if extruder_count > 1: # Multiextrusion # Make sure one of the extruder stacks is active if extruder_manager.activeExtruderIndex == -1: extruder_manager.setActiveExtruderIndex(0) # Move settable_per_extruder values out of the global container if previous_extruder_count == 1: extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() global_user_container = self._global_container_stack.getTop() for setting_instance in global_user_container.findInstances(): setting_key = setting_instance.definition.key settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder") if settable_per_extruder: limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder")) extruder_stack = extruder_stacks[max(0, limit_to_extruder)] extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) global_user_container.removeInstance(setting_key) else: # Single extrusion # Make sure the machine stack is active if extruder_manager.activeExtruderIndex > -1: extruder_manager.setActiveExtruderIndex(-1) # Restore material and variant on global stack # MachineManager._onGlobalContainerChanged removes the global material and variant of multiextruder machines if extruder_material_id or extruder_variant_id: # Prevent the DiscardOrKeepProfileChangesDialog from popping up (twice) if there are user changes # The dialog is not relevant here, since we're restoring the previous situation as good as possible preferences = Preferences.getInstance() choice_on_profile_override = preferences.getValue("cura/choice_on_profile_override") preferences.setValue("cura/choice_on_profile_override", "always_keep") if extruder_material_id: machine_manager.setActiveMaterial(extruder_material_id) if extruder_variant_id: machine_manager.setActiveVariant(extruder_variant_id) preferences.setValue("cura/choice_on_profile_override", choice_on_profile_override)
def calculateMaxLayers(self) -> None: scene = self.getController().getScene() self._old_max_layers = self._max_layers ## Recalculate num max layers new_max_layers = -1 for node in DepthFirstIterator(scene.getRoot()): # type: ignore layer_data = node.callDecoration("getLayerData") if not layer_data: continue self.setActivity(True) min_layer_number = sys.maxsize max_layer_number = -sys.maxsize for layer_id in layer_data.getLayers(): # If a layer doesn't contain any polygons, skip it (for infill meshes taller than print objects if len(layer_data.getLayer(layer_id).polygons) < 1: continue # Store the max and min feedrates and thicknesses for display purposes for p in layer_data.getLayer(layer_id).polygons: self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate) self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate) self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness) try: self._min_thickness = min( float(p.lineThicknesses[numpy.nonzero( p.lineThicknesses)].min()), self._min_thickness) except ValueError: # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding # the zero) can't be calculated Logger.log( "i", "Min thickness can't be calculated because all the values are zero" ) if max_layer_number < layer_id: max_layer_number = layer_id if min_layer_number > layer_id: min_layer_number = layer_id layer_count = max_layer_number - min_layer_number if new_max_layers < layer_count: new_max_layers = layer_count if new_max_layers >= 0 and new_max_layers != self._old_max_layers: self._max_layers = new_max_layers # The qt slider has a bit of weird behavior that if the maxvalue needs to be changed first # if it's the largest value. If we don't do this, we can have a slider block outside of the # slider. if new_max_layers > self._current_layer_num: self.maxLayersChanged.emit() self.setLayer(int(self._max_layers)) else: self.setLayer(int(self._max_layers)) self.maxLayersChanged.emit() self._startUpdateTopLayers()
def _onStartSliceCompleted(self, job: StartSliceJob) -> None: if self._error_message: self._error_message.hide() # Note that cancelled slice jobs can still call this method. if self._start_slice_job is job: self._start_slice_job = None if job.isCancelled() or job.getError() or job.getResult( ) == StartJobResult.Error: self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) return if job.getResult() == StartJobResult.MaterialIncompatible: if self._application.platformActivity: self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice with the current material as it is incompatible with the selected machine or configuration." ), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) else: self.backendStateChange.emit(BackendState.NotStarted) return if job.getResult() == StartJobResult.SettingError: if self._application.platformActivity: if not self._global_container_stack: Logger.log( "w", "Global container stack not assigned to CuraEngineBackend!" ) return extruders = list( ExtruderManager.getInstance().getMachineExtruders( self._global_container_stack.getId())) error_keys = [] #type: List[str] for extruder in extruders: error_keys.extend(extruder.getErrorKeys()) if not extruders: error_keys = self._global_container_stack.getErrorKeys() error_labels = set() for key in error_keys: for stack in [ self._global_container_stack ] + extruders: #Search all container stacks for the definition of this setting. Some are only in an extruder stack. definitions = cast( DefinitionContainerInterface, stack.getBottom()).findDefinitions(key=key) if definitions: break #Found it! No need to continue search. else: #No stack has a definition for this setting. Logger.log( "w", "When checking settings for errors, unable to find definition for key: {key}" .format(key=key)) continue error_labels.add(definitions[0].label) self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice with the current settings. The following settings have errors: {0}" ).format(", ".join(error_labels)), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) else: self.backendStateChange.emit(BackendState.NotStarted) return elif job.getResult() == StartJobResult.ObjectSettingError: errors = {} for node in DepthFirstIterator( self._application.getController().getScene().getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. stack = node.callDecoration("getStack") if not stack: continue for key in stack.getErrorKeys(): if not self._global_container_stack: Logger.log( "e", "CuraEngineBackend does not have global_container_stack assigned." ) continue definition = cast(DefinitionContainerInterface, self._global_container_stack.getBottom() ).findDefinitions(key=key) if not definition: Logger.log( "e", "When checking settings for errors, unable to find definition for key {key} in per-object stack." .format(key=key)) continue errors[key] = definition[0].label self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}" ).format(error_labels=", ".join(errors.values())), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) return if job.getResult() == StartJobResult.BuildPlateError: if self._application.platformActivity: self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice because the prime tower or prime position(s) are invalid." ), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) else: self.backendStateChange.emit(BackendState.NotStarted) if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder: self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice because there are objects associated with disabled Extruder %s." % job.getMessage()), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) return if job.getResult() == StartJobResult.NothingToSlice: if self._application.platformActivity: self._error_message = Message(catalog.i18nc( "@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit." ), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) else: self.backendStateChange.emit(BackendState.NotStarted) self._invokeSlice() return # Preparation completed, send it to the backend. self._socket.sendMessage(job.getSliceMessage()) # Notify the user that it's now up to the backend to do it's job self.backendStateChange.emit(BackendState.Processing) if self._slice_start_time: Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time)
def _clearLayerData(self, build_plate_numbers=set()): for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): if not build_plate_numbers or node.callDecoration( "getBuildPlateNumber") in build_plate_numbers: node.getParent().removeChild(node)
def clearFaces(self): for bc_node in DepthFirstIterator(self): if isinstance(bc_node, HighlightFace): self.removeFace(bc_node)
def _update(self, *args) -> None: nodes = [] filter_current_build_plate = Application.getInstance().getPreferences( ).getValue("view/filter_current_build_plate") active_build_plate_number = self._build_plate_number group_nr = 1 name_count_dict = defaultdict(int) # type: Dict[str, int] for node in DepthFirstIterator(Application.getInstance().getController( ).getScene().getRoot()): # type: ignore if not isinstance(node, SceneNode): continue if (not node.getMeshData() and not node.callDecoration("getLayerData") ) and not node.callDecoration("isGroup"): continue parent = node.getParent() if parent and parent.callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) if not node.callDecoration( "isSliceable") and not node.callDecoration("isGroup"): continue node_build_plate_number = node.callDecoration( "getBuildPlateNumber") if filter_current_build_plate and node_build_plate_number != active_build_plate_number: continue if not node.callDecoration("isGroup"): name = node.getName() else: name = catalog.i18nc( "@label", "Group #{group_nr}").format(group_nr=str(group_nr)) group_nr += 1 if hasattr(node, "isOutsideBuildArea"): is_outside_build_area = node.isOutsideBuildArea( ) # type: ignore else: is_outside_build_area = False #check if we already have an instance of the object based on name name_count_dict[name] += 1 name_count = name_count_dict[name] if name_count > 1: name = "{0}({1})".format(name, name_count - 1) node.setName(name) nodes.append({ "name": name, "isSelected": Selection.isSelected(node), "isOutsideBuildArea": is_outside_build_area, "buildPlateNumber": node_build_plate_number, "node": node }) nodes = sorted(nodes, key=lambda n: n["name"]) self.setItems(nodes) self.itemsChanged.emit()
def render(self): if not self._layer_shader: if self._compatibility_mode: shader_filename = "layers.shader" shadow_shader_filename = "layers_shadow.shader" else: shader_filename = "layers3d.shader" shadow_shader_filename = "layers3d_shadow.shader" self._layer_shader = OpenGL.getInstance().createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath( "SimulationView"), shader_filename)) self._layer_shadow_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath( "SimulationView"), shadow_shader_filename)) self._current_shader = self._layer_shader # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers) self._layer_shader.setUniformValue( "u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex))) if self._layer_view: self._layer_shader.setUniformValue( "u_max_feedrate", self._layer_view.getMaxFeedrate()) self._layer_shader.setUniformValue( "u_min_feedrate", self._layer_view.getMinFeedrate()) self._layer_shader.setUniformValue( "u_max_thickness", self._layer_view.getMaxThickness()) self._layer_shader.setUniformValue( "u_min_thickness", self._layer_view.getMinThickness()) self._layer_shader.setUniformValue( "u_layer_view_type", self._layer_view.getSimulationViewType()) self._layer_shader.setUniformValue( "u_extruder_opacity", self._layer_view.getExtruderOpacities()) self._layer_shader.setUniformValue( "u_show_travel_moves", self._layer_view.getShowTravelMoves()) self._layer_shader.setUniformValue( "u_show_helpers", self._layer_view.getShowHelpers()) self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin()) self._layer_shader.setUniformValue( "u_show_infill", self._layer_view.getShowInfill()) else: #defaults self._layer_shader.setUniformValue("u_max_feedrate", 1) self._layer_shader.setUniformValue("u_min_feedrate", 0) self._layer_shader.setUniformValue("u_max_thickness", 1) self._layer_shader.setUniformValue("u_min_thickness", 0) self._layer_shader.setUniformValue("u_layer_view_type", 1) self._layer_shader.setUniformValue( "u_extruder_opacity", [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]) self._layer_shader.setUniformValue("u_show_travel_moves", 0) self._layer_shader.setUniformValue("u_show_helpers", 1) self._layer_shader.setUniformValue("u_show_skin", 1) self._layer_shader.setUniformValue("u_show_infill", 1) if not self._tool_handle_shader: self._tool_handle_shader = OpenGL.getInstance( ).createShaderProgram( Resources.getPath(Resources.Shaders, "toolhandle.shader")) if not self._nozzle_shader: self._nozzle_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "color.shader")) self._nozzle_shader.setUniformValue( "u_color", Color(*Application.getInstance().getTheme().getColor( "layerview_nozzle").getRgb())) self.bind() tool_handle_batch = RenderBatch(self._tool_handle_shader, type=RenderBatch.RenderType.Overlay, backface_cull=True) head_position = None # Indicates the current position of the print head nozzle_node = None for node in DepthFirstIterator(self._scene.getRoot()): if isinstance(node, ToolHandle): tool_handle_batch.addItem(node.getWorldTransformation(), mesh=node.getSolidMesh()) elif isinstance(node, NozzleNode): nozzle_node = node nozzle_node.setVisible(False) elif isinstance(node, SceneNode) and (node.getMeshData( ) or node.callDecoration("isBlockSlicing")) and node.isVisible(): layer_data = node.callDecoration("getLayerData") if not layer_data: continue # Render all layers below a certain number as line mesh instead of vertices. if self._layer_view._current_layer_num > -1 and ( (not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())): start = 0 end = 0 element_counts = layer_data.getElementCounts() for layer in sorted(element_counts.keys()): # In the current layer, we show just the indicated paths if layer == self._layer_view._current_layer_num: # We look for the position of the head, searching the point of the current path index = self._layer_view._current_path_num offset = 0 for polygon in layer_data.getLayer(layer).polygons: # The size indicates all values in the two-dimension array, and the second dimension is # always size 3 because we have 3D points. if index >= polygon.data.size // 3 - offset: index -= polygon.data.size // 3 - offset offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon continue # The head position is calculated and translated head_position = Vector( polygon.data[index + offset][0], polygon.data[index + offset][1], polygon.data[index + offset] [2]) + node.getWorldPosition() break break if self._layer_view._minimum_layer_num > layer: start += element_counts[layer] end += element_counts[layer] # Calculate the range of paths in the last layer current_layer_start = end current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice # This uses glDrawRangeElements internally to only draw a certain range of lines. # All the layers but the current selected layer are rendered first if self._old_current_path != self._layer_view._current_path_num: self._current_shader = self._layer_shadow_shader self._switching_layers = False if not self._layer_view.isSimulationRunning( ) and self._old_current_layer != self._layer_view._current_layer_num: self._current_shader = self._layer_shader self._switching_layers = True layers_batch = RenderBatch( self._current_shader, type=RenderBatch.RenderType.Solid, mode=RenderBatch.RenderMode.Lines, range=(start, end), backface_cull=True) layers_batch.addItem(node.getWorldTransformation(), layer_data) layers_batch.render(self._scene.getActiveCamera()) # Current selected layer is rendered current_layer_batch = RenderBatch( self._layer_shader, type=RenderBatch.RenderType.Solid, mode=RenderBatch.RenderMode.Lines, range=(current_layer_start, current_layer_end)) current_layer_batch.addItem(node.getWorldTransformation(), layer_data) current_layer_batch.render(self._scene.getActiveCamera()) self._old_current_layer = self._layer_view._current_layer_num self._old_current_path = self._layer_view._current_path_num # Create a new batch that is not range-limited batch = RenderBatch(self._layer_shader, type=RenderBatch.RenderType.Solid) if self._layer_view.getCurrentLayerMesh(): batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh()) if self._layer_view.getCurrentLayerJumps(): batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps()) if len(batch.items) > 0: batch.render(self._scene.getActiveCamera()) # The nozzle is drawn when once we know the correct position of the head, # but the user is not using the layer slider, and the compatibility mode is not enabled if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity( ) and nozzle_node is not None: if head_position is not None: nozzle_node.setVisible(True) nozzle_node.setPosition(head_position) nozzle_batch = RenderBatch( self._nozzle_shader, type=RenderBatch.RenderType.Transparent) nozzle_batch.addItem(nozzle_node.getWorldTransformation(), mesh=nozzle_node.getMeshData()) nozzle_batch.render(self._scene.getActiveCamera()) # Render toolhandles on top of the layerview if len(tool_handle_batch.items) > 0: tool_handle_batch.render(self._scene.getActiveCamera()) self.release()
def sliceableNodes(self): # Add all sliceable scene nodes to check scene = Application.getInstance().getController().getScene() for node in DepthFirstIterator(scene.getRoot()): if node.callDecoration("isSliceable"): yield node
def calculateColorSchemeLimits(self) -> None: """ Calculates the limits of the colour schemes, depending on the layer view data that is visible to the user. """ # Before we start, save the old values so that we can tell if any of the spectrums need to change. old_min_feedrate = self._min_feedrate old_max_feedrate = self._max_feedrate old_min_linewidth = self._min_line_width old_max_linewidth = self._max_line_width old_min_thickness = self._min_thickness old_max_thickness = self._max_thickness self._min_feedrate = sys.float_info.max self._max_feedrate = sys.float_info.min self._min_line_width = sys.float_info.max self._max_line_width = sys.float_info.min self._min_thickness = sys.float_info.max self._max_thickness = sys.float_info.min # The colour scheme is only influenced by the visible lines, so filter the lines by if they should be visible. visible_line_types = [] if self.getShowSkin(): # Actually "shell". visible_line_types.append(LayerPolygon.SkinType) visible_line_types.append(LayerPolygon.Inset0Type) visible_line_types.append(LayerPolygon.InsetXType) if self.getShowInfill(): visible_line_types.append(LayerPolygon.InfillType) if self.getShowHelpers(): visible_line_types.append(LayerPolygon.PrimeTowerType) visible_line_types.append(LayerPolygon.SkirtType) visible_line_types.append(LayerPolygon.SupportType) visible_line_types.append(LayerPolygon.SupportInfillType) visible_line_types.append(LayerPolygon.SupportInterfaceType) if self.getShowTravelMoves(): visible_line_types.append(LayerPolygon.MoveCombingType) visible_line_types.append(LayerPolygon.MoveRetractionType) for node in DepthFirstIterator(self.getController().getScene().getRoot()): layer_data = node.callDecoration("getLayerData") if not layer_data: continue for layer_index in layer_data.getLayers(): for polyline in layer_data.getLayer(layer_index).polygons: is_visible = numpy.isin(polyline.types, visible_line_types) visible_indices = numpy.where(is_visible)[0] if visible_indices.size == 0: # No items to take maximum or minimum of. continue visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices) visible_linewidths = numpy.take(polyline.lineWidths, visible_indices) visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices) self._max_feedrate = max(float(visible_feedrates.max()), self._max_feedrate) self._min_feedrate = min(float(visible_feedrates.min()), self._min_feedrate) self._max_line_width = max(float(visible_linewidths.max()), self._max_line_width) self._min_line_width = min(float(visible_linewidths.min()), self._min_line_width) self._max_thickness = max(float(visible_thicknesses.max()), self._max_thickness) try: self._min_thickness = min(float(visible_thicknesses[numpy.nonzero(visible_thicknesses)].min()), self._min_thickness) except ValueError: # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding the zero) can't be calculated. Logger.log("i", "Min thickness can't be calculated because all the values are zero") if old_min_feedrate != self._min_feedrate or old_max_feedrate != self._max_feedrate \ or old_min_linewidth != self._min_line_width or old_max_linewidth != self._max_line_width \ or old_min_thickness != self._min_thickness or old_max_thickness != self._max_thickness: self.colorSchemeLimitsChanged.emit()
def run(self): stack = Application.getInstance().getGlobalContainerStack() if not stack: self.setResult(False) 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: return self._buildGlobalSettingsMessage(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(True)
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 if Application.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.SettingError) return for extruder_stack in cura.Settings.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 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) # 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) 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): start_time = time() if Application.getInstance().getController().getActiveView( ).getPluginId() == "LayerView": self._progress_message.show() Job.yieldThread() if self._abort_requested: if self._progress_message: self._progress_message.hide() return Application.getInstance().getController().activeViewChanged.connect( self._onActiveViewChanged) new_node = SceneNode() ## Remove old layer data (if any) for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): node.getParent().removeChild(node) break if self._abort_requested: if self._progress_message: self._progress_message.hide() return # Force garbage collection. # For some reason, Python has a tendency to keep the layer data # in memory longer than needed. Forcing the GC to run here makes # sure any old layer data is really cleaned up before adding new. gc.collect() mesh = MeshData() layer_data = LayerDataBuilder.LayerDataBuilder() layer_count = len(self._layers) # Find the minimum layer number # When using a raft, the raft layers are sent as layers < 0. Instead of allowing layers < 0, we # instead simply offset all other layers so the lowest layer is always 0. min_layer_number = 0 for layer in self._layers: if layer.id < min_layer_number: min_layer_number = layer.id current_layer = 0 for layer in self._layers: abs_layer_number = layer.id + abs(min_layer_number) layer_data.addLayer(abs_layer_number) this_layer = layer_data.getLayer(abs_layer_number) layer_data.setLayerHeight(abs_layer_number, layer.height) for p in range(layer.repeatedMessageCount("path_segment")): polygon = layer.getRepeatedMessage("path_segment", p) extruder = polygon.extruder line_types = numpy.fromstring( polygon.line_type, dtype="u1") # Convert bytearray to numpy array line_types = line_types.reshape((-1, 1)) points = numpy.fromstring( polygon.points, dtype="f4") # Convert bytearray to numpy array if polygon.point_type == 0: # Point2D points = points.reshape( (-1, 2) ) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. else: # Point3D points = points.reshape((-1, 3)) line_widths = numpy.fromstring( polygon.line_width, dtype="f4") # Convert bytearray to numpy array line_widths = line_widths.reshape( (-1, 1) ) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly. # In the future, line_thicknesses should be given by CuraEngine as well. # Currently the infill layer thickness also translates to line width line_thicknesses = numpy.zeros(line_widths.shape, dtype="f4") line_thicknesses[:] = layer.thickness / 1000 # from micrometer to millimeter # Create a new 3D-array, copy the 2D points over and insert the right height. # This uses manual array creation + copy rather than numpy.insert since this is # faster. new_points = numpy.empty((len(points), 3), numpy.float32) if polygon.point_type == 0: # Point2D new_points[:, 0] = points[:, 0] new_points[:, 1] = layer.height / 1000 # layer height value is in backend representation new_points[:, 2] = -points[:, 1] else: # Point3D new_points[:, 0] = points[:, 0] new_points[:, 1] = points[:, 2] new_points[:, 2] = -points[:, 1] this_poly = LayerPolygon.LayerPolygon(extruder, line_types, new_points, line_widths, line_thicknesses) this_poly.buildCache() this_layer.polygons.append(this_poly) Job.yieldThread() Job.yieldThread() current_layer += 1 progress = (current_layer / layer_count) * 99 # TODO: Rebuild the layer data mesh once the layer has been processed. # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh. if self._abort_requested: if self._progress_message: self._progress_message.hide() return if self._progress_message: self._progress_message.setProgress(progress) # We are done processing all the layers we got from the engine, now create a mesh out of the data # Find out colors per extruder global_container_stack = Application.getInstance( ).getGlobalContainerStack() manager = ExtruderManager.getInstance() extruders = list( manager.getMachineExtruders(global_container_stack.getId())) if extruders: material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32) for extruder in extruders: position = int( extruder.getMetaDataEntry("position", default="0")) # Get the position try: default_color = ExtrudersModel.defaultColors[position] except IndexError: default_color = "#e0e000" color_code = extruder.material.getMetaDataEntry( "color_code", default=default_color) color = colorCodeToRGBA(color_code) material_color_map[position, :] = color else: # Single extruder via global stack. material_color_map = numpy.zeros((1, 4), dtype=numpy.float32) color_code = global_container_stack.material.getMetaDataEntry( "color_code", default="#e0e000") color = colorCodeToRGBA(color_code) material_color_map[0, :] = color # We have to scale the colors for compatibility mode if OpenGLContext.isLegacyOpenGL() or bool(Preferences.getInstance( ).getValue("view/force_layer_view_compatibility_mode")): line_type_brightness = 0.5 # for compatibility mode else: line_type_brightness = 1.0 layer_mesh = layer_data.build(material_color_map, line_type_brightness) if self._abort_requested: if self._progress_message: self._progress_message.hide() return # Add LayerDataDecorator to scene node to indicate that the node has layer data decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_mesh) new_node.addDecorator(decorator) new_node.setMeshData(mesh) # Set build volume as parent, the build volume can move as a result of raft settings. # It makes sense to set the build volume as parent: the print is actually printed on it. new_node_parent = Application.getInstance().getBuildVolume() new_node.setParent( new_node_parent) # Note: After this we can no longer abort! settings = Application.getInstance().getGlobalContainerStack() if not settings.getProperty("machine_center_is_zero", "value"): new_node.setPosition( Vector(-settings.getProperty("machine_width", "value") / 2, 0.0, settings.getProperty("machine_depth", "value") / 2)) if self._progress_message: self._progress_message.setProgress(100) view = Application.getInstance().getController().getActiveView() if view.getPluginId() == "LayerView": view.resetLayerData() if self._progress_message: self._progress_message.hide() # Clear the unparsed layers. This saves us a bunch of memory if the Job does not get destroyed. self._layers = None Logger.log("d", "Processing layers took %s seconds", time() - start_time)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() self._checkSetup() global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if Application.getInstance().getPreferences().getValue( "view/show_overhang"): # Make sure the overhang angle is valid before passing it to the shader if self._support_angle is not None and self._support_angle >= 0 and self._support_angle <= 90: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - self._support_angle))) else: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(0)) ) #Overhang angle of 0 causes no area at all to be marked as overhang. else: self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible( ) and not node.callDecoration("getLayerData"): uniforms = {} shade_factor = 1.0 per_mesh_stack = node.callDecoration("getStack") extruder_index = node.callDecoration( "getActiveExtruderPosition") if extruder_index is None: extruder_index = "0" extruder_index = int(extruder_index) # Use the support extruder instead of the active extruder if this is a support_mesh if per_mesh_stack: if per_mesh_stack.getProperty("support_mesh", "value"): extruder_index = int( global_container_stack. getExtruderPositionValueWithDefault( "support_extruder_nr")) try: material_color = self._extruders_model.getItem( extruder_index)["color"] except KeyError: material_color = self._extruders_model.defaultColors[0] if extruder_index != ExtruderManager.getInstance( ).activeExtruderIndex: # Shade objects that are printed with the non-active extruder 25% darker shade_factor = 0.6 try: # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) uniforms["diffuse_color"] = [ shade_factor * int(material_color[1:3], 16) / 255, shade_factor * int(material_color[3:5], 16) / 255, shade_factor * int(material_color[5:7], 16) / 255, 1.0 ] # Color the currently selected face-id. (Disable for now.) #face = Selection.getHoverFace() uniforms[ "hover_face"] = -1 #if not face or node != face[0] else face[1] except ValueError: pass if node.callDecoration("isNonPrintingMesh"): if per_mesh_stack and (per_mesh_stack.getProperty( "infill_mesh", "value") or per_mesh_stack.getProperty( "cutting_mesh", "value")): renderer.queueNode( node, shader=self._non_printing_shader, uniforms=uniforms, transparent=True) else: renderer.queueNode( node, shader=self._non_printing_shader, transparent=True) elif getattr(node, "_outside_buildarea", False): renderer.queueNode(node, shader=self._disabled_shader) elif per_mesh_stack and per_mesh_stack.getProperty( "support_mesh", "value"): # Render support meshes with a vertical stripe that is darker shade_factor = 0.6 uniforms["diffuse_color_2"] = [ uniforms["diffuse_color"][0] * shade_factor, uniforms["diffuse_color"][1] * shade_factor, uniforms["diffuse_color"][2] * shade_factor, 1.0 ] renderer.queueNode(node, shader=self._support_mesh_shader, uniforms=uniforms) else: renderer.queueNode(node, shader=self._enabled_shader, uniforms=uniforms) if node.callDecoration("isGroup") and Selection.isSelected( node): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=RenderBatch.RenderMode.LineLoop)
def getUsedExtruderStacks(self) -> List["ContainerStack"]: global_stack = Application.getInstance().getGlobalContainerStack() container_registry = ContainerRegistry.getInstance() used_extruder_stack_ids = set() # Get the extruders of all meshes in the scene support_enabled = False support_bottom_enabled = False support_roof_enabled = False scene_root = Application.getInstance().getController().getScene( ).getRoot() # If no extruders are registered in the extruder manager yet, return an empty array if len(self.extruderIds) == 0: return [] # Get the extruders of all printable meshes in the scene meshes = [ node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable() ] for mesh in meshes: extruder_stack_id = mesh.callDecoration("getActiveExtruder") if not extruder_stack_id: # No per-object settings for this node extruder_stack_id = self.extruderIds["0"] used_extruder_stack_ids.add(extruder_stack_id) # Get whether any of them use support. stack_to_use = mesh.callDecoration( "getStack") # if there is a per-mesh stack, we use it if not stack_to_use: # if there is no per-mesh stack, we use the build extruder for this mesh stack_to_use = container_registry.findContainerStacks( id=extruder_stack_id)[0] support_enabled |= stack_to_use.getProperty( "support_enable", "value") support_bottom_enabled |= stack_to_use.getProperty( "support_bottom_enable", "value") support_roof_enabled |= stack_to_use.getProperty( "support_roof_enable", "value") # Check limit to extruders limit_to_extruder_feature_list = [ "wall_0_extruder_nr", "wall_x_extruder_nr", "roofing_extruder_nr", "top_bottom_extruder_nr", "infill_extruder_nr", ] for extruder_nr_feature_name in limit_to_extruder_feature_list: extruder_nr = int( global_stack.getProperty(extruder_nr_feature_name, "value")) if extruder_nr == -1: continue used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)]) # Check support extruders if support_enabled: used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_infill_extruder_nr", "value"))]) used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_extruder_nr_layer_0", "value"))]) if support_bottom_enabled: used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_bottom_extruder_nr", "value"))]) if support_roof_enabled: used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("support_roof_extruder_nr", "value"))]) # The platform adhesion extruder. Not used if using none. if global_stack.getProperty("adhesion_type", "value") != "none": used_extruder_stack_ids.add(self.extruderIds[str( global_stack.getProperty("adhesion_extruder_nr", "value"))]) try: return [ container_registry.findContainerStacks(id=stack_id)[0] for stack_id in used_extruder_stack_ids ] except IndexError: # One or more of the extruders was not found. Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids) return []
def _clearLayerData(self): for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData"): node.getParent().removeChild(node) break
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 # Don't slice if the buildplate or the nozzle type is incompatible with the materials if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \ not Application.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()): 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()): 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 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 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) extruders_enabled = { position: stack.isEnabled for position, stack in Application.getInstance(). getGlobalContainerStack().extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = Application.getInstance().getGlobalContainerStack() skip_group = False for node in group: extruder_position = node.callDecoration( "getActiveExtruderPosition") if 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: """Runs the job that initiates the slicing.""" 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 # Wait for error checker to be done. while CuraApplication.getInstance().getMachineErrorChecker( ).needToWaitForResult: time.sleep(0.1) if CuraApplication.getInstance().getMachineErrorChecker().hasError: self.setResult(StartJobResult.SettingError) 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 extruder_stack in stack.extruderList: 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()): if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return # Remove old layer data. for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData") and node.callDecoration( "getBuildPlateNumber") == self._build_plate_number: # Singe we walk through all nodes in the scene, they always have a parent. cast(SceneNode, 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: mesh_data = child_node.getMeshData() if mesh_data and mesh_data.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()): mesh_data = node.getMeshData() if node.callDecoration( "isSliceable") and mesh_data and mesh_data.getVertices( ) is not None: is_non_printing_mesh = bool( node.callDecoration("isNonPrintingMesh")) # 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 = [ stack.isEnabled for stack in global_stack.extruderList ] 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 = int( 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 = { p + 1 for p in associated_disabled_extruders } self.setMessage(", ".join( map(str, sorted(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 global_stack.extruderList: self._buildExtruderMessage(extruder_stack) for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") parent = group[0].getParent() if parent is not None and parent.callDecoration("isGroup"): self._handlePerObjectSettings(cast(CuraSceneNode, parent), group_message) for object in group: mesh_data = object.getMeshData() if mesh_data is None: continue rot_scale = object.getWorldTransformation().getTransposed( ).getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] if global_stack.getProperty("adhesion_type", "value") == "raft": # Add at least support interface thickness so that # the part does not fuse into the raft # bounding_box_bottom is just distance from bottom of object to bed # we need it to be at least support_roof_height to fit in one layer of # support interface bounding_box_bottom = object.getBoundingBox().bottom support_roof_height = global_stack.getProperty( "support_roof_height", "value") if bounding_box_bottom < support_roof_height: difference_needed = support_roof_height - bounding_box_bottom translate[1] += difference_needed # 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) 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 self._handlePerObjectSettings(cast(CuraSceneNode, object), obj) Job.yieldThread() self.setResult(StartJobResult.Finished)
def setMachineExtruderCount(self, extruder_count): extruder_manager = Application.getInstance().getExtruderManager() definition_changes_container = self._global_container_stack.definitionChanges if not self._global_container_stack or definition_changes_container == self._empty_container: return previous_extruder_count = self._global_container_stack.getProperty( "machine_extruder_count", "value") if extruder_count == previous_extruder_count: return # reset all extruder number settings whose value is no longer valid for setting_instance in self._global_container_stack.userChanges.findInstances( ): setting_key = setting_instance.definition.key if not self._global_container_stack.getProperty( setting_key, "type") in ("extruder", "optional_extruder"): continue old_value = int( self._global_container_stack.userChanges.getProperty( setting_key, "value")) if old_value >= extruder_count: self._global_container_stack.userChanges.removeInstance( setting_key) Logger.log( "d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value) # Check to see if any objects are set to print with an extruder that will no longer exist root_node = Application.getInstance().getController().getScene( ).getRoot() for node in DepthFirstIterator(root_node): if node.getMeshData(): extruder_nr = node.callDecoration("getActiveExtruderPosition") if extruder_nr is not None and int( extruder_nr) > extruder_count - 1: node.callDecoration( "setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId()) definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count) # Make sure one of the extruder stacks is active extruder_manager.setActiveExtruderIndex(0) # Move settable_per_extruder values out of the global container # After CURA-4482 this should not be the case anymore, but we still want to support older project files. global_user_container = self._global_container_stack.getTop() if previous_extruder_count == 1: extruder_stacks = ExtruderManager.getInstance( ).getActiveExtruderStacks() global_user_container = self._global_container_stack.getTop() for setting_instance in global_user_container.findInstances(): setting_key = setting_instance.definition.key settable_per_extruder = self._global_container_stack.getProperty( setting_key, "settable_per_extruder") if settable_per_extruder: limit_to_extruder = int( self._global_container_stack.getProperty( setting_key, "limit_to_extruder")) extruder_stack = extruder_stacks[max(0, limit_to_extruder)] extruder_stack.getTop().setProperty( setting_key, "value", global_user_container.getProperty(setting_key, "value")) global_user_container.removeInstance(setting_key) self.forceUpdate()
def run(self): self._scene.acquireLock() 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) self._scene.releaseLock() if not object_groups: return self._sendSettings(self._profile) slice_message = self._socket.createMessage("cura.proto.Slice") for group in object_groups: group_message = slice_message.addRepeatedMessage("object_lists") if group[0].getParent().callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) for current_object in group: mesh_data = current_object.getMeshData().getTransformed( current_object.getWorldTransformation()) obj = group_message.addRepeatedMessage("objects") obj.id = id(current_object) verts = numpy.array(mesh_data.getVertices()) verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj.vertices = verts self._handlePerObjectSettings(current_object, obj) Job.yieldThread() Logger.log("d", "Sending data to engine for slicing.") self._socket.sendMessage(slice_message) Logger.log("d", "Sending data to engine is completed") self.setResult(True)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "overhang.shader")) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram( Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue("u_diffuseColor1", [0.48, 0.48, 0.48, 1.0]) self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) self._disabled_shader.setUniformValue("u_width", 50.0) multi_extrusion = False global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if Preferences.getInstance().getValue("view/show_overhang"): angle = global_container_stack.getProperty( "support_angle", "value") # Make sure the overhang angle is valid before passing it to the shader # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None) if angle is not None and global_container_stack.getProperty( "support_angle", "validationState") in [None, ValidatorState.Valid]: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(90 - angle))) else: self._enabled_shader.setUniformValue( "u_overhangAngle", math.cos(math.radians(0)) ) #Overhang angle of 0 causes no area at all to be marked as overhang. else: self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): if node.getMeshData() and node.isVisible(): uniforms = {} if not multi_extrusion: if global_container_stack: material = global_container_stack.findContainer( {"type": "material"}) material_color = material.getMetaDataEntry( "color_code", default=self._extruders_model.defaultColors[0] ) if material else self._extruders_model.defaultColors[ 0] else: material_color = self._extruders_model.defaultColors[ 0] else: # Get color to render this mesh in from ExtrudersModel extruder_index = 0 extruder_id = node.callDecoration("getActiveExtruder") if extruder_id: extruder_index = max( 0, self._extruders_model.find("id", extruder_id)) material_color = self._extruders_model.getItem( extruder_index)["color"] try: # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0]) uniforms["diffuse_color"] = [ int(material_color[1:3], 16) / 255, int(material_color[3:5], 16) / 255, int(material_color[5:7], 16) / 255, 1.0 ] except ValueError: pass if hasattr(node, "_outside_buildarea"): if node._outside_buildarea: renderer.queueNode(node, shader=self._disabled_shader) else: renderer.queueNode(node, shader=self._enabled_shader, uniforms=uniforms) else: renderer.queueNode(node, material=self._enabled_shader, uniforms=uniforms) if node.callDecoration("isGroup") and Selection.isSelected( node): renderer.queueNode(scene.getRoot(), mesh=node.getBoundingBoxMesh(), mode=Renderer.RenderLines)
def getUsedExtruderStacks(self) -> List["ExtruderStack"]: global_stack = self._application.getGlobalContainerStack() container_registry = ContainerRegistry.getInstance() used_extruder_stack_ids = set() # Get the extruders of all meshes in the scene support_enabled = False support_bottom_enabled = False support_roof_enabled = False scene_root = self._application.getController().getScene().getRoot() # If no extruders are registered in the extruder manager yet, return an empty array if len(self.extruderIds) == 0: return [] # Get the extruders of all printable meshes in the scene meshes = [ node for node in DepthFirstIterator(scene_root) if isinstance(node, SceneNode) and node.isSelectable() ] #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. # Exclude anti-overhang meshes mesh_list = [] for mesh in meshes: stack = mesh.callDecoration("getStack") if stack is not None and ( stack.getProperty("anti_overhang_mesh", "value") or stack.getProperty("support_mesh", "value")): continue mesh_list.append(mesh) for mesh in mesh_list: extruder_stack_id = mesh.callDecoration("getActiveExtruder") if not extruder_stack_id: # No per-object settings for this node extruder_stack_id = self.extruderIds["0"] used_extruder_stack_ids.add(extruder_stack_id) # Get whether any of them use support. stack_to_use = mesh.callDecoration( "getStack") # if there is a per-mesh stack, we use it if not stack_to_use: # if there is no per-mesh stack, we use the build extruder for this mesh stack_to_use = container_registry.findContainerStacks( id=extruder_stack_id)[0] support_enabled |= stack_to_use.getProperty( "support_enable", "value") support_bottom_enabled |= stack_to_use.getProperty( "support_bottom_enable", "value") support_roof_enabled |= stack_to_use.getProperty( "support_roof_enable", "value") # Check limit to extruders limit_to_extruder_feature_list = [ "wall_0_extruder_nr", "wall_x_extruder_nr", "roofing_extruder_nr", "top_bottom_extruder_nr", "infill_extruder_nr", ] for extruder_nr_feature_name in limit_to_extruder_feature_list: extruder_nr = int( global_stack.getProperty(extruder_nr_feature_name, "value")) if extruder_nr == -1: continue if str(extruder_nr) not in self.extruderIds: extruder_nr = int(self._application.getMachineManager(). defaultExtruderPosition) used_extruder_stack_ids.add(self.extruderIds[str(extruder_nr)]) # Check support extruders if support_enabled: used_extruder_stack_ids.add( self.extruderIds[self.extruderValueWithDefault( str( global_stack.getProperty("support_infill_extruder_nr", "value")))]) used_extruder_stack_ids.add( self.extruderIds[self.extruderValueWithDefault( str( global_stack.getProperty("support_extruder_nr_layer_0", "value")))]) if support_bottom_enabled: used_extruder_stack_ids.add( self.extruderIds[self.extruderValueWithDefault( str( global_stack.getProperty( "support_bottom_extruder_nr", "value")))]) if support_roof_enabled: used_extruder_stack_ids.add( self.extruderIds[self.extruderValueWithDefault( str( global_stack.getProperty( "support_roof_extruder_nr", "value")))]) # The platform adhesion extruder. Not used if using none. if global_stack.getProperty("adhesion_type", "value") != "none" or ( global_stack.getProperty("prime_tower_brim_enable", "value") and global_stack.getProperty("adhesion_type", "value") != 'raft'): extruder_str_nr = str( global_stack.getProperty("adhesion_extruder_nr", "value")) if extruder_str_nr == "-1": extruder_str_nr = self._application.getMachineManager( ).defaultExtruderPosition if extruder_str_nr in self.extruderIds: used_extruder_stack_ids.add(self.extruderIds[extruder_str_nr]) try: return [ container_registry.findContainerStacks(id=stack_id)[0] for stack_id in used_extruder_stack_ids ] except IndexError: # One or more of the extruders was not found. Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids) return []
def _onWriteStarted(self, output_device): try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data # Listing all files placed on the buildplate modelhashes = [] for node in DepthFirstIterator(CuraApplication.getInstance( ).getController().getScene().getRoot()): if node.callDecoration("isSliceable"): modelhashes.append(node.getMeshData().getHash()) # Creating md5sums and formatting them as discussed on JIRA modelhash_formatted = ",".join(modelhashes) global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Get total material used (in mm^3) print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * global_container_stack.getProperty( "material_diameter", "value") # Send material per extruder material_used = [ str(math.pi * material_radius * material_radius * material_length) for material_length in print_information.materialLengths ] material_used = ",".join(material_used) containers = {"": global_container_stack.serialize()} for container in global_container_stack.getContainers(): container_id = container.getId() try: container_serialized = container.serialize() except NotImplementedError: Logger.log("w", "Container %s could not be serialized!", container_id) continue if container_serialized: containers[container_id] = container_serialized else: Logger.log("i", "No data found in %s to be serialized!", container_id) # Bundle the collected data submitted_data = { "processor": platform.processor(), "machine": platform.machine(), "platform": platform.platform(), "settings": json.dumps( containers ), # bundle of containers with their serialized contents "version": Application.getInstance().getVersion(), "modelhash": modelhash_formatted, "printtime": print_information.currentPrintTime.getDisplayString( DurationFormat.Format.ISO8601), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), } # Convert data to bytes submitted_data = urllib.parse.urlencode(submitted_data) binary_data = submitted_data.encode("utf-8") # Sending slice info non-blocking reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() except Exception as e: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. Logger.log( "e", "Exception raised while sending slice info: %s" % (repr(e)) ) # But we should be notified about these problems of course.
def _onWriteStarted(self, output_device): try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Get total material used (in mm^3) print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * global_container_stack.getProperty( "material_diameter", "value") # TODO: Send material per extruder instead of mashing it on a pile material_used = math.pi * material_radius * material_radius * sum( print_information.materialLengths ) #Volume of all materials used # Get model information (bounding boxes, hashes and transformation matrix) models_info = [] for node in DepthFirstIterator(Application.getInstance( ).getController().getScene().getRoot()): if type(node) is SceneNode and node.getMeshData( ) and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): model_info = {} model_info["hash"] = node.getMeshData().getHash() model_info["bounding_box"] = {} model_info["bounding_box"]["minimum"] = {} model_info["bounding_box"]["minimum"][ "x"] = node.getBoundingBox().minimum.x model_info["bounding_box"]["minimum"][ "y"] = node.getBoundingBox().minimum.y model_info["bounding_box"]["minimum"][ "z"] = node.getBoundingBox().minimum.z model_info["bounding_box"]["maximum"] = {} model_info["bounding_box"]["maximum"][ "x"] = node.getBoundingBox().maximum.x model_info["bounding_box"]["maximum"][ "y"] = node.getBoundingBox().maximum.y model_info["bounding_box"]["maximum"][ "z"] = node.getBoundingBox().maximum.z model_info["transformation"] = str( node.getWorldTransformation().getData()) models_info.append(model_info) # Bundle the collected data submitted_data = { "processor": platform.processor(), "machine": platform.machine(), "platform": platform.platform(), "settings": global_container_stack.serialize( ), # global_container with references on used containers "version": Application.getInstance().getVersion(), "modelhash": "None", "printtime": print_information.currentPrintTime.getDisplayString( DurationFormat.Format.ISO8601), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), } for container in global_container_stack.getContainers(): container_id = container.getId() try: container_serialized = container.serialize() except NotImplementedError: Logger.log("w", "Container %s could not be serialized!", container_id) continue if container_serialized: submitted_data["settings_%s" % ( container_id )] = container_serialized # This can be anything, eg. INI, JSON, etc. else: Logger.log("i", "No data found in %s to be serialized!", container_id) # Convert data to bytes submitted_data = urllib.parse.urlencode(submitted_data) binary_data = submitted_data.encode("utf-8") # Sending slice info non-blocking reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() except Exception as e: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. Logger.log( "e", "Exception raised while sending slice info: %s" % (repr(e)) ) # But we should be notified about these problems of course.
def _onWriteStarted(self, output_device): if not Preferences.getInstance().getValue("info/send_slice_info"): return # Do nothing, user does not want to send data settings = Application.getInstance().getMachineManager( ).getActiveProfile() # Load all machine definitions and put them in machine_settings dict #setting_file_name = Application.getInstance().getActiveMachineInstance().getMachineSettings()._json_file machine_settings = {} #with open(setting_file_name, "rt", -1, "utf-8") as f: # data = json.load(f, object_pairs_hook = collections.OrderedDict) #machine_settings[os.path.basename(setting_file_name)] = copy.deepcopy(data) active_machine_definition = Application.getInstance( ).getMachineManager().getActiveMachineInstance().getMachineDefinition( ) data = active_machine_definition._json_data # Loop through inherited json files setting_file_name = active_machine_definition._path while True: if "inherits" in data: inherited_setting_file_name = os.path.dirname( setting_file_name) + "/" + data["inherits"] with open(inherited_setting_file_name, "rt", -1, "utf-8") as f: data = json.load(f, object_pairs_hook=collections.OrderedDict) machine_settings[os.path.basename( inherited_setting_file_name)] = copy.deepcopy(data) else: break profile_values = settings.getChangedSettings() # Get total material used (in mm^3) print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * settings.getSettingValue("material_diameter") material_used = math.pi * material_radius * material_radius * print_information.materialAmount #Volume of material used # Get model information (bounding boxes, hashes and transformation matrix) models_info = [] for node in DepthFirstIterator(Application.getInstance().getController( ).getScene().getRoot()): if type(node) is SceneNode and node.getMeshData( ) and node.getMeshData().getVertices() is not None: if not getattr(node, "_outside_buildarea", False): model_info = {} model_info["hash"] = node.getMeshData().getHash() model_info["bounding_box"] = {} model_info["bounding_box"]["minimum"] = {} model_info["bounding_box"]["minimum"][ "x"] = node.getBoundingBox().minimum.x model_info["bounding_box"]["minimum"][ "y"] = node.getBoundingBox().minimum.y model_info["bounding_box"]["minimum"][ "z"] = node.getBoundingBox().minimum.z model_info["bounding_box"]["maximum"] = {} model_info["bounding_box"]["maximum"][ "x"] = node.getBoundingBox().maximum.x model_info["bounding_box"]["maximum"][ "y"] = node.getBoundingBox().maximum.y model_info["bounding_box"]["maximum"][ "z"] = node.getBoundingBox().maximum.z model_info["transformation"] = str( node.getWorldTransformation().getData()) models_info.append(model_info) # Bundle the collected data submitted_data = { "processor": platform.processor(), "machine": platform.machine(), "platform": platform.platform(), "machine_settings": json.dumps(machine_settings), "version": Application.getInstance().getVersion(), "modelhash": "None", "printtime": str(print_information.currentPrintTime), "filament": material_used, "language": Preferences.getInstance().getValue("general/language"), "materials_profiles ": {} } # Convert data to bytes submitted_data = urllib.parse.urlencode(submitted_data) binary_data = submitted_data.encode("utf-8") # Submit data try: f = urllib.request.urlopen( "https://stats.youmagine.com/curastats/slice", data=binary_data, timeout=1) except Exception as e: print("Exception occured", e) f.close()
def snapshot(width = 300, height = 300): scene = Application.getInstance().getController().getScene() active_camera = scene.getActiveCamera() render_width, render_height = active_camera.getWindowSize() render_width = int(render_width) render_height = int(render_height) preview_pass = PreviewPass(render_width, render_height) root = scene.getRoot() camera = Camera("snapshot", root) # determine zoom and look at bbox = None for node in DepthFirstIterator(root): if node.callDecoration("isSliceable") and node.getMeshData() and node.isVisible() and not node.callDecoration("isNonThumbnailVisibleMesh"): if bbox is None: bbox = node.getBoundingBox() else: bbox = bbox + node.getBoundingBox() # If there is no bounding box, it means that there is no model in the buildplate if bbox is None: return None look_at = bbox.center # guessed size so the objects are hopefully big size = max(bbox.width, bbox.height, bbox.depth * 0.5) # Looking from this direction (x, y, z) in OGL coordinates looking_from_offset = Vector(-1, 1, 2) if size > 0: # determine the watch distance depending on the size looking_from_offset = looking_from_offset * size * 1.3 camera.setPosition(look_at + looking_from_offset) camera.lookAt(look_at) satisfied = False size = None fovy = 30 while not satisfied: if size is not None: satisfied = True # always be satisfied after second try projection_matrix = Matrix() # Somehow the aspect ratio is also influenced in reverse by the screen width/height # So you have to set it to render_width/render_height to get 1 projection_matrix.setPerspective(fovy, render_width / render_height, 1, 500) camera.setProjectionMatrix(projection_matrix) preview_pass.setCamera(camera) preview_pass.render() pixel_output = preview_pass.getOutput() min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries(pixel_output) size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height) if size > 0.5 or satisfied: satisfied = True else: # make it big and allow for some empty space around fovy *= 0.5 # strangely enough this messes up the aspect ratio: fovy *= size * 1.1 # make it a square if max_x - min_x >= max_y - min_y: # make y bigger min_y, max_y = int((max_y + min_y) / 2 - (max_x - min_x) / 2), int((max_y + min_y) / 2 + (max_x - min_x) / 2) else: # make x bigger min_x, max_x = int((max_x + min_x) / 2 - (max_y - min_y) / 2), int((max_x + min_x) / 2 + (max_y - min_y) / 2) cropped_image = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y) # Scale it to the correct size scaled_image = cropped_image.scaled( width, height, aspectRatioMode = QtCore.Qt.IgnoreAspectRatio, transformMode = QtCore.Qt.SmoothTransformation) return scaled_image
def _readMeshFinished(self, job): Logger.log("d", "read mesh finisihed!") ### START PATCH: detect belt printer global_container_stack = self._application.getGlobalContainerStack() if not global_container_stack: return is_belt_printer = self._preferences.getValue("BeltPlugin/on_plugin") ### END PATCH nodes = job.getResult() file_name = job.getFileName() file_name_lower = file_name.lower() file_extension = file_name_lower.split(".")[-1] self._application._currently_loading_files.remove(file_name) self._application.fileLoaded.emit(file_name) target_build_plate = self._application.getMultiBuildPlateModel( ).activeBuildPlate root = self._application.getController().getScene().getRoot() fixed_nodes = [] for node_ in DepthFirstIterator(root): if node_.callDecoration("isSliceable") and node_.callDecoration( "getBuildPlateNumber") == target_build_plate: fixed_nodes.append(node_) global_container_stack = self._application.getGlobalContainerStack() machine_width = global_container_stack.getProperty( "machine_width", "value") machine_depth = global_container_stack.getProperty( "machine_depth", "value") arranger = Arrange.create(x=machine_width, y=machine_depth, fixed_nodes=fixed_nodes) min_offset = 8 default_extruder_position = self._application.getMachineManager( ).defaultExtruderPosition default_extruder_id = self._application._global_container_stack.extruders[ default_extruder_position].getId() select_models_on_load = self._application.getPreferences().getValue( "cura/select_models_on_load") for original_node in nodes: # Create a CuraSceneNode just if the original node is not that type if isinstance(original_node, CuraSceneNode): node = original_node else: node = CuraSceneNode() node.setMeshData(original_node.getMeshData()) #Setting meshdata does not apply scaling. if (original_node.getScale() != Vector(1.0, 1.0, 1.0)): node.scale(original_node.getScale()) node.setSelectable(True) node.setName(os.path.basename(file_name)) self._application.getBuildVolume().checkBoundsAndUpdate(node) is_non_sliceable = "." + file_extension in self._application._non_sliceable_extensions if is_non_sliceable: self._application.callLater( lambda: self._application.getController().setActiveView( "SimulationView")) block_slicing_decorator = BlockSlicingDecorator() node.addDecorator(block_slicing_decorator) else: sliceable_decorator = SliceableObjectDecorator() node.addDecorator(sliceable_decorator) scene = self._application.getController().getScene() # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) for child in node.getAllChildren(): if not child.getDecorator(ConvexHullDecorator): child.addDecorator(ConvexHullDecorator()) ### START PATCH: don't do standard arrange on load for belt printers ### but place in a line instead if is_belt_printer: half_node_depth = node.getBoundingBox().depth / 2 build_plate_empty = True leading_edge = self._application.getBuildVolume( ).getBoundingBox().front for existing_node in DepthFirstIterator(root): if (not issubclass(type(existing_node), CuraSceneNode) or (not existing_node.getMeshData() and not existing_node.callDecoration("getLayerData")) or (existing_node.callDecoration("getBuildPlateNumber") != target_build_plate)): continue build_plate_empty = False leading_edge = min(leading_edge, existing_node.getBoundingBox().back) if not build_plate_empty or leading_edge < half_node_depth: node.setPosition( Vector( 0, 0, leading_edge - half_node_depth - self._margin_between_models)) if file_extension != "3mf" and not is_belt_printer: ### END PATCH if node.callDecoration("isSliceable"): # Only check position if it's not already blatantly obvious that it won't fit. if node.getBoundingBox( ) is None or self._application._volume.getBoundingBox( ) is None or node.getBoundingBox( ).width < self._application._volume.getBoundingBox( ).width or node.getBoundingBox( ).depth < self._application._volume.getBoundingBox().depth: # Find node location offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( node, min_offset=min_offset) # If a model is to small then it will not contain any points if offset_shape_arr is None and hull_shape_arr is None: Message(self._application._i18n_catalog.i18nc( "@info:status", "The selected model was too small to load."), title=self._application._i18n_catalog. i18nc("@info:title", "Warning")).show() return # Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step=10) # This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy # of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if # the BuildPlateDecorator exists or not and always set the correct build plate number. build_plate_decorator = node.getDecorator(BuildPlateDecorator) if build_plate_decorator is None: build_plate_decorator = BuildPlateDecorator(target_build_plate) node.addDecorator(build_plate_decorator) build_plate_decorator.setBuildPlateNumber(target_build_plate) op = AddSceneNodeOperation(node, scene.getRoot()) op.push() node.callDecoration("setActiveExtruder", default_extruder_id) scene.sceneChanged.emit(node) if select_models_on_load: Selection.add(node) self._application.fileCompleted.emit(file_name)