Example #1
0
    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()
Example #2
0
    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
Example #3
0
    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()
Example #4
0
 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
Example #5
0
    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()
Example #6
0
    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
Example #7
0
    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()
Example #10
0
    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)
Example #11
0
    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()
Example #12
0
    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)
Example #13
0
    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
Example #14
0
 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
Example #15
0
    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
Example #16
0
    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
Example #17
0
    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
Example #18
0
    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
Example #20
0
    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()
Example #21
0
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
Example #22
0
    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()
Example #23
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
Example #24
0
    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
Example #25
0
    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)
Example #26
0
    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
Example #27
0
    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")
Example #28
0
    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()
Example #29
0
 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)
Example #30
0
    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
Example #31
0
    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
Example #32
0
    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()
Example #33
0
    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]
Example #34
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
Example #35
0
    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
Example #36
0
    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.
Example #37
0
    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
Example #38
0
    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)
Example #39
0
    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
Example #40
0
    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)
Example #41
0
 def setActiveExtruder(self, extruder_stack_id):
     self._extruder_stack = extruder_stack_id
     self._updateNextStack()
     ExtruderManager.getInstance().resetSelectedObjectExtruders()
     self.activeExtruderChanged.emit()
Example #42
0
    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)
Example #43
0
    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)
Example #44
0
    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
Example #45
0
    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()
Example #46
0
 def _getSettingFromAllExtruders(self, setting_key, property = "value"):
     return ExtruderManager.getInstance().getAllExtruderSettings(setting_key, property)
Example #47
0
    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)
Example #48
0
    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
Example #49
0
    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
Example #50
0
    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])
Example #51
0
    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)
Example #52
0
    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
Example #53
0
    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()
Example #56
0
    def _convertSavitarNodeToUMNode(self, savitar_node):
        um_node = SceneNode()
        transformation = self._createMatrixFromTransformationString(
            savitar_node.getTransformation())
        um_node.setTransformation(transformation)
        mesh_builder = MeshBuilder()

        data = numpy.fromstring(
            savitar_node.getMeshData().getFlatVerticesAsBytes(),
            dtype=numpy.float32)

        vertices = numpy.resize(data, (int(data.size / 3), 3))
        mesh_builder.setVertices(vertices)
        mesh_builder.calculateNormals(fast=True)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            um_node.setMeshData(mesh_data)

        for child in savitar_node.getChildren():
            child_node = self._convertSavitarNodeToUMNode(child)
            if child_node:
                um_node.addChild(child_node)

        if um_node.getMeshData() is None and len(um_node.getChildren()) == 0:
            return None

        settings = savitar_node.getSettings()

        # Add the setting override decorator, so we can add settings to this node.
        if settings:
            um_node.addDecorator(SettingOverrideDecorator())
            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()

            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                multi_extrusion = global_container_stack.getProperty(
                    "machine_extruder_count", "value") > 1

                # Ensure that all extruder data is reset
                if not multi_extrusion:
                    default_stack_id = global_container_stack.getId()
                else:
                    default_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(0)
                    if default_stack:
                        default_stack_id = default_stack.getId()
                    else:
                        default_stack_id = global_container_stack.getId()
                um_node.callDecoration("setActiveExtruder", default_stack_id)

                # Get the definition & set it
                definition = QualityManager.getInstance(
                ).getParentMachineDefinition(
                    global_container_stack.getBottom())
                um_node.callDecoration("getStack").getTop().setDefinition(
                    definition)

            setting_container = um_node.callDecoration("getStack").getTop()

            for key in settings:
                setting_value = settings[key]

                # Extruder_nr is a special case.
                if key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(int(setting_value))
                    if extruder_stack:
                        um_node.callDecoration("setActiveExtruder",
                                               extruder_stack.getId())
                    else:
                        Logger.log("w",
                                   "Unable to find extruder in position %s",
                                   setting_value)
                    continue
                setting_container.setProperty(key, "value", setting_value)

        if len(um_node.getChildren()) > 0:
            group_decorator = GroupDecorator()
            um_node.addDecorator(group_decorator)

        um_node.setSelectable(True)

        if um_node.getMeshData():
            # Assuming that all nodes with mesh data are printable objects
            # affects (auto) slicing
            sliceable_decorator = SliceableObjectDecorator()
            um_node.addDecorator(sliceable_decorator)

        return um_node
Example #57
0
    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)
Example #58
0
    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()
Example #59
0
    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
Example #60
0
    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)