diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 32c5191c7c..7651cad930 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -322,6 +322,7 @@ class CuraApplication(QtApplication): path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name) if path: + instance.setPath(path) with SaveFile(path, "wt", -1, "utf-8") as f: f.write(data) @@ -346,6 +347,7 @@ class CuraApplication(QtApplication): elif stack_type == "extruder_train": path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name) if path: + stack.setPath(path) with SaveFile(path, "wt", -1, "utf-8") as f: f.write(data) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index c082c64da1..27a9bd90dd 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -167,6 +167,19 @@ class ContainerManager(QObject): return True + @pyqtSlot(str, str, result=str) + def getContainerMetaDataEntry(self, container_id, entry_name): + containers = self._container_registry.findContainers(None, id=container_id) + if not containers: + UM.Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id) + return False + + result = containers[0].getMetaDataEntry(entry_name) + if result: + return result + else: + return "" + ## Set a metadata entry of the specified container. # # This will set the specified entry of the container's metadata to the specified @@ -577,6 +590,28 @@ class ContainerManager(QObject): return new_name + @pyqtSlot(str, result = str) + def duplicateMaterial(self, material_id): + containers = self._container_registry.findInstanceContainers(id=material_id) + if not containers: + UM.Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id) + return "" + + # Ensure all settings are saved. + UM.Application.getInstance().saveSettings() + + # Create a new ID & container to hold the data. + new_id = self._container_registry.uniqueName(material_id) + container_type = type(containers[0]) # Could be either a XMLMaterialProfile or a InstanceContainer + duplicated_container = container_type(new_id) + + # Instead of duplicating we load the data from the basefile again. + # This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile + # are also correctly created. + with open(containers[0].getPath(), encoding="utf-8") as f: + duplicated_container.deserialize(f.read()) + self._container_registry.addContainer(duplicated_container) + # Factory function, used by QML @staticmethod def createContainerManager(engine, js_engine): diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index ea3ca30118..89fa5e3e88 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -71,7 +71,7 @@ class ThreeMFReader(MeshReader): rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0)) # TODO: We currently do not check for normals and simply recalculate them. - mesh_builder.calculateNormals() + mesh_builder.calculateNormals(flip = True) mesh_builder.setFileName(file_name) node.setMeshData(mesh_builder.build().getTransformed(rotation)) node.setSelectable(True) diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py index c8d39b7a78..2bccb8e3cb 100644 --- a/plugins/CuraProfileReader/CuraProfileReader.py +++ b/plugins/CuraProfileReader/CuraProfileReader.py @@ -27,15 +27,15 @@ class CuraProfileReader(ProfileReader): # returned. def read(self, file_name): try: - archive = zipfile.ZipFile(file_name, "r") - results = [] - for profile_id in archive.namelist(): - with archive.open(profile_id) as f: - serialized = f.read() - profile = self._loadProfile(serialized.decode("utf-8"), profile_id) - if profile is not None: - results.append(profile) - return results + with zipfile.ZipFile(file_name, "r") as archive: + results = [] + for profile_id in archive.namelist(): + with archive.open(profile_id) as f: + serialized = f.read() + profile = self._loadProfile(serialized.decode("utf-8"), profile_id) + if profile is not None: + results.append(profile) + return results except zipfile.BadZipFile: # It must be an older profile from Cura 2.1. diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index 5c52c89416..953f60a33d 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -77,7 +77,10 @@ class PerObjectSettingsTool(Tool): if not self._multi_extrusion: default_stack_id = global_container_stack.getId() else: - default_stack_id = ExtruderManager.getInstance().getExtruderStack(0).getId() + 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): diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 2d017f829f..fd9f106334 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -51,6 +51,8 @@ class SolidView(View): if multi_extrusion: support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value") support_angle_stack = ExtruderManager.getInstance().getExtruderStack(support_extruder_nr) + if not support_angle_stack: + support_angle_stack = global_container_stack else: support_angle_stack = global_container_stack diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index ab1915bde1..b1cad3eb68 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2478,19 +2478,6 @@ "enabled": "support_enable", "settable_per_mesh": true }, - "support_area_smoothing": - { - "label": "Support Area Smoothing", - "description": "Maximum distance in the X/Y directions of a line segment which is to be smoothed out. Ragged lines are introduced by the join distance and support bridge, which cause the machine to resonate. Smoothing the support areas won't cause them to break with the constraints, except it might change the overhang.", - "unit": "mm", - "type": "float", - "default_value": 0.6, - "global_inherits_stack": "support_extruder_nr", - "minimum_value": "0", - "maximum_value_warning": "1.0", - "enabled": "support_enable", - "settable_per_mesh": true - }, "support_interface_enable": { "label": "Enable Support Interface", @@ -2546,6 +2533,19 @@ } } }, + "support_interface_skip_height": + { + "label": "Support Interface Resolution", + "description": "When checking where there's model above the support, take steps of the given height. Lower values will slice slower, while higher values may cause normal support to be printed in some places where there should have been support interface.", + "unit": "mm", + "type": "float", + "default_value": 0.3, + "minimum_value": "0", + "global_inherits_stack": "support_extruder_nr", + "maximum_value_warning": "support_interface_height", + "enabled": "extruderValue(support_extruder_nr, 'support_interface_enable') and support_enable", + "settable_per_mesh": true + }, "support_interface_density": { "label": "Support Interface Density", diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index d2d7c1b0a2..b1f0afe52f 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -129,30 +129,24 @@ UM.ManagementPage enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) }, - /* // apparently visible does not work on OS X - Button + // apparently visible does not work on OS X + /*Button { text: catalog.i18nc("@action:button", "Duplicate"); iconName: "list-add"; enabled: base.currentItem != null onClicked: { - var material_id = Cura.ContainerManager.duplicateContainer(base.currentItem.id) + var base_file = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "base_file") + // We need to copy the base container instead of the specific variant. + var material_id = base_file == "" ? Cura.ContainerManager.duplicateMaterial(base.currentItem.id): Cura.ContainerManager.duplicateMaterial(base_file) if(material_id == "") { return } - if(Cura.MachineManager.filterQualityByMachine) - { - var quality_id = Cura.ContainerManager.duplicateContainer(Cura.MachineManager.activeQualityId) - Cura.ContainerManager.setContainerMetaDataEntry(quality_id, "material", material_id) - Cura.MachineManager.setActiveQuality(quality_id) - } - Cura.MachineManager.setActiveMaterial(material_id) } - visible: false; }, */ Button diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index ed16b722dd..cdc557a089 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -138,7 +138,7 @@ Item { { id: linkedSettingIcon; - visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && !definition.settable_per_extruder && base.showLinkedSettingIcon + visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && (!definition.settable_per_extruder || definition.global_inherits_stack != "-1") && base.showLinkedSettingIcon height: parent.height; width: height;