def _onStackChanged(self): if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) for extruder in extruders: extruder.propertyChanged.disconnect(self._onSettingPropertyChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() if self._global_container_stack: self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged) extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) for extruder in extruders: extruder.propertyChanged.connect(self._onSettingPropertyChanged) self._width = self._global_container_stack.getProperty("machine_width", "value") machine_height = self._global_container_stack.getProperty("machine_height", "value") if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time" and len(self._scene_objects) > 1: self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height) if self._height < machine_height: self._build_volume_message.show() else: self._build_volume_message.hide() else: self._height = self._global_container_stack.getProperty("machine_height", "value") self._build_volume_message.hide() self._depth = self._global_container_stack.getProperty("machine_depth", "value") self._shape = self._global_container_stack.getProperty("machine_shape", "value") self._updateDisallowedAreas() self._updateRaftThickness() self.rebuild()
def __init__(self, parent = None): super().__init__(parent) self._width = 0 self._height = 0 self._depth = 0 self._shader = None self._grid_mesh = None self._grid_shader = None self._disallowed_areas = [] self._disallowed_area_mesh = None self._prime_tower_area = None self._prime_tower_area_mesh = None self.setCalculateBoundingBox(False) self._volume_aabb = None self._raft_thickness = 0.0 self._adhesion_type = None self._platform = Platform(self) self._global_container_stack = None Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) self._onGlobalContainerStackChanged() self._active_extruder_stack = None ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) self._onActiveExtruderStackChanged() self._has_errors = False
def setExtruderForSelection(self, extruder_id: str) -> None: operation = GroupedOperation() nodes_to_change = [] for node in Selection.getAllSelectedObjects(): # Do not change any nodes that already have the right extruder set. if node.callDecoration("getActiveExtruder") == extruder_id: continue # If the node is a group, apply the active extruder to all children of the group. if node.callDecoration("isGroup"): for grouped_node in BreadthFirstIterator(node): if grouped_node.callDecoration("getActiveExtruder") == extruder_id: continue if grouped_node.callDecoration("isGroup"): continue nodes_to_change.append(grouped_node) continue nodes_to_change.append(node) if not nodes_to_change: # If there are no changes to make, we still need to reset the selected extruders. # This is a workaround for checked menu items being deselected while still being # selected. ExtruderManager.getInstance().resetSelectedObjectExtruders() return for node in nodes_to_change: operation.addOperation(SetObjectExtruderOperation(node, extruder_id)) operation.push()
def _getSettingFromAllExtruders(self, setting_key, property = "value"): all_values = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property) all_types = ExtruderManager.getInstance().getAllExtruderSettings(setting_key, "type") for i in range(len(all_values)): if not all_values[i] and (all_types[i] == "int" or all_types[i] == "float"): all_values[i] = 0 return all_values
def _onGlobalContainerChanged(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not self._multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance().getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() root_node = Application.getInstance().getController().getScene().getRoot() for node in DepthFirstIterator(root_node): new_stack_id = default_stack_id # Get position of old extruder stack for this node old_extruder_pos = node.callDecoration("getActiveExtruderPosition") if old_extruder_pos is not None: # Fetch current (new) extruder stack at position new_stack = ExtruderManager.getInstance().getExtruderStack(old_extruder_pos) if new_stack: new_stack_id = new_stack.getId() node.callDecoration("setActiveExtruder", new_stack_id) self._updateEnabled()
def _getEdgeDisallowedSize(self): if not self._global_container_stack: return 0 container_stack = self._global_container_stack # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects if container_stack.getProperty("print_sequence", "value") == "one_at_a_time": return 0.1 # Return a very small value, so we do draw disallowed area's near the edges. adhesion_type = container_stack.getProperty("adhesion_type", "value") if adhesion_type == "skirt": skirt_distance = self._getSettingFromAdhesionExtruder("skirt_gap") skirt_line_count = self._getSettingFromAdhesionExtruder("skirt_line_count") bed_adhesion_size = skirt_distance + (skirt_line_count * self._getSettingFromAdhesionExtruder("skirt_brim_line_width")) if len(ExtruderManager.getInstance().getUsedExtruderStacks()) > 1: adhesion_extruder_nr = int(self._global_container_stack.getProperty("adhesion_extruder_nr", "value")) extruder_values = ExtruderManager.getInstance().getAllExtruderValues("skirt_brim_line_width") del extruder_values[adhesion_extruder_nr] # Remove the value of the adhesion extruder nr. for value in extruder_values: bed_adhesion_size += value elif adhesion_type == "brim": bed_adhesion_size = self._getSettingFromAdhesionExtruder("brim_line_count") * self._getSettingFromAdhesionExtruder("skirt_brim_line_width") if self._global_container_stack.getProperty("machine_extruder_count", "value") > 1: adhesion_extruder_nr = int(self._global_container_stack.getProperty("adhesion_extruder_nr", "value")) extruder_values = ExtruderManager.getInstance().getAllExtruderValues("skirt_brim_line_width") del extruder_values[adhesion_extruder_nr] # Remove the value of the adhesion extruder nr. for value in extruder_values: bed_adhesion_size += value elif adhesion_type == "raft": bed_adhesion_size = self._getSettingFromAdhesionExtruder("raft_margin") elif adhesion_type == "none": bed_adhesion_size = 0 else: raise Exception("Unknown bed adhesion type. Did you forget to update the build volume calculations for your new bed adhesion type?") support_expansion = 0 if self._getSettingFromSupportInfillExtruder("support_offset") and self._global_container_stack.getProperty("support_enable", "value"): support_expansion += self._getSettingFromSupportInfillExtruder("support_offset") farthest_shield_distance = 0 if container_stack.getProperty("draft_shield_enabled", "value"): farthest_shield_distance = max(farthest_shield_distance, container_stack.getProperty("draft_shield_dist", "value")) if container_stack.getProperty("ooze_shield_enabled", "value"): farthest_shield_distance = max(farthest_shield_distance, container_stack.getProperty("ooze_shield_dist", "value")) move_from_wall_radius = 0 # Moves that start from outer wall. move_from_wall_radius = max(move_from_wall_radius, max(self._getSettingFromAllExtruders("infill_wipe_dist"))) used_extruders = ExtruderManager.getInstance().getUsedExtruderStacks() avoid_enabled_per_extruder = [stack.getProperty("travel_avoid_other_parts","value") for stack in used_extruders] travel_avoid_distance_per_extruder = [stack.getProperty("travel_avoid_distance", "value") for stack in used_extruders] for avoid_other_parts_enabled, avoid_distance in zip(avoid_enabled_per_extruder, travel_avoid_distance_per_extruder): #For each extruder (or just global). if avoid_other_parts_enabled: move_from_wall_radius = max(move_from_wall_radius, avoid_distance) # Now combine our different pieces of data to get the final border size. # Support expansion is added to the bed adhesion, since the bed adhesion goes around support. # Support expansion is added to farthest shield distance, since the shields go around support. border_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size) return border_size
def __init__(self, parent = None): super().__init__(parent) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) self._global_container_stack = None self._settings_with_inheritance_warning = [] self._active_container_stack = None self._onGlobalContainerChanged() ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) self._onActiveExtruderChanged()
def setVisible(self, visible): if not self._node: return if not self._stack: self._node.addDecorator(SettingOverrideDecorator()) self._stack = self._node.callDecoration("getStack") settings = self._stack.getTop() all_instances = settings.findInstances() visibility_changed = False # Flag to check if at the end the signal needs to be emitted # Remove all instances that are not in visibility list for instance in all_instances: if instance.definition.key not in visible: settings.removeInstance(instance.definition.key) visibility_changed = True # Add all instances that are not added, but are in visibility list for item in visible: if not settings.getInstance(item): definition = self._stack.getSettingDefinition(item) if definition: new_instance = SettingInstance(definition, settings) stack_nr = -1 if definition.limit_to_extruder and self._stack.getProperty("machine_extruder_count", "value") > 1: # Obtain the value from the correct container stack. Only once, upon adding the setting. stack_nr = str( int(round(float(self._stack.getProperty(item, "limit_to_extruder")))) ) # Stack to get the setting from. Round it and remove the fractional part. if stack_nr not in ExtruderManager.getInstance().extruderIds and self._stack.getProperty( "extruder_nr", "value" ): # Property not defined, but we have an extruder number. stack_nr = str(int(round(float(self._stack.getProperty("extruder_nr", "value"))))) if ( stack_nr in ExtruderManager.getInstance().extruderIds ): # We have either a limit_to_extruder or an extruder_nr. stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks( id=ExtruderManager.getInstance().extruderIds[stack_nr] )[0] else: stack = UM.Application.getInstance().getGlobalContainerStack() new_instance.setProperty("value", stack.getRawProperty(item, "value")) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) visibility_changed = True else: Logger.log( "w", "Unable to add instance (%s) to per-object visibility because we couldn't find the matching definition", item, ) if visibility_changed: self.visibilityChanged.emit()
def setVisible(self, visible): if not self._node: return if not self._stack: self._node.addDecorator(SettingOverrideDecorator()) self._stack = self._node.callDecoration("getStack") settings = self._stack.getTop() all_instances = settings.findInstances() visibility_changed = False # Flag to check if at the end the signal needs to be emitted # Remove all instances that are not in visibility list for instance in all_instances: if instance.definition.key not in visible: settings.removeInstance(instance.definition.key) visibility_changed = True # Add all instances that are not added, but are in visibility list for item in visible: if not settings.getInstance(item): # Setting was not added already. definition = self._stack.getSettingDefinition(item) if definition: new_instance = SettingInstance(definition, settings) stack_nr = -1 stack = None # Check from what stack we should copy the raw property of the setting from. if self._stack.getProperty("machine_extruder_count", "value") > 1: if definition.limit_to_extruder != "-1": # A limit to extruder function was set and it's a multi extrusion machine. Check what stack we do need to use. stack_nr = str(int(round(float(self._stack.getProperty(item, "limit_to_extruder"))))) # Check if the found stack_number is in the extruder list of extruders. if stack_nr not in ExtruderManager.getInstance().extruderIds and self._stack.getProperty("extruder_nr", "value") is not None: stack_nr = -1 # Use the found stack number to get the right stack to copy the value from. if stack_nr in ExtruderManager.getInstance().extruderIds: stack = ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0] else: stack = self._stack # Use the raw property to set the value (so the inheritance doesn't break) if stack is not None: new_instance.setProperty("value", stack.getRawProperty(item, "value")) else: new_instance.setProperty("value", None) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) visibility_changed = True else: Logger.log("w", "Unable to add instance (%s) to per-object visibility because we couldn't find the matching definition", item) if visibility_changed: self.visibilityChanged.emit()
def _onActiveExtruderChanged(self): if self._global_container_stack: # Connect all extruders of the active machine. This might cause a few connects that have already happend, # but that shouldn't cause issues as only new / unique connections are added. extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) if extruders: for extruder in extruders: extruder.propertyChanged.connect(self._onSettingChanged) if self._active_extruder_stack: self._active_extruder_stack.containersChanged.disconnect(self._onChanged) self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack() if self._active_extruder_stack: self._active_extruder_stack.containersChanged.connect(self._onChanged)
def __init__(self, parent = None): super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings")) self._qml_url = "MachineSettingsAction.qml" self._global_container_stack = None self._container_index = 0 self._extruder_container_index = 0 self._container_registry = ContainerRegistry.getInstance() self._container_registry.containerAdded.connect(self._onContainerAdded) self._container_registry.containerRemoved.connect(self._onContainerRemoved) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) self._backend = Application.getInstance().getBackend()
def __init__(self, parent = None): super().__init__(parent) Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged) self._global_container_stack = None self._settings_with_inheritance_warning = [] self._active_container_stack = None self._onGlobalContainerChanged() ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) self._onActiveExtruderChanged() self._update_timer = QTimer() self._update_timer.setInterval(500) self._update_timer.setSingleShot(True) self._update_timer.timeout.connect(self._update)
def _onGlobalContainerChanged(self) -> None: if self._global_stack: try: self._global_stack.propertyChanged.disconnect(self._onPropertyChanged) except TypeError: pass for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): extruder_stack.propertyChanged.disconnect(self._onPropertyChanged) self._global_stack = Application.getInstance().getGlobalContainerStack() if self._global_stack: self._global_stack.propertyChanged.connect(self._onPropertyChanged) for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): extruder_stack.propertyChanged.connect(self._onPropertyChanged) self._onPropertyChanged("support_angle", "value") # Force an re-evaluation
def _extruderOffsets(self) -> Dict[int, List[float]]: result = {} for extruder in ExtruderManager.getInstance().getActiveExtruderStacks(): result[int(extruder.getMetaData().get("position", "0"))] = [ extruder.getProperty("machine_nozzle_offset_x", "value"), extruder.getProperty("machine_nozzle_offset_y", "value")] return result
def _serialiseSettings(self, stack): prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) container_with_profile = stack.findContainer({"type": "quality"}) flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile) serialized = flat_global_container.serialize() data = {"global_quality": serialized} for extruder in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): extruder_quality = extruder.findContainer({"type": "quality"}) flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality) extruder_serialized = flat_extruder_quality.serialize() data.setdefault("extruder_quality", []).append(extruder_serialized) json_string = json.dumps(data) # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. escaped_string = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(escaped_string), 80 - prefix_length): result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n" return result
def _computeDisallowedAreasPrinted(self, used_extruders): result = {} for extruder in used_extruders: result[extruder.getId()] = [] #Currently, the only normally printed object is the prime tower. if ExtruderManager.getInstance().getResolveOrValue("prime_tower_enable") == True: prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value") machine_width = self._global_container_stack.getProperty("machine_width", "value") machine_depth = self._global_container_stack.getProperty("machine_depth", "value") prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") if not self._global_container_stack.getProperty("machine_center_is_zero", "value"): prime_tower_x = prime_tower_x - machine_width / 2 #Offset by half machine_width and _depth to put the origin in the front-left. prime_tower_y = prime_tower_y + machine_depth / 2 prime_tower_area = Polygon([ [prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size], [prime_tower_x, prime_tower_y - prime_tower_size], [prime_tower_x, prime_tower_y], [prime_tower_x - prime_tower_size, prime_tower_y], ]) prime_tower_area = prime_tower_area.getMinkowskiHull(Polygon.approximatedCircle(0)) for extruder in used_extruders: result[extruder.getId()].append(prime_tower_area) #The prime tower location is the same for each extruder, regardless of offset. return result
def _checkForWarnings(self): warnings = [] print_information = CuraApplication.getInstance().getPrintInformation() if not print_information.materialLengths: Logger.log("w", "There is no material length information. Unable to check for warnings.") return warnings extruder_manager = ExtruderManager.getInstance() for index, extruder in enumerate(self.activePrinter.extruders): if index < len(print_information.materialLengths) and print_information.materialLengths[index] != 0: # The extruder is by this print. # TODO: material length check # Check if the right Printcore is active. variant = extruder_manager.getExtruderStack(index).findContainer({"type": "variant"}) if variant: if variant.getName() != extruder.hotendID: warnings.append(i18n_catalog.i18nc("@label", "Different PrintCore (Cura: {cura_printcore_name}, Printer: {remote_printcore_name}) selected for extruder {extruder_id}".format(cura_printcore_name = variant.getName(), remote_printcore_name = extruder.hotendID, extruder_id = index + 1))) else: Logger.log("w", "Unable to find variant.") # Check if the right material is loaded. local_material = extruder_manager.getExtruderStack(index).findContainer({"type": "material"}) if local_material: if extruder.activeMaterial.guid != local_material.getMetaDataEntry("GUID"): Logger.log("w", "Extruder %s has a different material (%s) as Cura (%s)", index + 1, extruder.activeMaterial.guid, local_material.getMetaDataEntry("GUID")) warnings.append(i18n_catalog.i18nc("@label", "Different material (Cura: {0}, Printer: {1}) selected for extruder {2}").format(local_material.getName(), extruder.activeMaterial.name, index + 1)) else: Logger.log("w", "Unable to find material.") return warnings
def _fetchInstanceContainers(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: return {}, {} # Fetch the list of quality changes. quality_manager = QualityManager.getInstance() machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.definition) quality_changes_list = quality_manager.findAllQualityChangesForMachine(machine_definition) extruder_manager = ExtruderManager.getInstance() active_extruder = extruder_manager.getActiveExtruderStack() extruder_stacks = self._getOrderedExtruderStacksList() # Fetch the list of usable qualities across all extruders. # The actual list of quality profiles come from the first extruder in the extruder list. quality_list = quality_manager.findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) # Filter the quality_change by the list of available quality_types quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list]) filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and qc.getMetaDataEntry("extruder") is not None and (qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())} return filtered_quality_changes, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
def _fetchInstanceContainers(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: return [] # Fetch the list of quality changes. quality_manager = QualityManager.getInstance() machine_definition = quality_manager.getParentMachineDefinition(global_container_stack.getBottom()) quality_changes_list = quality_manager.findAllQualityChangesForMachine(machine_definition) # Get the list of extruders and place the selected extruder at the front of the list. extruder_manager = ExtruderManager.getInstance() active_extruder = extruder_manager.getActiveExtruderStack() extruder_stacks = extruder_manager.getActiveExtruderStacks() if active_extruder in extruder_stacks: extruder_stacks.remove(active_extruder) extruder_stacks = [active_extruder] + extruder_stacks # Fetch the list of useable qualities across all extruders. # The actual list of quality profiles come from the first extruder in the extruder list. quality_list = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) # Filter the quality_change by the list of available quality_types quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list]) filtered_quality_changes = [qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set] return quality_list + filtered_quality_changes
def _handlePerObjectSettings(self, node: CuraSceneNode, message: Arcus.PythonMessage): stack = node.callDecoration("getStack") # Check if the node has a stack attached to it and the stack has any settings in the top container. if not stack: return # Check all settings for relations, so we can also calculate the correct values for dependent settings. top_of_stack = stack.getTop() # Cache for efficiency. changed_setting_keys = top_of_stack.getAllKeys() # Add all relations to changed settings as well. for key in top_of_stack.getAllKeys(): instance = top_of_stack.getInstance(key) self._addRelations(changed_setting_keys, instance.definition.relations) Job.yieldThread() # Ensure that the engine is aware what the build extruder is. changed_setting_keys.add("extruder_nr") # Get values for all changed settings for key in changed_setting_keys: setting = message.addRepeatedMessage("settings") setting.name = key extruder = int(round(float(stack.getProperty(key, "limit_to_extruder")))) # Check if limited to a specific extruder, but not overridden by per-object settings. if extruder >= 0 and key not in changed_setting_keys: limited_stack = ExtruderManager.getInstance().getActiveExtruderStacks()[extruder] else: limited_stack = stack setting.value = str(limited_stack.getProperty(key, "value")).encode("utf-8") Job.yieldThread()
def extruder_manager(application, container_registry) -> ExtruderManager: if ExtruderManager.getInstance() is not None: # Reset the data ExtruderManager._ExtruderManager__instance = None with patch("cura.CuraApplication.CuraApplication.getInstance", MagicMock(return_value=application)): with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value=container_registry)): manager = ExtruderManager() return manager
def __init__(self): super().__init__() self._stack = PerObjectContainerStack(stack_id = id(self)) self._stack.setDirty(False) # This stack does not need to be saved. self._stack.addContainer(InstanceContainer(container_id = "SettingOverrideInstanceContainer")) if ExtruderManager.getInstance().extruderCount > 1: self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId() else: self._extruder_stack = None self._stack.propertyChanged.connect(self._onSettingChanged) ContainerRegistry.getInstance().addContainer(self._stack) Application.getInstance().globalContainerStackChanged.connect(self._updateNextStack) self.activeExtruderChanged.connect(self._updateNextStack) self._updateNextStack()
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode): mesh_writer = Application.getInstance().getMeshFileHandler().getWriter("3MFWriter") if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace return False # Indicate that the 3mf mesh writer should not close the archive just yet (we still need to add stuff to it). mesh_writer.setStoreArchive(True) mesh_writer.write(stream, nodes, mode) archive = mesh_writer.getArchive() if archive is None: # This happens if there was no mesh data to write. archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED) global_container_stack = Application.getInstance().getGlobalContainerStack() # Add global container stack data to the archive. self._writeContainerToArchive(global_container_stack, archive) # Also write all containers in the stack to the file for container in global_container_stack.getContainers(): self._writeContainerToArchive(container, archive) # Check if the machine has extruders and save all that data as well. for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId()): self._writeContainerToArchive(extruder_stack, archive) for container in extruder_stack.getContainers(): self._writeContainerToArchive(container, archive) # Write preferences to archive original_preferences = Preferences.getInstance() #Copy only the preferences that we use to the workspace. temp_preferences = Preferences() for preference in {"general/visible_settings", "cura/active_mode", "cura/categories_expanded"}: temp_preferences.addPreference(preference, None) temp_preferences.setValue(preference, original_preferences.getValue(preference)) preferences_string = StringIO() temp_preferences.writeToFile(preferences_string) preferences_file = zipfile.ZipInfo("Cura/preferences.cfg") archive.writestr(preferences_file, preferences_string.getvalue()) # Save Cura version version_file = zipfile.ZipInfo("Cura/version.ini") version_config_parser = configparser.ConfigParser() version_config_parser.add_section("versions") version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode())) version_file_string = StringIO() version_config_parser.write(version_file_string) archive.writestr(version_file, version_file_string.getvalue()) # Close the archive & reset states. archive.close() mesh_writer.setStoreArchive(False) return True
def _serialiseSettings(self, stack): prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) container_with_profile = stack.qualityChanges if not container_with_profile: Logger.log("e", "No valid quality profile found, not writing settings to GCode!") return "" flat_global_container = self._createFlattenedContainerInstance(stack.getTop(), container_with_profile) # If the quality changes is not set, we need to set type manually if flat_global_container.getMetaDataEntry("type", None) is None: flat_global_container.addMetaDataEntry("type", "quality_changes") # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_global_container.getMetaDataEntry("quality_type", None) is None: flat_global_container.addMetaDataEntry("quality_type", stack.quality.getMetaDataEntry("quality_type", "normal")) serialized = flat_global_container.serialize() data = {"global_quality": serialized} for extruder in sorted(ExtruderManager.getInstance().getMachineExtruders(stack.getId()), key = lambda k: k.getMetaDataEntry("position")): extruder_quality = extruder.qualityChanges if not extruder_quality: Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId()) continue flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality) # If the quality changes is not set, we need to set type manually if flat_extruder_quality.getMetaDataEntry("type", None) is None: flat_extruder_quality.addMetaDataEntry("type", "quality_changes") # Ensure that extruder is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("extruder", None) is None: flat_extruder_quality.addMetaDataEntry("extruder", extruder.getBottom().getId()) # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None: flat_extruder_quality.addMetaDataEntry("quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal")) extruder_serialized = flat_extruder_quality.serialize() data.setdefault("extruder_quality", []).append(extruder_serialized) json_string = json.dumps(data) # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. escaped_string = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(escaped_string), 80 - prefix_length): result += prefix + escaped_string[pos : pos + 80 - prefix_length] + "\n" return result
def _cacheAllExtruderSettings(self): global_stack = cast(ContainerStack, CuraApplication.getInstance().getGlobalContainerStack()) # NB: keys must be strings for the string formatter self._all_extruders_settings = { "-1": self._buildReplacementTokens(global_stack) } for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks(): extruder_nr = extruder_stack.getProperty("extruder_nr", "value") self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
def __init__(self, width, height): super().__init__("layerview", width, height) self._layer_shader = None self._tool_handle_shader = None self._gl = OpenGL.getInstance().getBindingsObject() self._scene = Application.getInstance().getController().getScene() self._extruder_manager = ExtruderManager.getInstance() self._layer_view = None
def _onActiveExtruderStackChanged(self): extruder_container_stack = ExtruderManager.getInstance().getActiveExtruderStack() if not self._global_container_stack or not extruder_container_stack: return # Make sure there is a definition_changes container to store the machine settings definition_changes_container = extruder_container_stack.definitionChanges if definition_changes_container == self._empty_container: definition_changes_container = CuraStackBuilder.createDefinitionChangesContainer( extruder_container_stack, extruder_container_stack.getId() + "_settings")
def _onGlobalStackChanged(self): if self._global_stack: self._global_stack.propertyChanged.disconnect(self._onSettingValueChanged) self._global_stack.containersChanged.disconnect(self._onChanged) extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_stack.getId()) for extruder in extruders: extruder.propertyChanged.disconnect(self._onSettingValueChanged) self._global_stack = Application.getInstance().getGlobalContainerStack() if self._global_stack: self._global_stack.propertyChanged.connect(self._onSettingValueChanged) self._global_stack.containersChanged.connect(self._onChanged) extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_stack.getId()) for extruder in extruders: extruder.propertyChanged.connect(self._onSettingValueChanged) self._onChanged()
def execute(self, data): """ Entry point of the plugin. data is the list of original g-code instructions, the returned string is the list of modified g-code instructions """ stretcher = Stretcher( ExtruderManager.getInstance().getActiveExtruderStack().getProperty("machine_nozzle_size", "value") , self.getSettingValueByKey("wc_stretch"), self.getSettingValueByKey("pw_stretch")) return stretcher.execute(data)
def _getOrderedExtruderStacksList() -> List["ExtruderStack"]: extruder_manager = ExtruderManager.getInstance() extruder_stacks = extruder_manager.getActiveExtruderStacks() active_extruder = extruder_manager.getActiveExtruderStack() if active_extruder in extruder_stacks: extruder_stacks.remove(active_extruder) extruder_stacks = [active_extruder] + extruder_stacks return extruder_stacks
def _getEdgeDisallowedSize(self): if not self._global_container_stack: return 0 container_stack = self._global_container_stack # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects if container_stack.getProperty("print_sequence", "value") == "one_at_a_time": return 0.1 # Return a very small value, so we do draw disallowed area's near the edges. adhesion_type = container_stack.getProperty("adhesion_type", "value") if adhesion_type == "skirt": skirt_distance = self._getSettingFromAdhesionExtruder("skirt_gap") skirt_line_count = self._getSettingFromAdhesionExtruder( "skirt_line_count") bed_adhesion_size = skirt_distance + ( skirt_line_count * self._getSettingFromAdhesionExtruder("skirt_brim_line_width")) if self._global_container_stack.getProperty( "machine_extruder_count", "value") > 1: adhesion_extruder_nr = int( self._global_container_stack.getProperty( "adhesion_extruder_nr", "value")) extruder_values = ExtruderManager.getInstance( ).getAllExtruderValues("skirt_brim_line_width") del extruder_values[ adhesion_extruder_nr] # Remove the value of the adhesion extruder nr. for value in extruder_values: bed_adhesion_size += value elif adhesion_type == "brim": bed_adhesion_size = self._getSettingFromAdhesionExtruder( "brim_line_count") * self._getSettingFromAdhesionExtruder( "skirt_brim_line_width") if self._global_container_stack.getProperty( "machine_extruder_count", "value") > 1: adhesion_extruder_nr = int( self._global_container_stack.getProperty( "adhesion_extruder_nr", "value")) extruder_values = ExtruderManager.getInstance( ).getAllExtruderValues("skirt_brim_line_width") del extruder_values[ adhesion_extruder_nr] # Remove the value of the adhesion extruder nr. for value in extruder_values: bed_adhesion_size += value elif adhesion_type == "raft": bed_adhesion_size = self._getSettingFromAdhesionExtruder( "raft_margin") else: raise Exception( "Unknown bed adhesion type. Did you forget to update the build volume calculations for your new bed adhesion type?" ) support_expansion = 0 if self._getSettingFromSupportInfillExtruder( "support_offset") and self._global_container_stack.getProperty( "support_enable", "value"): support_expansion += self._getSettingFromSupportInfillExtruder( "support_offset") farthest_shield_distance = 0 if container_stack.getProperty("draft_shield_enabled", "value"): farthest_shield_distance = max( farthest_shield_distance, container_stack.getProperty("draft_shield_dist", "value")) if container_stack.getProperty("ooze_shield_enabled", "value"): farthest_shield_distance = max( farthest_shield_distance, container_stack.getProperty("ooze_shield_dist", "value")) move_from_wall_radius = 0 # Moves that start from outer wall. move_from_wall_radius = max( move_from_wall_radius, max(self._getSettingFromAllExtruders("infill_wipe_dist"))) avoid_enabled_per_extruder = self._getSettingFromAllExtruders( ("travel_avoid_other_parts")) avoid_distance_per_extruder = self._getSettingFromAllExtruders( "travel_avoid_distance") for index, avoid_other_parts_enabled in enumerate( avoid_enabled_per_extruder ): #For each extruder (or just global). if avoid_other_parts_enabled: move_from_wall_radius = max(move_from_wall_radius, avoid_distance_per_extruder[index] ) #Index of the same extruder. #Now combine our different pieces of data to get the final border size. #Support expansion is added to the bed adhesion, since the bed adhesion goes around support. #Support expansion is added to farthest shield distance, since the shields go around support. border_size = max(move_from_wall_radius, support_expansion + farthest_shield_distance, support_expansion + bed_adhesion_size) return border_size
def __updateExtruders(self): changed = False if self.rowCount() != 0: changed = True items = [] global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if self._add_global: material = global_container_stack.material color = material.getMetaDataEntry( "color_code", default=self.defaultColors[0] ) if material else self.defaultColors[0] item = { "id": global_container_stack.getId(), "name": "Global", "color": color, "index": -1, "definition": "" } items.append(item) changed = True machine_extruder_count = global_container_stack.getProperty( "machine_extruder_count", "value") manager = ExtruderManager.getInstance() for extruder in manager.getMachineExtruders( global_container_stack.getId()): position = extruder.getMetaDataEntry( "position", default="0") # Get the position try: position = int(position) except ValueError: #Not a proper int. position = -1 if position >= machine_extruder_count: continue extruder_name = extruder.getName() material = extruder.material variant = extruder.variant default_color = self.defaultColors[ position] if position >= 0 and position < len( self.defaultColors) else self.defaultColors[0] color = material.getMetaDataEntry( "color_code", default=default_color) if material else default_color item = { #Construct an item with only the relevant information. "id": extruder.getId(), "name": extruder_name, "color": color, "index": position, "definition": extruder.getBottom().getId(), "material": material.getName() if material else "", "variant": variant.getName() if variant else "", } items.append(item) changed = True if changed: items.sort(key=lambda i: i["index"]) # We need optional extruder to be last, so add it after we do sorting. # This way we can simply intrepret the -1 of the index as the last item (which it now always is) if self._add_optional_extruder: item = { "id": "", "name": "Not overridden", "color": "#ffffff", "index": -1, "definition": "" } items.append(item) self.setItems(items) self.modelChanged.emit()
def updateMaterialForDiameter(self): # Updates the material container to a material that matches the material diameter set for the printer if not self._global_container_stack: return if not self._global_container_stack.getMetaDataEntry("has_materials", False): return material = ExtruderManager.getInstance().getActiveExtruderStack().material material_diameter = material.getProperty("material_diameter", "value") if not material_diameter: # in case of "empty" material material_diameter = 0 material_approximate_diameter = str(round(material_diameter)) definition_changes = self._global_container_stack.definitionChanges machine_diameter = definition_changes.getProperty("material_diameter", "value") if not machine_diameter: machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value") machine_approximate_diameter = str(round(machine_diameter)) if material_approximate_diameter != machine_approximate_diameter: Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.") stacks = ExtruderManager.getInstance().getExtruderStacks() if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): materials_definition = self._global_container_stack.definition.getId() has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False) else: materials_definition = "fdmprinter" has_material_variants = False for stack in stacks: old_material = stack.material search_criteria = { "type": "material", "approximate_diameter": machine_approximate_diameter, "material": old_material.getMetaDataEntry("material", "value"), "supplier": old_material.getMetaDataEntry("supplier", "value"), "color_name": old_material.getMetaDataEntry("color_name", "value"), "definition": materials_definition } if has_material_variants: search_criteria["variant"] = stack.variant.getId() if old_material == self._empty_container: search_criteria.pop("material", None) search_criteria.pop("supplier", None) search_criteria.pop("definition", None) search_criteria["id"] = stack.getMetaDataEntry("preferred_material") materials = self._container_registry.findInstanceContainers(**search_criteria) if not materials: # Same material with new diameter is not found, search for generic version of the same material type search_criteria.pop("supplier", None) search_criteria["color_name"] = "Generic" materials = self._container_registry.findInstanceContainers(**search_criteria) if not materials: # Generic material with new diameter is not found, search for preferred material search_criteria.pop("color_name", None) search_criteria.pop("material", None) search_criteria["id"] = stack.getMetaDataEntry("preferred_material") materials = self._container_registry.findInstanceContainers(**search_criteria) if not materials: # Preferred material with new diameter is not found, search for any material search_criteria.pop("id", None) materials = self._container_registry.findInstanceContainers(**search_criteria) if not materials: # Just use empty material as a final fallback materials = [self._empty_container] Logger.log("i", "Selecting new material: %s" % materials[0].getId()) stack.material = materials[0]
def write(self, stream, nodes, mode=WorkspaceWriter.OutputMode.BinaryMode): mesh_writer = Application.getInstance().getMeshFileHandler().getWriter( "3MFWriter") if not mesh_writer: # We need to have the 3mf mesh writer, otherwise we can't save the entire workspace return False # Indicate that the 3mf mesh writer should not close the archive just yet (we still need to add stuff to it). mesh_writer.setStoreArchive(True) mesh_writer.write(stream, nodes, mode) archive = mesh_writer.getArchive() if archive is None: # This happens if there was no mesh data to write. archive = zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED) global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Add global container stack data to the archive. self._writeContainerToArchive(global_container_stack, archive) # Also write all containers in the stack to the file for container in global_container_stack.getContainers(): self._writeContainerToArchive(container, archive) # Check if the machine has extruders and save all that data as well. for extruder_stack in ExtruderManager.getInstance( ).getMachineExtruders(global_container_stack.getId()): self._writeContainerToArchive(extruder_stack, archive) for container in extruder_stack.getContainers(): self._writeContainerToArchive(container, archive) # Write preferences to archive original_preferences = Preferences.getInstance( ) #Copy only the preferences that we use to the workspace. temp_preferences = Preferences() for preference in { "general/visible_settings", "cura/active_mode", "cura/categories_expanded" }: temp_preferences.addPreference(preference, None) temp_preferences.setValue( preference, original_preferences.getValue(preference)) preferences_string = StringIO() temp_preferences.writeToFile(preferences_string) preferences_file = zipfile.ZipInfo("Cura/preferences.cfg") archive.writestr(preferences_file, preferences_string.getvalue()) # Save Cura version version_file = zipfile.ZipInfo("Cura/version.ini") version_config_parser = configparser.ConfigParser() version_config_parser.add_section("versions") version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) version_config_parser.set( "versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode())) version_file_string = StringIO() version_config_parser.write(version_file_string) archive.writestr(version_file, version_file_string.getvalue()) # Close the archive & reset states. archive.close() mesh_writer.setStoreArchive(False) return True
def _recomputeItems(self): #Some globals that we can re-use. global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack is None: return # Get the list of extruders and place the selected extruder at the front of the list. extruder_manager = ExtruderManager.getInstance() active_extruder = extruder_manager.getActiveExtruderStack() extruder_stacks = extruder_manager.getActiveExtruderStacks() if active_extruder in extruder_stacks: extruder_stacks.remove(active_extruder) extruder_stacks = [active_extruder] + extruder_stacks # Get a list of available qualities for this machine and material qualities = QualityManager.getInstance( ).findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) container_registry = ContainerRegistry.getInstance() machine_manager = Application.getInstance().getMachineManager() unit = global_container_stack.getBottom().getProperty( "layer_height", "unit") if not unit: unit = "" # group all quality items according to quality_types, so we know which profile suits the currently # active machine and material, and later yield the right ones. tmp_all_quality_items = OrderedDict() for item in super()._recomputeItems(): profile = container_registry.findContainers(id=item["id"]) quality_type = profile[0].getMetaDataEntry( "quality_type") if profile else "" if quality_type not in tmp_all_quality_items: tmp_all_quality_items[quality_type] = { "suitable_container": None, "all_containers": [] } tmp_all_quality_items[quality_type]["all_containers"].append(item) if tmp_all_quality_items[quality_type][ "suitable_container"] is None and profile[0] in qualities: tmp_all_quality_items[quality_type][ "suitable_container"] = item # reverse the ordering (finest first, coarsest last) all_quality_items = OrderedDict() for key in reversed(tmp_all_quality_items.keys()): all_quality_items[key] = tmp_all_quality_items[key] # First the suitable containers are set in the model containers = [] for data_item in all_quality_items.values(): suitable_item = data_item["suitable_container"] if suitable_item is None: suitable_item = data_item["all_containers"][0] containers.append(suitable_item) # Once the suitable containers are collected, the rest of the containers are appended for data_item in all_quality_items.values(): for item in data_item["all_containers"]: if item not in containers: containers.append(item) # Now all the containers are set for item in containers: profile = container_registry.findContainers(id=item["id"]) if not profile: item[ "layer_height"] = "" #Can't update a profile that is unknown. item["available"] = False yield item continue profile = profile[0] item["available"] = profile in qualities #Easy case: This profile defines its own layer height. if profile.hasProperty("layer_height", "value"): self._setItemLayerHeight( item, profile.getProperty("layer_height", "value"), unit) yield item continue #Quality-changes profile that has no value for layer height. Get the corresponding quality profile and ask that profile. quality_type = profile.getMetaDataEntry("quality_type", None) if quality_type: quality_results = machine_manager.determineQualityAndQualityChangesForQualityType( quality_type) for quality_result in quality_results: if quality_result["stack"] is global_container_stack: quality = quality_result["quality"] break else: #No global container stack in the results: if quality_results: quality = quality_results[0][ "quality"] #Take any of the extruders. else: quality = None if quality and quality.hasProperty("layer_height", "value"): self._setItemLayerHeight( item, quality.getProperty("layer_height", "value"), unit) yield item continue #Quality has no value for layer height either. Get the layer height from somewhere lower in the stack. skip_until_container = global_container_stack.material if not skip_until_container or skip_until_container == ContainerRegistry.getInstance( ).getEmptyInstanceContainer(): #No material in stack. skip_until_container = global_container_stack.variant if not skip_until_container or skip_until_container == ContainerRegistry.getInstance( ).getEmptyInstanceContainer(): #No variant in stack. skip_until_container = global_container_stack.getBottom() self._setItemLayerHeight( item, global_container_stack.getRawProperty( "layer_height", "value", skip_until_container=skip_until_container.getId()), unit) # Fall through to the currently loaded material. yield item
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_dict[ "material_used"] = print_information.materialLengths[int( extruder.getMetaDataEntry("position", "0"))] 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 _fetchInstanceContainers(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if not global_container_stack: return [] # Fetch the list of quality changes. quality_manager = QualityManager.getInstance() machine_definition = quality_manager.getParentMachineDefinition( global_container_stack.getBottom()) quality_changes_list = quality_manager.findAllQualityChangesForMachine( machine_definition) # Detecting if the machine has multiple extrusion multiple_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 # Get the list of extruders extruder_manager = ExtruderManager.getInstance() active_extruder = extruder_manager.getActiveExtruderStack() extruder_stacks = extruder_manager.getActiveExtruderStacks() if multiple_extrusion: # Place the active extruder at the front of the list. # This is a workaround checking if there is an active_extruder or not before moving it to the front of the list. # Actually, when a printer has multiple extruders, should exist always an active_extruder. However, in some # cases the active_extruder is still None. if active_extruder in extruder_stacks: extruder_stacks.remove(active_extruder) new_extruder_stacks = [] if active_extruder is not None: new_extruder_stacks = [active_extruder] else: # if there is no active extruder, use the first one in the active extruder stacks active_extruder = extruder_stacks[0] extruder_stacks = new_extruder_stacks + extruder_stacks # Fetch the list of useable qualities across all extruders. # The actual list of quality profiles come from the first extruder in the extruder list. quality_list = quality_manager.findAllUsableQualitiesForMachineAndExtruders( global_container_stack, extruder_stacks) # Filter the quality_change by the list of available quality_types quality_type_set = set( [x.getMetaDataEntry("quality_type") for x in quality_list]) if multiple_extrusion: # If the printer has multiple extruders then quality changes related to the current extruder are kept filtered_quality_changes = [ qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and qc.getMetaDataEntry("extruder") is not None and (qc.getMetaDataEntry("extruder") == active_extruder.definition. getMetaDataEntry("quality_definition") or qc.getMetaDataEntry( "extruder") == active_extruder.definition.getId()) ] else: # If not, the quality changes of the global stack are selected filtered_quality_changes = [ qc for qc in quality_changes_list if qc.getMetaDataEntry("quality_type") in quality_type_set and qc.getMetaDataEntry("extruder") is None ] return quality_list + filtered_quality_changes
def _update(self): item_dict = OrderedDict() item_list = [] global_stack = Application.getInstance().getGlobalContainerStack() if not global_stack: return stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() # Check if the definition container has a translation file and ensure it's loaded. definition = global_stack.getBottom() definition_suffix = ContainerRegistry.getMimeTypeForContainer(type(definition)).preferredSuffix catalog = i18nCatalog(os.path.basename(definition.getId() + "." + definition_suffix)) if catalog.hasTranslationLoaded(): self._i18n_catalog = catalog for file_name in definition.getInheritedFiles(): catalog = i18nCatalog(os.path.basename(file_name)) if catalog.hasTranslationLoaded(): self._i18n_catalog = catalog for stack in stacks: # Make a list of all containers in the stack. containers = [] latest_stack = stack while latest_stack: containers.extend(latest_stack.getContainers()) latest_stack = latest_stack.getNextStack() # Override "getExtruderValue" with "getDefaultExtruderValue" so we can get the default values user_changes = containers.pop(0) default_value_resolve_context = PropertyEvaluationContext(stack) default_value_resolve_context.context["evaluate_from_container_index"] = 1 # skip the user settings container default_value_resolve_context.context["override_operators"] = { "extruderValue": ExtruderManager.getDefaultExtruderValue, "extruderValues": ExtruderManager.getDefaultExtruderValues, "resolveOrValue": ExtruderManager.getDefaultResolveOrValue } for setting_key in user_changes.getAllKeys(): if setting_key == "print_mode": continue original_value = None # Find the category of the instance by moving up until we find a category. category = user_changes.getInstance(setting_key).definition while category.type != "category": category = category.parent # Handle translation (and fallback if we weren't able to find any translation files. if self._i18n_catalog: category_label = self._i18n_catalog.i18nc(category.key + " label", category.label) else: category_label = category.label if self._i18n_catalog: label = self._i18n_catalog.i18nc(setting_key + " label", stack.getProperty(setting_key, "label")) else: label = stack.getProperty(setting_key, "label") for container in containers: if stack == global_stack: resolve = global_stack.getProperty(setting_key, "resolve", default_value_resolve_context) if resolve is not None: original_value = resolve break original_value = container.getProperty(setting_key, "value", default_value_resolve_context) # If a value is a function, ensure it's called with the stack it's in. if isinstance(original_value, SettingFunction): original_value = original_value(stack, default_value_resolve_context) if original_value is not None: break item_to_add = {"key": setting_key, "label": label, "user_value": str(user_changes.getProperty(setting_key, "value")), "original_value": str(original_value), "extruder": "", "category": category_label} if stack != global_stack: item_to_add["extruder"] = stack.getName() if category_label not in item_dict: item_dict[category_label] = [] item_dict[category_label].append(item_to_add) for each_item_list in item_dict.values(): item_list += each_item_list self.setItems(item_list)
def _getFilteredContainersForStack( self, machine_definition: "DefinitionContainerInterface" = None, material_containers: List[InstanceContainer] = None, **kwargs): # Fill in any default values. if machine_definition is None: machine_definition = Application.getInstance( ).getGlobalContainerStack().getBottom() quality_definition_id = machine_definition.getMetaDataEntry( "quality_definition") if quality_definition_id is not None: machine_definition = ContainerRegistry.getInstance( ).findDefinitionContainers(id=quality_definition_id)[0] # for convenience if material_containers is None: material_containers = [] if not material_containers: active_stacks = ExtruderManager.getInstance( ).getActiveGlobalAndExtruderStacks() if active_stacks: material_containers = [ stack.material for stack in active_stacks ] criteria = kwargs filter_by_material = False machine_definition = self.getParentMachineDefinition( machine_definition) whole_machine_definition = self.getWholeMachineDefinition( machine_definition) if whole_machine_definition.getMetaDataEntry("has_machine_quality"): definition_id = machine_definition.getMetaDataEntry( "quality_definition", whole_machine_definition.getId()) criteria["definition"] = definition_id filter_by_material = whole_machine_definition.getMetaDataEntry( "has_materials") else: criteria["definition"] = "fdmprinter" # Stick the material IDs in a set material_ids = set() for material_instance in material_containers: if material_instance is not None: # Add the parent material too. for basic_material in self._getBasicMaterials( material_instance): material_ids.add(basic_material.getId()) material_ids.add(material_instance.getId()) containers = ContainerRegistry.getInstance().findInstanceContainers( **criteria) result = [] for container in containers: # If the machine specifies we should filter by material, exclude containers that do not match any active material. if filter_by_material and container.getMetaDataEntry( "material" ) not in material_ids and "global_quality" not in kwargs: continue result.append(container) return result
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 setActiveExtruder(self, extruder_stack_id): self._extruder_stack = extruder_stack_id self._updateNextStack() ExtruderManager.getInstance().resetSelectedObjectExtruders() self.activeExtruderChanged.emit()
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 node_position = node.callDecoration("getActiveExtruderPosition") if not stack.extruders[str(node_position)].isEnabled: 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) # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not object_groups: self.setResult(StartJobResult.NothingToSlice) return self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()): 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() 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 _onStartSliceCompleted(self, job: StartSliceJob) -> None: """Event handler to call when the job to initiate the slicing process is completed. When the start slice job is successfully completed, it will be happily slicing. This function handles any errors that may occur during the bootstrapping of a slice job. :param job: The start slice job that was just finished. """ 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.setState(BackendState.Error) self.backendError.emit(job) return application = CuraApplication.getInstance() if job.getResult() == StartJobResult.MaterialIncompatible: if 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.setState(BackendState.Error) self.backendError.emit(job) else: self.setState(BackendState.NotStarted) return if job.getResult() == StartJobResult.SettingError: if application.platformActivity: if not self._global_container_stack: Logger.log( "w", "Global container stack not assigned to CuraEngineBackend!" ) return extruders = ExtruderManager.getInstance( ).getActiveExtruderStacks() 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.setState(BackendState.Error) self.backendError.emit(job) else: self.setState(BackendState.NotStarted) return elif job.getResult() == StartJobResult.ObjectSettingError: errors = {} for node in DepthFirstIterator( application.getController().getScene().getRoot()): 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.setState(BackendState.Error) self.backendError.emit(job) return if job.getResult() == StartJobResult.BuildPlateError: if 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.setState(BackendState.Error) self.backendError.emit(job) else: self.setState(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.setState(BackendState.Error) self.backendError.emit(job) return if job.getResult() == StartJobResult.NothingToSlice: if application.platformActivity: self._error_message = Message(catalog.i18nc( "@info:status", "Please review settings and check if your models:" "\n- Fit within the build volume" "\n- Are assigned to an enabled extruder" "\n- Are not all set as modifier meshes"), title=catalog.i18nc( "@info:title", "Unable to slice")) self._error_message.show() self.setState(BackendState.Error) self.backendError.emit(job) else: self.setState(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.setState(BackendState.Processing) if self._slice_start_time: Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time)
def _computeDisallowedAreasStatic(self, border_size, used_extruders): #Convert disallowed areas to polygons and dilate them. machine_disallowed_polygons = [] for area in self._global_container_stack.getProperty( "machine_disallowed_areas", "value"): polygon = Polygon(numpy.array(area, numpy.float32)) polygon = polygon.getMinkowskiHull( Polygon.approximatedCircle(border_size)) machine_disallowed_polygons.append(polygon) result = {} for extruder in used_extruders: extruder_id = extruder.getId() offset_x = extruder.getProperty("machine_nozzle_offset_x", "value") if not offset_x: offset_x = 0 offset_y = extruder.getProperty("machine_nozzle_offset_y", "value") if not offset_y: offset_y = 0 result[extruder_id] = [] for polygon in machine_disallowed_polygons: result[extruder_id].append( polygon.translate(offset_x, offset_y) ) #Compensate for the nozzle offset of this extruder. #Add the border around the edge of the build volume. left_unreachable_border = 0 right_unreachable_border = 0 top_unreachable_border = 0 bottom_unreachable_border = 0 #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders. for other_extruder in ExtruderManager.getInstance( ).getActiveExtruderStacks(): other_offset_x = other_extruder.getProperty( "machine_nozzle_offset_x", "value") other_offset_y = other_extruder.getProperty( "machine_nozzle_offset_y", "value") left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x) right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x) top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y) bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y) half_machine_width = self._global_container_stack.getProperty( "machine_width", "value") / 2 half_machine_depth = self._global_container_stack.getProperty( "machine_depth", "value") / 2 if border_size - left_unreachable_border > 0: result[extruder_id].append( Polygon( numpy.array( [[-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], [ -half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border ], [ -half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border ]], numpy.float32))) if border_size + right_unreachable_border > 0: result[extruder_id].append( Polygon( numpy.array( [[half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], [ half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border ], [ half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border ]], numpy.float32))) if border_size + bottom_unreachable_border > 0: result[extruder_id].append( Polygon( numpy.array( [[-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], [ half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border ], [ -half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border ]], numpy.float32))) if border_size - top_unreachable_border > 0: result[extruder_id].append( Polygon( numpy.array( [[half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], [ -half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border ], [ half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border ]], numpy.float32))) return result
def _calculateInformation(self): if Application.getInstance().getGlobalContainerStack() is None: return # Material amount is sent as an amount of mm^3, so calculate length from that radius = Application.getInstance().getGlobalContainerStack( ).getProperty("material_diameter", "value") / 2 self._material_lengths = [] self._material_weights = [] self._material_costs = [] self._material_names = [] material_preference_values = json.loads( Preferences.getInstance().getValue("cura/material_settings")) extruder_stacks = list( ExtruderManager.getInstance().getMachineExtruders( Application.getInstance().getGlobalContainerStack().getId())) for index, amount in enumerate(self._material_amounts): ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some # list comprehension filtering to solve this for us. material = None if extruder_stacks: # Multi extrusion machine extruder_stack = [ extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index) ][0] density = extruder_stack.getMetaDataEntry("properties", {}).get( "density", 0) material = extruder_stack.findContainer({"type": "material"}) else: # Machine with no extruder stacks density = Application.getInstance().getGlobalContainerStack( ).getMetaDataEntry("properties", {}).get("density", 0) material = Application.getInstance().getGlobalContainerStack( ).findContainer({"type": "material"}) weight = float(amount) * float(density) / 1000 cost = 0 material_name = catalog.i18nc("@label unknown material", "Unknown") if material: material_guid = material.getMetaDataEntry("GUID") material_name = material.getName() if material_guid in material_preference_values: material_values = material_preference_values[material_guid] weight_per_spool = float( material_values["spool_weight"] if material_values and "spool_weight" in material_values else 0) cost_per_spool = float( material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0) if weight_per_spool != 0: cost = cost_per_spool * weight / weight_per_spool else: cost = 0 if radius != 0: length = round((amount / (math.pi * radius**2)) / 1000, 2) else: length = 0 self._material_weights.append(weight) self._material_lengths.append(length) self._material_costs.append(cost) self._material_names.append(material_name) self.materialLengthsChanged.emit() self.materialWeightsChanged.emit() self.materialCostsChanged.emit() self.materialNamesChanged.emit()
def _getSettingFromAllExtruders(self, setting_key, property = "value"): return ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property)
def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() if not self._theme: self._theme = Application.getInstance().getTheme() if not self._enabled_shader: self._enabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "overhang.shader")) self._enabled_shader.setUniformValue("u_overhangColor", Color(*self._theme.getColor("model_overhang").getRgb())) if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) self._disabled_shader.setUniformValue("u_diffuseColor1", Color(*self._theme.getColor("model_unslicable").getRgb())) self._disabled_shader.setUniformValue("u_diffuseColor2", Color(*self._theme.getColor("model_unslicable_alt").getRgb())) self._disabled_shader.setUniformValue("u_width", 50.0) if not 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", Color(*self._theme.getColor("model_non_printing").getRgb())) 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")) self._support_mesh_shader.setUniformValue("u_vertical_stripes", True) self._support_mesh_shader.setUniformValue("u_width", 5.0) global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: support_extruder_nr = global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr") support_angle_stack = Application.getInstance().getExtruderManager().getExtruderStack(support_extruder_nr) if support_angle_stack is not None and Preferences.getInstance().getValue("view/show_overhang"): angle = support_angle_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))) 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 = int(node.callDecoration("getActiveExtruderPosition")) # 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 ] 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 _convertSavitarNodeToUMNode( self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]: """Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node. :returns: Scene node. """ try: node_name = savitar_node.getName() node_id = savitar_node.getId() except AttributeError: Logger.log( "e", "Outdated version of libSavitar detected! Please update to the newest version!" ) node_name = "" node_id = "" if node_name == "": if file_name != "": node_name = os.path.basename(file_name) else: node_name = "Object {}".format(node_id) active_build_plate = CuraApplication.getInstance( ).getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) um_node.setId(node_id) transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) if file_name: # The filename is used to give the user the option to reload the file if it is changed on disk # It is only set for the root node of the 3mf file mesh_builder.setFileName(file_name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = CuraApplication.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = ContainerTree.getInstance().machines[ global_container_stack.definition.getId( )].quality_definition um_node.callDecoration("getStack").getTop().setDefinition( definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: if len(um_node.getAllChildren()) == 1: # We don't want groups of one, so move the node up one "level" child_node = um_node.getChildren()[0] parent_transformation = um_node.getLocalTransformation() child_transformation = child_node.getLocalTransformation() child_node.setTransformation( parent_transformation.multiply(child_transformation)) um_node = cast(CuraSceneNode, um_node.getChildren()[0]) else: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def _computeDisallowedAreasStatic(self, border_size, used_extruders): #Convert disallowed areas to polygons and dilate them. machine_disallowed_polygons = [] for area in self._global_container_stack.getProperty("machine_disallowed_areas", "value"): polygon = Polygon(numpy.array(area, numpy.float32)) polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(border_size)) machine_disallowed_polygons.append(polygon) result = {} for extruder in used_extruders: extruder_id = extruder.getId() offset_x = extruder.getProperty("machine_nozzle_offset_x", "value") if offset_x is None: offset_x = 0 offset_y = extruder.getProperty("machine_nozzle_offset_y", "value") if offset_y is None: offset_y = 0 result[extruder_id] = [] for polygon in machine_disallowed_polygons: result[extruder_id].append(polygon.translate(offset_x, offset_y)) #Compensate for the nozzle offset of this extruder. #Add the border around the edge of the build volume. left_unreachable_border = 0 right_unreachable_border = 0 top_unreachable_border = 0 bottom_unreachable_border = 0 #The build volume is defined as the union of the area that all extruders can reach, so we need to know the relative offset to all extruders. for other_extruder in ExtruderManager.getInstance().getActiveExtruderStacks(): other_offset_x = other_extruder.getProperty("machine_nozzle_offset_x", "value") other_offset_y = other_extruder.getProperty("machine_nozzle_offset_y", "value") left_unreachable_border = min(left_unreachable_border, other_offset_x - offset_x) right_unreachable_border = max(right_unreachable_border, other_offset_x - offset_x) top_unreachable_border = min(top_unreachable_border, other_offset_y - offset_y) bottom_unreachable_border = max(bottom_unreachable_border, other_offset_y - offset_y) half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2 half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2 if self._shape != "elliptic": if border_size - left_unreachable_border > 0: result[extruder_id].append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border], [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border] ], numpy.float32))) if border_size + right_unreachable_border > 0: result[extruder_id].append(Polygon(numpy.array([ [half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border], [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border] ], numpy.float32))) if border_size + bottom_unreachable_border > 0: result[extruder_id].append(Polygon(numpy.array([ [-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], [half_machine_width - border_size - right_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border], [-half_machine_width + border_size - left_unreachable_border, half_machine_depth - border_size - bottom_unreachable_border] ], numpy.float32))) if border_size - top_unreachable_border > 0: result[extruder_id].append(Polygon(numpy.array([ [half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], [-half_machine_width + border_size - left_unreachable_border, -half_machine_depth + border_size - top_unreachable_border], [half_machine_width - border_size - right_unreachable_border, -half_machine_depth + border_size - top_unreachable_border] ], numpy.float32))) else: sections = 32 arc_vertex = [0, half_machine_depth - border_size] for i in range(0, sections): quadrant = math.floor(4 * i / sections) vertices = [] if quadrant == 0: vertices.append([-half_machine_width, half_machine_depth]) elif quadrant == 1: vertices.append([-half_machine_width, -half_machine_depth]) elif quadrant == 2: vertices.append([half_machine_width, -half_machine_depth]) elif quadrant == 3: vertices.append([half_machine_width, half_machine_depth]) vertices.append(arc_vertex) angle = 2 * math.pi * (i + 1) / sections arc_vertex = [-(half_machine_width - border_size) * math.sin(angle), (half_machine_depth - border_size) * math.cos(angle)] vertices.append(arc_vertex) result[extruder_id].append(Polygon(numpy.array(vertices, numpy.float32))) if border_size > 0: result[extruder_id].append(Polygon(numpy.array([ [-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], [-half_machine_width + border_size, 0] ], numpy.float32))) result[extruder_id].append(Polygon(numpy.array([ [-half_machine_width, half_machine_depth], [ half_machine_width, half_machine_depth], [ 0, half_machine_depth - border_size] ], numpy.float32))) result[extruder_id].append(Polygon(numpy.array([ [ half_machine_width, half_machine_depth], [ half_machine_width, -half_machine_depth], [ half_machine_width - border_size, 0] ], numpy.float32))) result[extruder_id].append(Polygon(numpy.array([ [ half_machine_width,-half_machine_depth], [-half_machine_width,-half_machine_depth], [ 0, -half_machine_depth + border_size] ], numpy.float32))) return result
def _updateDisallowedAreas(self): if not self._global_container_stack: return self._error_areas = [] extruder_manager = ExtruderManager.getInstance() used_extruders = extruder_manager.getUsedExtruderStacks() disallowed_border_size = self._getEdgeDisallowedSize() result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added. prime_areas = self._computeDisallowedAreasPrime(disallowed_border_size, used_extruders) prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking. #Check if prime positions intersect with disallowed areas. for extruder in used_extruders: extruder_id = extruder.getId() collision = False for prime_polygon in prime_areas[extruder_id]: for disallowed_polygon in prime_disallowed_areas[extruder_id]: if prime_polygon.intersectsPolygon(disallowed_polygon) is not None: collision = True break if collision: break #Also check other prime positions (without additional offset). for other_extruder_id in prime_areas: if extruder_id == other_extruder_id: #It is allowed to collide with itself. continue for other_prime_polygon in prime_areas[other_extruder_id]: if prime_polygon.intersectsPolygon(other_prime_polygon): collision = True break if collision: break if collision: break result_areas[extruder_id].extend(prime_areas[extruder_id]) nozzle_disallowed_areas = extruder.getProperty("nozzle_disallowed_areas", "value") for area in nozzle_disallowed_areas: polygon = Polygon(numpy.array(area, numpy.float32)) polygon = polygon.getMinkowskiHull(Polygon.approximatedCircle(disallowed_border_size)) result_areas[extruder_id].append(polygon) #Don't perform the offset on these. # Add prime tower location as disallowed area. prime_tower_collision = False prime_tower_areas = self._computeDisallowedAreasPrinted(used_extruders) for extruder_id in prime_tower_areas: for prime_tower_area in prime_tower_areas[extruder_id]: for area in result_areas[extruder_id]: if prime_tower_area.intersectsPolygon(area) is not None: prime_tower_collision = True break if prime_tower_collision: #Already found a collision. break if not prime_tower_collision: result_areas[extruder_id].extend(prime_tower_areas[extruder_id]) else: self._error_areas.extend(prime_tower_areas[extruder_id]) self._has_errors = len(self._error_areas) > 0 self._disallowed_areas = [] for extruder_id in result_areas: self._disallowed_areas.extend(result_areas[extruder_id])
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 read(self, file_name): archive = zipfile.ZipFile(file_name, "r") cura_file_names = [ name for name in archive.namelist() if name.startswith("Cura/") ] # Create a shadow copy of the preferences (we don't want all of the preferences, but we do want to re-use its # parsing code. temp_preferences = Preferences() temp_preferences.readFromFile( io.TextIOWrapper(archive.open("Cura/preferences.cfg")) ) # We need to wrap it, else the archive parser breaks. # Copy a number of settings from the temp preferences to the global global_preferences = Preferences.getInstance() visible_settings = temp_preferences.getValue( "general/visible_settings") if visible_settings is None: Logger.log( "w", "Workspace did not contain visible settings. Leaving visibility unchanged" ) else: global_preferences.setValue("general/visible_settings", visible_settings) categories_expanded = temp_preferences.getValue( "cura/categories_expanded") if categories_expanded is None: Logger.log( "w", "Workspace did not contain expanded categories. Leaving them unchanged" ) else: global_preferences.setValue("cura/categories_expanded", categories_expanded) Application.getInstance().expandedCategoriesChanged.emit( ) # Notify the GUI of the change self._id_mapping = {} # We don't add containers right away, but wait right until right before the stack serialization. # We do this so that if something goes wrong, it's easier to clean up. containers_to_add = [] # TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few # TODO: cases that the container loaded is the same (most notable in materials & definitions). # TODO: It might be possible that we need to add smarter checking in the future. Logger.log("d", "Workspace loading is checking definitions...") # Get all the definition files & check if they exist. If not, add them. definition_container_files = [ name for name in cura_file_names if name.endswith(self._definition_container_suffix) ] for definition_container_file in definition_container_files: container_id = self._stripFileToId(definition_container_file) definitions = self._container_registry.findDefinitionContainers( id=container_id) if not definitions: definition_container = DefinitionContainer(container_id) definition_container.deserialize( archive.open(definition_container_file).read().decode( "utf-8")) self._container_registry.addContainer(definition_container) Job.yieldThread() Logger.log("d", "Workspace loading is checking materials...") material_containers = [] # Get all the material files and check if they exist. If not, add them. xml_material_profile = self._getXmlProfileClass() if self._material_container_suffix is None: self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer( xml_material_profile).suffixes[0] if xml_material_profile: material_container_files = [ name for name in cura_file_names if name.endswith(self._material_container_suffix) ] for material_container_file in material_container_files: container_id = self._stripFileToId(material_container_file) materials = self._container_registry.findInstanceContainers( id=container_id) if not materials: material_container = xml_material_profile(container_id) material_container.deserialize( archive.open(material_container_file).read().decode( "utf-8")) containers_to_add.append(material_container) else: if not materials[0].isReadOnly( ): # Only create new materials if they are not read only. if self._resolve_strategies["material"] == "override": materials[0].deserialize( archive.open(material_container_file).read(). decode("utf-8")) elif self._resolve_strategies["material"] == "new": # Note that we *must* deserialize it with a new ID, as multiple containers will be # auto created & added. material_container = xml_material_profile( self.getNewId(container_id)) material_container.deserialize( archive.open(material_container_file).read(). decode("utf-8")) containers_to_add.append(material_container) material_containers.append(material_container) Job.yieldThread() Logger.log("d", "Workspace loading is checking instance containers...") # Get quality_changes and user profiles saved in the workspace instance_container_files = [ name for name in cura_file_names if name.endswith(self._instance_container_suffix) ] user_instance_containers = [] quality_changes_instance_containers = [] for instance_container_file in instance_container_files: container_id = self._stripFileToId(instance_container_file) instance_container = InstanceContainer(container_id) # Deserialize InstanceContainer by converting read data from bytes to string instance_container.deserialize( archive.open(instance_container_file).read().decode("utf-8")) container_type = instance_container.getMetaDataEntry("type") Job.yieldThread() if container_type == "user": # Check if quality changes already exists. user_containers = self._container_registry.findInstanceContainers( id=container_id) if not user_containers: containers_to_add.append(instance_container) else: if self._resolve_strategies[ "machine"] == "override" or self._resolve_strategies[ "machine"] is None: user_containers[0].deserialize( archive.open(instance_container_file).read(). decode("utf-8")) elif self._resolve_strategies["machine"] == "new": # The machine is going to get a spiffy new name, so ensure that the id's of user settings match. extruder_id = instance_container.getMetaDataEntry( "extruder", None) if extruder_id: new_id = self.getNewId( extruder_id) + "_current_settings" instance_container._id = new_id instance_container.setName(new_id) instance_container.setMetaDataEntry( "extruder", self.getNewId(extruder_id)) containers_to_add.append(instance_container) machine_id = instance_container.getMetaDataEntry( "machine", None) if machine_id: new_id = self.getNewId( machine_id) + "_current_settings" instance_container._id = new_id instance_container.setName(new_id) instance_container.setMetaDataEntry( "machine", self.getNewId(machine_id)) containers_to_add.append(instance_container) user_instance_containers.append(instance_container) elif container_type == "quality_changes": # Check if quality changes already exists. quality_changes = self._container_registry.findInstanceContainers( id=container_id) if not quality_changes: containers_to_add.append(instance_container) else: if self._resolve_strategies[ "quality_changes"] == "override": quality_changes[0].deserialize( archive.open(instance_container_file).read(). decode("utf-8")) elif self._resolve_strategies["quality_changes"] is None: # The ID already exists, but nothing in the values changed, so do nothing. pass quality_changes_instance_containers.append(instance_container) else: continue # Add all the containers right before we try to add / serialize the stack for container in containers_to_add: self._container_registry.addContainer(container) container.setDirty(True) # Get the stack(s) saved in the workspace. Logger.log("d", "Workspace loading is checking stacks containers...") container_stack_files = [ name for name in cura_file_names if name.endswith(self._container_stack_suffix) ] global_stack = None extruder_stacks = [] container_stacks_added = [] try: for container_stack_file in container_stack_files: container_id = self._stripFileToId(container_stack_file) # Check if a stack by this ID already exists; container_stacks = self._container_registry.findContainerStacks( id=container_id) if container_stacks: stack = container_stacks[0] if self._resolve_strategies["machine"] == "override": # TODO: HACK # There is a machine, check if it has authenticationd data. If so, keep that data. network_authentication_id = container_stacks[ 0].getMetaDataEntry("network_authentication_id") network_authentication_key = container_stacks[ 0].getMetaDataEntry("network_authentication_key") container_stacks[0].deserialize( archive.open(container_stack_file).read().decode( "utf-8")) if network_authentication_id: container_stacks[0].addMetaDataEntry( "network_authentication_id", network_authentication_id) if network_authentication_key: container_stacks[0].addMetaDataEntry( "network_authentication_key", network_authentication_key) elif self._resolve_strategies["machine"] == "new": new_id = self.getNewId(container_id) stack = ContainerStack(new_id) stack.deserialize( archive.open(container_stack_file).read().decode( "utf-8")) # Ensure a unique ID and name stack._id = new_id # Extruder stacks are "bound" to a machine. If we add the machine as a new one, the id of the # bound machine also needs to change. if stack.getMetaDataEntry("machine", None): stack.setMetaDataEntry( "machine", self.getNewId( stack.getMetaDataEntry("machine"))) if stack.getMetaDataEntry("type") != "extruder_train": # Only machines need a new name, stacks may be non-unique stack.setName( self._container_registry.uniqueName( stack.getName())) container_stacks_added.append(stack) self._container_registry.addContainer(stack) else: Logger.log( "w", "Resolve strategy of %s for machine is not supported", self._resolve_strategies["machine"]) else: stack = ContainerStack(container_id) # Deserialize stack by converting read data from bytes to string stack.deserialize( archive.open(container_stack_file).read().decode( "utf-8")) container_stacks_added.append(stack) self._container_registry.addContainer(stack) if stack.getMetaDataEntry("type") == "extruder_train": extruder_stacks.append(stack) else: global_stack = stack Job.yieldThread() except: Logger.logException( "w", "We failed to serialize the stack. Trying to clean up.") # Something went really wrong. Try to remove any data that we added. for container in containers_to_add: self._container_registry.getInstance().removeContainer( container.getId()) for container in container_stacks_added: self._container_registry.getInstance().removeContainer( container.getId()) return None if self._resolve_strategies["machine"] == "new": # A new machine was made, but it was serialized with the wrong user container. Fix that now. for container in user_instance_containers: extruder_id = container.getMetaDataEntry("extruder", None) if extruder_id: for extruder in extruder_stacks: if extruder.getId() == extruder_id: extruder.replaceContainer(0, container) continue machine_id = container.getMetaDataEntry("machine", None) if machine_id: if global_stack.getId() == machine_id: global_stack.replaceContainer(0, container) continue if self._resolve_strategies["quality_changes"] == "new": # Quality changes needs to get a new ID, added to registry and to the right stacks for container in quality_changes_instance_containers: old_id = container.getId() container.setName( self._container_registry.uniqueName(container.getName())) # We're not really supposed to change the ID in normal cases, but this is an exception. container._id = self.getNewId(container.getId()) # The container was not added yet, as it didn't have an unique ID. It does now, so add it. self._container_registry.addContainer(container) # Replace the quality changes container old_container = global_stack.findContainer( {"type": "quality_changes"}) if old_container.getId() == old_id: quality_changes_index = global_stack.getContainerIndex( old_container) global_stack.replaceContainer(quality_changes_index, container) continue for stack in extruder_stacks: old_container = stack.findContainer( {"type": "quality_changes"}) if old_container.getId() == old_id: quality_changes_index = stack.getContainerIndex( old_container) stack.replaceContainer(quality_changes_index, container) if self._resolve_strategies["material"] == "new": for material in material_containers: old_material = global_stack.findContainer({"type": "material"}) if old_material.getId() in self._id_mapping: material_index = global_stack.getContainerIndex( old_material) global_stack.replaceContainer(material_index, material) continue for stack in extruder_stacks: old_material = stack.findContainer({"type": "material"}) if old_material.getId() in self._id_mapping: material_index = stack.getContainerIndex(old_material) stack.replaceContainer(material_index, material) continue for stack in extruder_stacks: ExtruderManager.getInstance().registerExtruder( stack, global_stack.getId()) else: # Machine has no extruders, but it needs to be registered with the extruder manager. ExtruderManager.getInstance().registerExtruder( None, global_stack.getId()) Logger.log( "d", "Workspace loading is notifying rest of the code of changes...") # Notify everything/one that is to notify about changes. global_stack.containersChanged.emit(global_stack.getTop()) for stack in extruder_stacks: stack.setNextStack(global_stack) stack.containersChanged.emit(stack.getTop()) # Actually change the active machine. Application.getInstance().setGlobalContainerStack(global_stack) # Load all the nodes / meshdata of the workspace nodes = self._3mf_mesh_reader.read(file_name) if nodes is None: nodes = [] return nodes
def _updateDisallowedAreas(self): if not self._global_container_stack: return self._has_errors = False # Reset. self._error_areas = [] disallowed_areas = copy.deepcopy( self._global_container_stack.getProperty( "machine_disallowed_areas", "value")) areas = [] machine_width = self._global_container_stack.getProperty( "machine_width", "value") machine_depth = self._global_container_stack.getProperty( "machine_depth", "value") prime_tower_area = None # Add prime tower location as disallowed area. if ExtruderManager.getInstance().getResolveOrValue( "prime_tower_enable") == True: prime_tower_size = self._global_container_stack.getProperty( "prime_tower_size", "value") prime_tower_x = self._global_container_stack.getProperty( "prime_tower_position_x", "value") - machine_width / 2 prime_tower_y = -self._global_container_stack.getProperty( "prime_tower_position_y", "value") + machine_depth / 2 prime_tower_area = Polygon([ [ prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size ], [prime_tower_x, prime_tower_y - prime_tower_size], [prime_tower_x, prime_tower_y], [prime_tower_x - prime_tower_size, prime_tower_y], ]) disallowed_polygons = [] # Check if prime positions intersect with disallowed areas prime_collision = False if disallowed_areas: for area in disallowed_areas: poly = Polygon(numpy.array(area, numpy.float32)) # Minkowski with zero, to ensure that the polygon is correct & watertight. poly = poly.getMinkowskiHull(Polygon.approximatedCircle(0)) disallowed_polygons.append(poly) extruder_manager = ExtruderManager.getInstance() extruders = extruder_manager.getMachineExtruders( self._global_container_stack.getId()) prime_polygons = [] # Each extruder has it's own prime location for extruder in extruders: prime_x = extruder.getProperty("extruder_prime_pos_x", "value") - machine_width / 2 prime_y = machine_depth / 2 - extruder.getProperty( "extruder_prime_pos_y", "value") prime_polygon = Polygon([ [prime_x - PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE], [prime_x + PRIME_CLEARANCE, prime_y - PRIME_CLEARANCE], [prime_x + PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], [prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE], ]) prime_polygon = prime_polygon.getMinkowskiHull( Polygon.approximatedCircle(0)) collision = False # Check if prime polygon is intersecting with any of the other disallowed areas. # Note that we check the prime area without bed adhesion. for poly in disallowed_polygons: if prime_polygon.intersectsPolygon(poly) is not None: collision = True break # Also collide with other prime positions for poly in prime_polygons: if prime_polygon.intersectsPolygon(poly) is not None: collision = True break if not collision: # Prime area is valid. Add as normal. # Once it's added like this, it will recieve a bed adhesion offset, just like the others. prime_polygons.append(prime_polygon) else: self._error_areas.append(prime_polygon) prime_collision = collision or prime_collision disallowed_polygons.extend(prime_polygons) disallowed_border_size = self._getEdgeDisallowedSize() # Extend every area already in the disallowed_areas with the skirt size. if disallowed_areas: for poly in disallowed_polygons: poly = poly.getMinkowskiHull( Polygon.approximatedCircle(disallowed_border_size)) areas.append(poly) # Add the skirt areas around the borders of the build plate. if disallowed_border_size > 0: half_machine_width = self._global_container_stack.getProperty( "machine_width", "value") / 2 half_machine_depth = self._global_container_stack.getProperty( "machine_depth", "value") / 2 areas.append( Polygon( numpy.array( [[-half_machine_width, -half_machine_depth], [-half_machine_width, half_machine_depth], [ -half_machine_width + disallowed_border_size, half_machine_depth - disallowed_border_size ], [ -half_machine_width + disallowed_border_size, -half_machine_depth + disallowed_border_size ]], numpy.float32))) areas.append( Polygon( numpy.array( [[half_machine_width, half_machine_depth], [half_machine_width, -half_machine_depth], [ half_machine_width - disallowed_border_size, -half_machine_depth + disallowed_border_size ], [ half_machine_width - disallowed_border_size, half_machine_depth - disallowed_border_size ]], numpy.float32))) areas.append( Polygon( numpy.array( [[-half_machine_width, half_machine_depth], [half_machine_width, half_machine_depth], [ half_machine_width - disallowed_border_size, half_machine_depth - disallowed_border_size ], [ -half_machine_width + disallowed_border_size, half_machine_depth - disallowed_border_size ]], numpy.float32))) areas.append( Polygon( numpy.array( [[half_machine_width, -half_machine_depth], [-half_machine_width, -half_machine_depth], [ -half_machine_width + disallowed_border_size, -half_machine_depth + disallowed_border_size ], [ half_machine_width - disallowed_border_size, -half_machine_depth + disallowed_border_size ]], numpy.float32))) # Check if the prime tower area intersects with any of the other areas. # If this is the case, add it to the error area's so it can be drawn in red. # If not, add it back to disallowed area's, so it's rendered as normal. prime_tower_collision = False if prime_tower_area: # Using Minkowski of 0 fixes the prime tower area so it's rendered correctly prime_tower_area = prime_tower_area.getMinkowskiHull( Polygon.approximatedCircle(0)) for area in areas: if prime_tower_area.intersectsPolygon(area) is not None: prime_tower_collision = True break if not prime_tower_collision: areas.append(prime_tower_area) else: self._error_areas.append(prime_tower_area) # The buildplate has errors if either prime tower or prime has a colission. self._has_errors = prime_tower_collision or prime_collision self._disallowed_areas = areas
def read(self, file_name): if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance( ).getGlobalContainerStack() if not global_container_stack: return None multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 if multi_extrusion: Logger.log( "e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name) raise Exception( "Unable to import legacy profile. Multi extrusion is not supported" ) Logger.log("i", "Importing legacy profile from file " + file_name + ".") container_registry = ContainerRegistry.getInstance() profile_id = container_registry.uniqueName("Imported Legacy Profile") profile = InstanceContainer(profile_id) # Create an empty profile. parser = configparser.ConfigParser(interpolation=None) try: parser.read([file_name]) # Parse the INI file. except Exception as e: Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e)) return None # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile". # Since importing multiple machine profiles is out of scope, just import the first section we find. section = "" for found_section in parser.sections(): if found_section.startswith("profile"): section = found_section break if not section: # No section starting with "profile" was found. Probably not a proper INI file. return None try: with open( os.path.join( PluginRegistry.getInstance().getPluginPath( "LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f: dict_of_doom = json.load(f) # Parse the Dictionary of Doom. except IOError as e: Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) return None except Exception as e: Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e)) return None defaults = self.prepareDefaults(dict_of_doom) legacy_settings = self.prepareLocals( parser, section, defaults) #Gets the settings from the legacy profile. #Check the target version in the Dictionary of Doom with this application version. if "target_version" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no target version. Is it the correct JSON file?" ) return None if InstanceContainer.Version != dict_of_doom["target_version"]: Logger.log( "e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version)) return None if "translation" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no translation. Is it the correct JSON file?" ) return None current_printer_definition = global_container_stack.definition profile.setDefinition(current_printer_definition.getId()) for new_setting in dict_of_doom[ "translation"]: # Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") try: new_value = eval( compiled, {"math": math}, legacy_settings ) # Pass the legacy settings as local variables to allow access to in the evaluation. value_using_defaults = eval( compiled, {"math": math}, defaults ) #Evaluate again using only the default values to try to see if they are default. except Exception: # Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log( "w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile." ) continue definitions = current_printer_definition.findDefinitions( key=new_setting) if definitions: if new_value != value_using_defaults and definitions[ 0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. profile.setProperty( new_setting, "value", new_value) # Store the setting in the profile! if len(profile.getAllKeys()) == 0: Logger.log( "i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile." ) profile.addMetaDataEntry("type", "profile") # don't know what quality_type it is based on, so use "normal" by default profile.addMetaDataEntry("quality_type", "normal") profile.setName(profile_id) profile.setDirty(True) #Serialise and deserialise in order to perform the version upgrade. parser = configparser.ConfigParser(interpolation=None) data = profile.serialize() parser.read_string(data) parser["general"]["version"] = "1" if parser.has_section("values"): parser["settings"] = parser["values"] del parser["values"] stream = io.StringIO() parser.write(stream) data = stream.getvalue() profile.deserialize(data) #We need to return one extruder stack and one global stack. global_container_id = container_registry.uniqueName( "Global Imported Legacy Profile") global_profile = profile.duplicate( new_id=global_container_id, new_name=profile_id ) #Needs to have the same name as the extruder profile. global_profile.setDirty(True) #Only the extruder stack has an extruder metadata entry. profile.addMetaDataEntry( "extruder", ExtruderManager.getInstance().getActiveExtruderStack().definition. getId()) #Split all settings into per-extruder and global settings. for setting_key in profile.getAllKeys(): settable_per_extruder = global_container_stack.getProperty( setting_key, "settable_per_extruder") if settable_per_extruder: global_profile.removeInstance(setting_key) else: profile.removeInstance(setting_key) return [global_profile, profile]
def setVisible(self, visible): if not self._node: return if not self._stack: self._node.addDecorator(SettingOverrideDecorator()) self._stack = self._node.callDecoration("getStack") settings = self._stack.getTop() all_instances = settings.findInstances() visibility_changed = False # Flag to check if at the end the signal needs to be emitted # Remove all instances that are not in visibility list for instance in all_instances: if instance.definition.key not in visible: settings.removeInstance(instance.definition.key) visibility_changed = True # Add all instances that are not added, but are in visibility list for item in visible: if not settings.getInstance( item): # Setting was not added already. definition = self._stack.getSettingDefinition(item) if definition: new_instance = SettingInstance(definition, settings) stack_nr = -1 stack = None # Check from what stack we should copy the raw property of the setting from. if self._stack.getProperty("machine_extruder_count", "value") > 1: if definition.limit_to_extruder != "-1": # A limit to extruder function was set and it's a multi extrusion machine. Check what stack we do need to use. stack_nr = str( int( round( float( self._stack.getProperty( item, "limit_to_extruder"))))) # Check if the found stack_number is in the extruder list of extruders. if stack_nr not in ExtruderManager.getInstance( ).extruderIds and self._stack.getProperty( "extruder_nr", "value") is not None: stack_nr = -1 # Use the found stack number to get the right stack to copy the value from. if stack_nr in ExtruderManager.getInstance( ).extruderIds: stack = ContainerRegistry.getInstance( ).findContainerStacks( id=ExtruderManager.getInstance( ).extruderIds[stack_nr])[0] else: stack = self._stack # Use the raw property to set the value (so the inheritance doesn't break) if stack is not None: new_instance.setProperty( "value", stack.getRawProperty(item, "value")) else: new_instance.setProperty("value", None) new_instance.resetState( ) # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) visibility_changed = True else: Logger.log( "w", "Unable to add instance (%s) to per-object visibility because we couldn't find the matching definition", item) if visibility_changed: self.visibilityChanged.emit()
def _convertSavitarNodeToUMNode(self, savitar_node): um_node = SceneNode() transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: um_node.addDecorator(SettingOverrideDecorator()) global_container_stack = Application.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 # Ensure that all extruder data is reset if not multi_extrusion: default_stack_id = global_container_stack.getId() else: default_stack = ExtruderManager.getInstance( ).getExtruderStack(0) if default_stack: default_stack_id = default_stack.getId() else: default_stack_id = global_container_stack.getId() um_node.callDecoration("setActiveExtruder", default_stack_id) # Get the definition & set it definition = QualityManager.getInstance( ).getParentMachineDefinition( global_container_stack.getBottom()) um_node.callDecoration("getStack").getTop().setDefinition( definition) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def 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.allActiveVariantIds[ extruder_manager.extruderIds["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 _updateExtruders(self): changed = False if self.rowCount() != 0: changed = True items = [] global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if self._add_global: material = global_container_stack.findContainer( {"type": "material"}) color = material.getMetaDataEntry( "color_code", default=self.defaultColors[0] ) if material else self.defaultColors[0] item = { "id": global_container_stack.getId(), "name": "Global", "color": color, "index": -1, "definition": "" } items.append(item) changed = True manager = ExtruderManager.getInstance() for extruder in manager.getMachineExtruders( global_container_stack.getId()): extruder_name = extruder.getName() material = extruder.findContainer({"type": "material"}) variant = extruder.findContainer({"type": "variant"}) position = extruder.getMetaDataEntry( "position", default="0") # Get the position try: position = int(position) except ValueError: #Not a proper int. position = -1 default_color = self.defaultColors[ position] if position >= 0 and position < len( self.defaultColors) else self.defaultColors[0] color = material.getMetaDataEntry( "color_code", default=default_color) if material else default_color item = { #Construct an item with only the relevant information. "id": extruder.getId(), "name": extruder_name, "color": color, "index": position, "definition": extruder.getBottom().getId(), "material": material.getName() if material else "", "variant": variant.getName() if variant else "", } items.append(item) changed = True if changed: items.sort(key=lambda i: i["index"]) self.setItems(items) self.modelChanged.emit()
def _serialiseSettings(self, stack): prefix = ";SETTING_" + str( GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) container_with_profile = stack.qualityChanges if not container_with_profile: Logger.log( "e", "No valid quality profile found, not writing settings to GCode!" ) return "" flat_global_container = self._createFlattenedContainerInstance( stack.getTop(), container_with_profile) # If the quality changes is not set, we need to set type manually if flat_global_container.getMetaDataEntry("type", None) is None: flat_global_container.addMetaDataEntry("type", "quality_changes") # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_global_container.getMetaDataEntry("quality_type", None) is None: flat_global_container.addMetaDataEntry( "quality_type", stack.findContainer({ "type": "quality" }).getMetaDataEntry("quality_type", "normal")) serialized = flat_global_container.serialize() data = {"global_quality": serialized} for extruder in sorted( ExtruderManager.getInstance().getMachineExtruders( stack.getId()), key=lambda k: k.getMetaDataEntry("position")): extruder_quality = extruder.qualityChanges if not extruder_quality: Logger.log( "w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId()) continue flat_extruder_quality = self._createFlattenedContainerInstance( extruder.getTop(), extruder_quality) # If the quality changes is not set, we need to set type manually if flat_extruder_quality.getMetaDataEntry("type", None) is None: flat_extruder_quality.addMetaDataEntry("type", "quality_changes") # Ensure that extruder is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("extruder", None) is None: flat_extruder_quality.addMetaDataEntry( "extruder", extruder.getBottom().getId()) # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None: flat_extruder_quality.addMetaDataEntry( "quality_type", extruder.findContainer({ "type": "quality" }).getMetaDataEntry("quality_type", "normal")) extruder_serialized = flat_extruder_quality.serialize() data.setdefault("extruder_quality", []).append(extruder_serialized) json_string = json.dumps(data) # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. escaped_string = pattern.sub( lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(escaped_string), 80 - prefix_length): result += prefix + escaped_string[pos:pos + 80 - prefix_length] + "\n" return result
def _onStartSliceCompleted(self, job): 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( ) == StartSliceJob.StartJobResult.Error: self.backendStateChange.emit(BackendState.Error) self.backendError.emit(job) return if job.getResult( ) == StartSliceJob.StartJobResult.MaterialIncompatible: if Application.getInstance().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() == StartSliceJob.StartJobResult.SettingError: if Application.getInstance().platformActivity: extruders = list( ExtruderManager.getInstance().getMachineExtruders( self._global_container_stack.getId())) error_keys = [] 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 = 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) error_labels = ", ".join(error_labels) self._error_message = Message(catalog.i18nc( "@info:status", "Unable to slice with the current settings. The following settings have errors: {0}" ).format(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( ) == StartSliceJob.StartJobResult.ObjectSettingError: errors = {} for node in DepthFirstIterator(Application.getInstance( ).getController().getScene().getRoot()): stack = node.callDecoration("getStack") if not stack: continue for key in stack.getErrorKeys(): definition = 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 definition = definition[0] errors[key] = definition.label error_labels = ", ".join(errors.values()) 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=error_labels), 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() == StartSliceJob.StartJobResult.BuildPlateError: if Application.getInstance().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( ) == StartSliceJob.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() == StartSliceJob.StartJobResult.NothingToSlice: if Application.getInstance().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) Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time)