diff --git a/CMakeLists.txt b/CMakeLists.txt index ab08a4d624..8105b677ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,9 @@ add_custom_target(tests) add_custom_command(TARGET tests POST_BUILD COMMAND "PYTHONPATH=${CMAKE_SOURCE_DIR}/../Uranium/:${CMAKE_SOURCE_DIR}" ${PYTHON_EXECUTABLE} -m pytest -r a --junitxml=${CMAKE_BINARY_DIR}/junit.xml ${CMAKE_SOURCE_DIR} || exit 0) option(CURA_DEBUGMODE "Enable debug dialog and other debug features" OFF) +if(CURA_DEBUGMODE) + set(_cura_debugmode "ON") +endif() set(CURA_VERSION "master" CACHE STRING "Version name of Cura") set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'") diff --git a/README.md b/README.md index 0ab3de61a4..28c0f13496 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Third party plugins * [X3G Writer](https://github.com/Ghostkeeper/X3GWriter): Adds support for exporting X3G files. * [Auto orientation](https://github.com/nallath/CuraOrientationPlugin): Calculate the optimal orientation for a model. * [OctoPrint Plugin](https://github.com/fieldofview/OctoPrintPlugin): Send printjobs directly to OctoPrint and monitor their progress in Cura. +* [WirelessPrinting Plugin](https://github.com/probonopd/WirelessPrinting): Print wirelessly from Cura to your 3D printer connected to an ESP8266 module. Making profiles for other printers ---------------------------------- diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py old mode 100644 new mode 100755 index 477f3d462d..1b04e0390a --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -385,16 +385,20 @@ class BuildVolume(SceneNode): self.setPosition(Vector(0, -self._raft_thickness, 0), SceneNode.TransformSpace.World) self.raftThicknessChanged.emit() - def _updateExtraZClearance(self): - extra_z = None + def _updateExtraZClearance(self) -> None: + extra_z = 0.0 extruders = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()) + use_extruders = False for extruder in extruders: - retraction_hop = extruder.getProperty("retraction_hop", "value") - if extra_z is None or retraction_hop > extra_z: - extra_z = retraction_hop - if extra_z is None: + if extruder.getProperty("retraction_hop_enabled", "value"): + retraction_hop = extruder.getProperty("retraction_hop", "value") + if extra_z is None or retraction_hop > extra_z: + extra_z = retraction_hop + use_extruders = True + if not use_extruders: # If no extruders, take global value. - extra_z = self._global_container_stack.getProperty("retraction_hop", "value") + if self._global_container_stack.getProperty("retraction_hop_enabled", "value"): + extra_z = self._global_container_stack.getProperty("retraction_hop", "value") if extra_z != self._extra_z_clearance: self._extra_z_clearance = extra_z @@ -890,7 +894,7 @@ class BuildVolume(SceneNode): _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist"] _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"] - _extra_z_settings = ["retraction_hop"] + _extra_z_settings = ["retraction_hop_enabled", "retraction_hop"] _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"] _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"] _ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"] diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py index 2b97feec82..7065b71735 100644 --- a/cura/ConvexHullDecorator.py +++ b/cura/ConvexHullDecorator.py @@ -253,21 +253,21 @@ class ConvexHullDecorator(SceneNodeDecorator): ## Offset the convex hull with settings that influence the collision area. # - # This also applies a minimum offset of 0.5mm, because of edge cases due - # to the rounding we apply. - # # \param convex_hull Polygon of the original convex hull. # \return New Polygon instance that is offset with everything that # influences the collision area. def _offsetHull(self, convex_hull): - horizontal_expansion = max(0.5, self._getSettingProperty("xy_offset", "value")) - expansion_polygon = Polygon(numpy.array([ - [-horizontal_expansion, -horizontal_expansion], - [-horizontal_expansion, horizontal_expansion], - [horizontal_expansion, horizontal_expansion], - [horizontal_expansion, -horizontal_expansion] - ], numpy.float32)) - return convex_hull.getMinkowskiHull(expansion_polygon) + horizontal_expansion = self._getSettingProperty("xy_offset", "value") + if horizontal_expansion != 0: + expansion_polygon = Polygon(numpy.array([ + [-horizontal_expansion, -horizontal_expansion], + [-horizontal_expansion, horizontal_expansion], + [horizontal_expansion, horizontal_expansion], + [horizontal_expansion, -horizontal_expansion] + ], numpy.float32)) + return convex_hull.getMinkowskiHull(expansion_polygon) + else: + return convex_hull def _onChanged(self, *args): self._raft_thickness = self._build_volume.getRaftThickness() diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py old mode 100644 new mode 100755 index a71422aa7d..301dff3d20 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -55,7 +55,7 @@ from . import MachineActionManager from cura.Settings.MachineManager import MachineManager from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.CuraContainerRegistry import CuraContainerRegistry +from cura.Settings.UserChangesModel import UserChangesModel from cura.Settings.ExtrudersModel import ExtrudersModel from cura.Settings.ContainerSettingsModel import ContainerSettingsModel from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler @@ -126,6 +126,8 @@ class CuraApplication(QtApplication): SettingDefinition.addSettingType("extruder", None, str, Validator) + SettingDefinition.addSettingType("[int]", None, str, None) + SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues) SettingFunction.registerOperator("extruderValue", ExtruderManager.getExtruderValue) SettingFunction.registerOperator("resolveOrValue", ExtruderManager.getResolveOrValue) @@ -243,6 +245,7 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True) Preferences.getInstance().addPreference("cura/dialog_on_project_save", True) Preferences.getInstance().addPreference("cura/asked_dialog_on_project_save", False) + Preferences.getInstance().addPreference("cura/choice_on_profile_override", 0) Preferences.getInstance().addPreference("cura/currency", "€") Preferences.getInstance().addPreference("cura/material_settings", "{}") @@ -325,11 +328,35 @@ class CuraApplication(QtApplication): ## A reusable dialogbox # showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) + def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []): self._message_box_callback = callback self._message_box_callback_arguments = callback_arguments self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon) + showDiscardOrKeepProfileChanges = pyqtSignal() + + def discardOrKeepProfileChanges(self): + choice = Preferences.getInstance().getValue("cura/choice_on_profile_override") + if choice == 1: + # don't show dialog and DISCARD the profile + self.discardOrKeepProfileChangesClosed("discard") + elif choice == 2: + # don't show dialog and KEEP the profile + self.discardOrKeepProfileChangesClosed("keep") + else: + # ALWAYS ask whether to keep or discard the profile + self.showDiscardOrKeepProfileChanges.emit() + + @pyqtSlot(str) + def discardOrKeepProfileChangesClosed(self, option): + if option == "discard": + global_stack = self.getGlobalContainerStack() + for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): + extruder.getTop().clear() + + global_stack.getTop().clear() + @pyqtSlot(int) def messageBoxClosed(self, button): if self._message_box_callback: @@ -339,11 +366,6 @@ class CuraApplication(QtApplication): showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) - def setViewLegendItems(self, items): - self.viewLegendItemsChanged.emit(items) - - viewLegendItemsChanged = pyqtSignal("QVariantList", arguments = ["items"]) - ## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. # # Note that the AutoSave plugin also calls this method. @@ -655,6 +677,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel") qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") + qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) @@ -1078,18 +1101,6 @@ class CuraApplication(QtApplication): fileLoaded = pyqtSignal(str) - def _onFileLoaded(self, job): - nodes = job.getResult() - for node in nodes: - if node is not None: - self.fileLoaded.emit(job.getFileName()) - node.setSelectable(True) - node.setName(os.path.basename(job.getFileName())) - op = AddSceneNodeOperation(node, self.getController().getScene().getRoot()) - op.push() - - self.getController().getScene().sceneChanged.emit(node) #Force scene change. - def _onJobFinished(self, job): if type(job) is not ReadMeshJob or not job.getResult(): return @@ -1117,10 +1128,8 @@ class CuraApplication(QtApplication): else: Logger.log("w", "Could not find a mesh in reloaded node.") - def _openFile(self, file): - job = ReadMeshJob(os.path.abspath(file)) - job.finished.connect(self._onFileLoaded) - job.start() + def _openFile(self, filename): + self.readLocalFile(QUrl.fromLocalFile(filename)) def _addProfileReader(self, profile_reader): # TODO: Add the profile reader to the list of plug-ins that can be used when importing profiles. @@ -1232,15 +1241,3 @@ class CuraApplication(QtApplication): def addNonSliceableExtension(self, extension): self._non_sliceable_extensions.append(extension) - - - @pyqtSlot("QVector3D") - def testQVector3D(self, vect): - Logger.log("d", "got QVector3D: %s : %s %s %s" % (vect, vect.x(), vect.y(), vect.z())) - - @pyqtProperty("QVector3D") - def getQVector3D(self): - from PyQt5.QtGui import QVector3D - vect = QVector3D(1.0, 2.0, 3.0) - Logger.log("d", "get QVector3D: %s" % vect) - return vect diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index 8a4d13e526..fb66275395 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -3,4 +3,4 @@ CuraVersion = "@CURA_VERSION@" CuraBuildType = "@CURA_BUILDTYPE@" -CuraDebugMode = True if "@CURA_DEBUGMODE@" == "ON" else False +CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False diff --git a/cura/LayerDataBuilder.py b/cura/LayerDataBuilder.py old mode 100644 new mode 100755 index 7cb6f75df3..df4b9e3218 --- a/cura/LayerDataBuilder.py +++ b/cura/LayerDataBuilder.py @@ -63,7 +63,7 @@ class LayerDataBuilder(MeshBuilder): line_dimensions = numpy.empty((vertex_count, 2), numpy.float32) colors = numpy.empty((vertex_count, 4), numpy.float32) indices = numpy.empty((index_count, 2), numpy.int32) - extruders = numpy.empty((vertex_count), numpy.int32) # Only usable for newer OpenGL versions + extruders = numpy.empty((vertex_count), numpy.float32) line_types = numpy.empty((vertex_count), numpy.float32) vertex_offset = 0 diff --git a/cura/MachineActionManager.py b/cura/MachineActionManager.py index 74b1d3bd08..ad9c91ec46 100644 --- a/cura/MachineActionManager.py +++ b/cura/MachineActionManager.py @@ -105,7 +105,7 @@ class MachineActionManager(QObject): if definition_id in self._supported_actions: return list(self._supported_actions[definition_id]) else: - return set() + return list() ## Get all actions required by given machine # \param definition_id The ID of the definition you want the required actions of diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 5d540628af..486a3d185b 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -177,7 +177,7 @@ class PrintInformation(QObject): self._active_material_container = active_material_containers[0] self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged) - def _onMaterialMetaDataChanged(self): + def _onMaterialMetaDataChanged(self, *args, **kwargs): self._calculateInformation() @pyqtSlot(str) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 7f0b7c4c07..f411190fd5 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -47,8 +47,8 @@ class PrinterOutputDevice(QObject, OutputDevice): self._job_name = "" self._error_text = "" self._accepts_commands = True - self._preheat_bed_timeout = 900 #Default time-out for pre-heating the bed, in seconds. - self._preheat_bed_timer = QTimer() #Timer that tracks how long to preheat still. + self._preheat_bed_timeout = 900 # Default time-out for pre-heating the bed, in seconds. + self._preheat_bed_timer = QTimer() # Timer that tracks how long to preheat still. self._preheat_bed_timer.setSingleShot(True) self._preheat_bed_timer.timeout.connect(self.cancelPreheatBed) @@ -232,11 +232,15 @@ class PrinterOutputDevice(QObject, OutputDevice): # \return The duration of the time-out to pre-heat the bed, formatted. @pyqtProperty(str, notify = preheatBedRemainingTimeChanged) def preheatBedRemainingTime(self): + if not self._preheat_bed_timer.isActive(): + return "" period = self._preheat_bed_timer.remainingTime() if period <= 0: return "" minutes, period = divmod(period, 60000) #60000 milliseconds in a minute. seconds, _ = divmod(period, 1000) #1000 milliseconds in a second. + if minutes <= 0 and seconds <= 0: + return "" return "%d:%02d" % (minutes, seconds) ## Time the print has been printing. diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 7bc2ff9efc..bac11f78cf 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -823,7 +823,7 @@ class ContainerManager(QObject): # # \param QVariant, essentially a list with QUrl objects. # \return Dict with keys status, text - @pyqtSlot(QVariant, result="QVariantMap") + @pyqtSlot("QVariantList", result="QVariantMap") def importProfiles(self, file_urls): status = "ok" results = {"ok": [], "error": []} @@ -856,10 +856,10 @@ class ContainerManager(QObject): return self._container_registry.importProfile(path) @pyqtSlot("QVariantList", QUrl, str) - def exportProfile(self, instance_id, file_url, file_type): + def exportProfile(self, instance_id: str, file_url: QUrl, file_type: str) -> None: if not file_url.isValid(): return path = file_url.toLocalFile() if not path: return - self._container_registry.exportProfile(instance_id, path, file_type) + self._container_registry.exportProfile(instance_id, path, file_type) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index ce42854d43..42f0edefe1 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -939,48 +939,7 @@ class MachineManager(QObject): container.nameChanged.connect(self._onQualityNameChanged) def _askUserToKeepOrClearCurrentSettings(self): - # Ask the user if the user profile should be cleared or not (discarding the current settings) - # In Simple Mode we assume the user always wants to keep the (limited) current settings - details_text = catalog.i18nc("@label", "You made changes to the following setting(s)/override(s):") - - # user changes in global stack - details_list = [setting.definition.label for setting in self._global_container_stack.getTop().findInstances(**{})] - - # user changes in extruder stacks - stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())) - for stack in stacks: - details_list.extend([ - "%s (%s)" % (setting.definition.label, stack.getName()) - for setting in stack.getTop().findInstances(**{})]) - - # Format to output string - details = "\n ".join([details_text, ] + details_list) - - num_changed_settings = len(details_list) - Application.getInstance().messageBox( - catalog.i18nc("@window:title", "Switched profiles"), - catalog.i18nc( - "@label", - "Do you want to transfer your %d changed setting(s)/override(s) to this profile?") % num_changed_settings, - catalog.i18nc( - "@label", - "If you transfer your settings they will override settings in the profile. If you don't transfer these settings, they will be lost."), - details, - buttons=QMessageBox.Yes + QMessageBox.No, - icon=QMessageBox.Question, - callback=self._keepUserSettingsDialogCallback) - - def _keepUserSettingsDialogCallback(self, button): - if button == QMessageBox.Yes: - # Yes, keep the settings in the user profile with this profile - pass - elif button == QMessageBox.No: - # No, discard the settings in the user profile - global_stack = Application.getInstance().getGlobalContainerStack() - for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()): - extruder.getTop().clear() - - global_stack.getTop().clear() + Application.getInstance().discardOrKeepProfileChanges() @pyqtProperty(str, notify = activeVariantChanged) def activeVariantName(self): diff --git a/cura/Settings/UserChangesModel.py b/cura/Settings/UserChangesModel.py new file mode 100644 index 0000000000..8b61186650 --- /dev/null +++ b/cura/Settings/UserChangesModel.py @@ -0,0 +1,122 @@ +from UM.Qt.ListModel import ListModel + +from PyQt5.QtCore import pyqtSlot, Qt +from UM.Application import Application +from cura.Settings.ExtruderManager import ExtruderManager +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.i18n import i18nCatalog +from UM.Settings.SettingFunction import SettingFunction + +from collections import OrderedDict +import os + + +class UserChangesModel(ListModel): + KeyRole = Qt.UserRole + 1 + LabelRole = Qt.UserRole + 2 + ExtruderRole = Qt.UserRole + 3 + OriginalValueRole = Qt.UserRole + 4 + UserValueRole = Qt.UserRole + 6 + CategoryRole = Qt.UserRole + 7 + + def __init__(self, parent = None): + super().__init__(parent = parent) + self.addRoleName(self.KeyRole, "key") + self.addRoleName(self.LabelRole, "label") + self.addRoleName(self.ExtruderRole, "extruder") + self.addRoleName(self.OriginalValueRole, "original_value") + self.addRoleName(self.UserValueRole, "user_value") + self.addRoleName(self.CategoryRole, "category") + + self._i18n_catalog = None + + self._update() + + @pyqtSlot() + def forceUpdate(self): + self._update() + + 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() + + # Drop the user container. + user_changes = containers.pop(0) + + for setting_key in user_changes.getAllKeys(): + 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") + if resolve is not None: + original_value = resolve + break + + original_value = container.getProperty(setting_key, "value") + + # 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) + + 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) diff --git a/cura_app.py b/cura_app.py index 633110eff8..989c45b37a 100755 --- a/cura_app.py +++ b/cura_app.py @@ -17,6 +17,12 @@ if Platform.isLinux(): # Needed for platform.linux_distribution, which is not av libGL = find_library("GL") ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL) +# When frozen, i.e. installer version, don't let PYTHONPATH mess up the search path for DLLs. +if Platform.isWindows() and hasattr(sys, "frozen"): + try: + del os.environ["PYTHONPATH"] + except KeyError: pass + #WORKAROUND: GITHUB-704 GITHUB-708 # It looks like setuptools creates a .pth file in # the default /usr/lib which causes the default site-packages diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index fde4d6ab21..2aa6fb27d3 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -18,6 +18,10 @@ from cura.QualityManager import QualityManager from UM.Scene.SceneNode import SceneNode MYPY = False + +import Savitar +import numpy + try: if not MYPY: import xml.etree.cElementTree as ET @@ -38,98 +42,10 @@ class ThreeMFReader(MeshReader): self._base_name = "" self._unit = None - def _createNodeFromObject(self, object, name = ""): - node = SceneNode() - node.setName(name) - mesh_builder = MeshBuilder() - vertex_list = [] - - components = object.find(".//3mf:components", self._namespaces) - if components: - for component in components: - id = component.get("objectid") - new_object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) - new_node = self._createNodeFromObject(new_object, self._base_name + "_" + str(id)) - node.addChild(new_node) - transform = component.get("transform") - if transform is not None: - new_node.setTransformation(self._createMatrixFromTransformationString(transform)) - - # for vertex in entry.mesh.vertices.vertex: - for vertex in object.findall(".//3mf:vertex", self._namespaces): - vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) - Job.yieldThread() - - xml_settings = list(object.findall(".//cura:setting", self._namespaces)) - - # Add the setting override decorator, so we can add settings to this node. - if xml_settings: - 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() - node.callDecoration("setActiveExtruder", default_stack_id) - - # Get the definition & set it - definition = QualityManager.getInstance().getParentMachineDefinition(global_container_stack.getBottom()) - node.callDecoration("getStack").getTop().setDefinition(definition) - - setting_container = node.callDecoration("getStack").getTop() - for setting in xml_settings: - setting_key = setting.get("key") - setting_value = setting.text - - # Extruder_nr is a special case. - if setting_key == "extruder_nr": - extruder_stack = ExtruderManager.getInstance().getExtruderStack(int(setting_value)) - if extruder_stack: - node.callDecoration("setActiveExtruder", extruder_stack.getId()) - else: - Logger.log("w", "Unable to find extruder in position %s", setting_value) - continue - setting_container.setProperty(setting_key,"value", setting_value) - - if len(node.getChildren()) > 0: - group_decorator = GroupDecorator() - node.addDecorator(group_decorator) - - triangles = object.findall(".//3mf:triangle", self._namespaces) - mesh_builder.reserveFaceCount(len(triangles)) - - for triangle in triangles: - v1 = int(triangle.get("v1")) - v2 = int(triangle.get("v2")) - v3 = int(triangle.get("v3")) - - mesh_builder.addFaceByPoints(vertex_list[v1][0], vertex_list[v1][1], vertex_list[v1][2], - vertex_list[v2][0], vertex_list[v2][1], vertex_list[v2][2], - vertex_list[v3][0], vertex_list[v3][1], vertex_list[v3][2]) - - Job.yieldThread() - - # TODO: We currently do not check for normals and simply recalculate them. - mesh_builder.calculateNormals(fast=True) - mesh_builder.setFileName(name) - mesh_data = mesh_builder.build() - - if len(mesh_data.getVertices()): - node.setMeshData(mesh_data) - - node.setSelectable(True) - return node - def _createMatrixFromTransformationString(self, transformation): + if transformation == "": + return Matrix() + splitted_transformation = transformation.split() ## Transformation is saved as: ## M00 M01 M02 0.0 @@ -156,51 +72,103 @@ class ThreeMFReader(MeshReader): return temp_mat + + ## Convenience function that converts a SceneNode object (as obtained from libSavitar) to a Uranium scenenode. + # \returns Uranium Scenen node. + 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) + return um_node + def read(self, file_name): result = [] # The base object of 3mf is a zipped archive. - archive = zipfile.ZipFile(file_name, "r") - self._base_name = os.path.basename(file_name) try: - self._root = ET.parse(archive.open("3D/3dmodel.model")) - self._unit = self._root.getroot().get("unit") - - build_items = self._root.findall("./3mf:build/3mf:item", self._namespaces) - - for build_item in build_items: - id = build_item.get("objectid") - object = self._root.find("./3mf:resources/3mf:object[@id='{0}']".format(id), self._namespaces) - if "type" in object.attrib: - if object.attrib["type"] == "support" or object.attrib["type"] == "other": - # Ignore support objects, as cura does not support these. - # We can't guarantee that they wont be made solid. - # We also ignore "other", as I have no idea what to do with them. - Logger.log("w", "3MF file contained an object of type %s which is not supported by Cura", object.attrib["type"]) - continue - elif object.attrib["type"] == "solidsupport" or object.attrib["type"] == "model": - pass # Load these as normal - else: - # We should technically fail at this point because it's an invalid 3MF, but try to continue anyway. - Logger.log("e", "3MF file contained an object of type %s which is not supported by the 3mf spec", - object.attrib["type"]) - continue - - build_item_node = self._createNodeFromObject(object, self._base_name + "_" + str(id)) - + archive = zipfile.ZipFile(file_name, "r") + self._base_name = os.path.basename(file_name) + parser = Savitar.ThreeMFParser() + scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read()) + self._unit = scene_3mf.getUnit() + for node in scene_3mf.getSceneNodes(): + um_node = self._convertSavitarNodeToUMNode(node) + if um_node is None: + continue # compensate for original center position, if object(s) is/are not around its zero position + transform_matrix = Matrix() - mesh_data = build_item_node.getMeshData() + mesh_data = um_node.getMeshData() if mesh_data is not None: extents = mesh_data.getExtents() center_vector = Vector(extents.center.x, extents.center.y, extents.center.z) transform_matrix.setByTranslation(center_vector) - - # offset with transform from 3mf - transform = build_item.get("transform") - if transform is not None: - transform_matrix.multiply(self._createMatrixFromTransformationString(transform)) - - build_item_node.setTransformation(transform_matrix) + transform_matrix.multiply(um_node.getLocalTransformation()) + um_node.setTransformation(transform_matrix) global_container_stack = Application.getInstance().getGlobalContainerStack() @@ -215,9 +183,9 @@ class ThreeMFReader(MeshReader): # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the # build volume. if global_container_stack: - translation_vector = Vector(x = -global_container_stack.getProperty("machine_width", "value") / 2, - y = -global_container_stack.getProperty("machine_depth", "value") / 2, - z = 0) + translation_vector = Vector(x=-global_container_stack.getProperty("machine_width", "value") / 2, + y=-global_container_stack.getProperty("machine_depth", "value") / 2, + z=0) translation_matrix = Matrix() translation_matrix.setByTranslation(translation_vector) transformation_matrix.multiply(translation_matrix) @@ -228,12 +196,13 @@ class ThreeMFReader(MeshReader): transformation_matrix.multiply(scale_matrix) # Pre multiply the transformation with the loaded transformation, so the data is handled correctly. - build_item_node.setTransformation(build_item_node.getLocalTransformation().preMultiply(transformation_matrix)) + um_node.setTransformation(um_node.getLocalTransformation().preMultiply(transformation_matrix)) - result.append(build_item_node) + result.append(um_node) - except Exception as e: - Logger.log("e", "An exception occurred in 3mf reader: %s", e) + except Exception: + Logger.logException("e", "An exception occurred in 3mf reader.") + return [] return result diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index b0d0da66c4..707238ab26 100644 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -476,7 +476,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader): return nodes def _stripFileToId(self, file): - return file.replace("Cura/", "").split(".")[0] + mime_type = MimeTypeDatabase.getMimeTypeForFile(file) + file = mime_type.stripExtension(file) + return file.replace("Cura/", "") def _getXmlProfileClass(self): return self._container_registry.getContainerForMimeType(MimeTypeDatabase.getMimeType("application/x-ultimaker-material-profile")) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index c907752137..34d47f527b 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -6,6 +6,11 @@ from UM.Math.Vector import Vector from UM.Logger import Logger from UM.Math.Matrix import Matrix from UM.Application import Application +import UM.Scene.SceneNode + +import Savitar + +import numpy MYPY = False try: @@ -35,18 +40,18 @@ class ThreeMFWriter(MeshWriter): def _convertMatrixToString(self, matrix): result = "" - result += str(matrix._data[0,0]) + " " - result += str(matrix._data[1,0]) + " " - result += str(matrix._data[2,0]) + " " - result += str(matrix._data[0,1]) + " " - result += str(matrix._data[1,1]) + " " - result += str(matrix._data[2,1]) + " " - result += str(matrix._data[0,2]) + " " - result += str(matrix._data[1,2]) + " " - result += str(matrix._data[2,2]) + " " - result += str(matrix._data[0,3]) + " " - result += str(matrix._data[1,3]) + " " - result += str(matrix._data[2,3]) + result += str(matrix._data[0, 0]) + " " + result += str(matrix._data[1, 0]) + " " + result += str(matrix._data[2, 0]) + " " + result += str(matrix._data[0, 1]) + " " + result += str(matrix._data[1, 1]) + " " + result += str(matrix._data[2, 1]) + " " + result += str(matrix._data[0, 2]) + " " + result += str(matrix._data[1, 2]) + " " + result += str(matrix._data[2, 2]) + " " + result += str(matrix._data[0, 3]) + " " + result += str(matrix._data[1, 3]) + " " + result += str(matrix._data[2, 3]) return result ## Should we store the archive @@ -55,6 +60,48 @@ class ThreeMFWriter(MeshWriter): def setStoreArchive(self, store_archive): self._store_archive = store_archive + ## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode + # \returns Uranium Scenen node. + def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()): + if type(um_node) is not UM.Scene.SceneNode.SceneNode: + return None + + savitar_node = Savitar.SceneNode() + + node_matrix = um_node.getLocalTransformation() + + matrix_string = self._convertMatrixToString(node_matrix.preMultiply(transformation)) + + savitar_node.setTransformation(matrix_string) + mesh_data = um_node.getMeshData() + if mesh_data is not None: + savitar_node.getMeshData().setVerticesFromBytes(mesh_data.getVerticesAsByteArray()) + indices_array = mesh_data.getIndicesAsByteArray() + if indices_array is not None: + savitar_node.getMeshData().setFacesFromBytes(indices_array) + else: + savitar_node.getMeshData().setFacesFromBytes(numpy.arange(mesh_data.getVertices().size / 3, dtype=numpy.int32).tostring()) + + # Handle per object settings (if any) + stack = um_node.callDecoration("getStack") + if stack is not None: + changed_setting_keys = set(stack.getTop().getAllKeys()) + + # Ensure that we save the extruder used for this object. + if stack.getProperty("machine_extruder_count", "value") > 1: + changed_setting_keys.add("extruder_nr") + + # Get values for all changed settings & save them. + for key in changed_setting_keys: + savitar_node.setSetting(key, str(stack.getProperty(key, "value"))) + + for child_node in um_node.getChildren(): + savitar_child_node = self._convertUMNodeToSavitarNode(child_node) + if savitar_child_node is not None: + savitar_node.addChild(savitar_child_node) + + return savitar_node + def getArchive(self): return self._archive @@ -79,98 +126,7 @@ class ThreeMFWriter(MeshWriter): relations_element = ET.Element("Relationships", xmlns = self._namespaces["relationships"]) model_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/3D/3dmodel.model", Id = "rel0", Type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel") - model = ET.Element("model", unit = "millimeter", xmlns = self._namespaces["3mf"]) - model.set("xmlns:cura", self._namespaces["cura"]) - - # Add the version of Cura this was created with. Since there is no "version" or similar metadata name we need - # to prefix it with the cura namespace, as specified by the 3MF specification. - version_metadata = ET.SubElement(model, "metadata", name = "cura:version") - version_metadata.text = Application.getInstance().getVersion() - - resources = ET.SubElement(model, "resources") - build = ET.SubElement(model, "build") - - added_nodes = [] - index = 0 # Ensure index always exists (even if there are no nodes to write) - # Write all nodes with meshData to the file as objects inside the resource tag - for index, n in enumerate(MeshWriter._meshNodes(nodes)): - added_nodes.append(n) # Save the nodes that have mesh data - object = ET.SubElement(resources, "object", id = str(index+1), type = "model") - mesh = ET.SubElement(object, "mesh") - - mesh_data = n.getMeshData() - vertices = ET.SubElement(mesh, "vertices") - verts = mesh_data.getVertices() - - if verts is None: - Logger.log("d", "3mf writer can't write nodes without mesh data. Skipping this node.") - continue # No mesh data, nothing to do. - if mesh_data.hasIndices(): - for face in mesh_data.getIndices(): - v1 = verts[face[0]] - v2 = verts[face[1]] - v3 = verts[face[2]] - xml_vertex1 = ET.SubElement(vertices, "vertex", x = str(v1[0]), y = str(v1[1]), z = str(v1[2])) - xml_vertex2 = ET.SubElement(vertices, "vertex", x = str(v2[0]), y = str(v2[1]), z = str(v2[2])) - xml_vertex3 = ET.SubElement(vertices, "vertex", x = str(v3[0]), y = str(v3[1]), z = str(v3[2])) - - triangles = ET.SubElement(mesh, "triangles") - for face in mesh_data.getIndices(): - triangle = ET.SubElement(triangles, "triangle", v1 = str(face[0]) , v2 = str(face[1]), v3 = str(face[2])) - else: - triangles = ET.SubElement(mesh, "triangles") - for idx, vert in enumerate(verts): - xml_vertex = ET.SubElement(vertices, "vertex", x = str(vert[0]), y = str(vert[1]), z = str(vert[2])) - - # If we have no faces defined, assume that every three subsequent vertices form a face. - if idx % 3 == 0: - triangle = ET.SubElement(triangles, "triangle", v1 = str(idx), v2 = str(idx + 1), v3 = str(idx + 2)) - - # Handle per object settings - stack = n.callDecoration("getStack") - if stack is not None: - changed_setting_keys = set(stack.getTop().getAllKeys()) - - # Ensure that we save the extruder used for this object. - if stack.getProperty("machine_extruder_count", "value") > 1: - changed_setting_keys.add("extruder_nr") - - settings_xml = ET.SubElement(object, "settings", xmlns=self._namespaces["cura"]) - - # Get values for all changed settings & save them. - for key in changed_setting_keys: - setting_xml = ET.SubElement(settings_xml, "setting", key = key) - setting_xml.text = str(stack.getProperty(key, "value")) - - # Add one to the index as we haven't incremented the last iteration. - index += 1 - nodes_to_add = set() - - for node in added_nodes: - # Check the parents of the nodes with mesh_data and ensure that they are also added. - parent_node = node.getParent() - while parent_node is not None: - if parent_node.callDecoration("isGroup"): - nodes_to_add.add(parent_node) - parent_node = parent_node.getParent() - else: - parent_node = None - - # Sort all the nodes by depth (so nodes with the highest depth are done first) - sorted_nodes_to_add = sorted(nodes_to_add, key=lambda node: node.getDepth(), reverse = True) - - # We have already saved the nodes with mesh data, but now we also want to save nodes required for the scene - for node in sorted_nodes_to_add: - object = ET.SubElement(resources, "object", id=str(index + 1), type="model") - components = ET.SubElement(object, "components") - for child in node.getChildren(): - if child in added_nodes: - component = ET.SubElement(components, "component", objectid = str(added_nodes.index(child) + 1), transform = self._convertMatrixToString(child.getLocalTransformation())) - index += 1 - added_nodes.append(node) - - # Create a transformation Matrix to convert from our worldspace into 3MF. - # First step: flip the y and z axis. + savitar_scene = Savitar.Scene() transformation_matrix = Matrix() transformation_matrix._data[1, 1] = 0 transformation_matrix._data[1, 2] = -1 @@ -188,14 +144,22 @@ class ThreeMFWriter(MeshWriter): translation_matrix.setByTranslation(translation_vector) transformation_matrix.preMultiply(translation_matrix) - # Find out what the final build items are and add them. - for node in added_nodes: - if node.getParent().callDecoration("isGroup") is None: - node_matrix = node.getLocalTransformation() + root_node = UM.Application.Application.getInstance().getController().getScene().getRoot() + for node in nodes: + if node == root_node: + for root_child in node.getChildren(): + savitar_node = self._convertUMNodeToSavitarNode(root_child, transformation_matrix) + if savitar_node: + savitar_scene.addSceneNode(savitar_node) + else: + savitar_node = self._convertUMNodeToSavitarNode(node, transformation_matrix) + if savitar_node: + savitar_scene.addSceneNode(savitar_node) - ET.SubElement(build, "item", objectid = str(added_nodes.index(node) + 1), transform = self._convertMatrixToString(node_matrix.preMultiply(transformation_matrix))) + parser = Savitar.ThreeMFParser() + scene_string = parser.sceneToString(savitar_scene) - archive.writestr(model_file, b' \n' + ET.tostring(model)) + archive.writestr(model_file, scene_string) archive.writestr(content_types_file, b' \n' + ET.tostring(content_types)) archive.writestr(relations_file, b' \n' + ET.tostring(relations_element)) except Exception as e: diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt old mode 100644 new mode 100755 index 83114bf342..bcbdb73f13 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -1,5 +1,9 @@ [2.5.0] -*Included PauseBackendPlugin. This enables pausing the backend and manually start the backend. Thanks to community member Aldo Hoeben for this feature. +*Layerview double slider. +The layerview now has a nice slider with double handles where you can drag maximum layer, minimum layer and the layer range. Thansk to community member Aldo Hoeben for this feature. + +*Included PauseBackendPlugin. +This enables pausing the backend and manually start the backend. Thanks to community member Aldo Hoeben for this feature. [2.4.0] *Project saving & opening diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py old mode 100644 new mode 100755 index f2023e270a..981145bebd --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -105,6 +105,8 @@ class CuraEngineBackend(QObject, Backend): self._backend_log_max_lines = 20000 # Maximum number of lines to buffer self._error_message = None # Pop-up message that shows errors. + self._last_num_objects = 0 # Count number of objects to see if there is something changed + self._postponed_scene_change_sources = [] # scene change is postponed (by a tool) self.backendQuit.connect(self._onBackendQuit) self.backendConnected.connect(self._onBackendConnected) @@ -340,21 +342,32 @@ class CuraEngineBackend(QObject, Backend): # # \param source The scene node that was changed. def _onSceneChanged(self, source): - if self._tool_active: - return - if type(source) is not SceneNode: return - if source is self._scene.getRoot(): - return + root_scene_nodes_changed = False + if source == self._scene.getRoot(): + num_objects = 0 + for node in DepthFirstIterator(self._scene.getRoot()): + # Only count sliceable objects + if node.callDecoration("isSliceable"): + num_objects += 1 + if num_objects != self._last_num_objects: + self._last_num_objects = num_objects + root_scene_nodes_changed = True + else: + return - self.determineAutoSlicing() + if not source.callDecoration("isGroup") and not root_scene_nodes_changed: + if source.getMeshData() is None: + return + if source.getMeshData().getVertices() is None: + return - if source.getMeshData() is None: - return - - if source.getMeshData().getVertices() is None: + if self._tool_active: + # do it later, each source only has to be done once + if source not in self._postponed_scene_change_sources: + self._postponed_scene_change_sources.append(source) return self.needsSlicing() @@ -501,7 +514,11 @@ class CuraEngineBackend(QObject, Backend): # \param tool The tool that the user was using. def _onToolOperationStopped(self, tool): self._tool_active = False # React on scene change again - self.determineAutoSlicing() + self.determineAutoSlicing() # Switch timer on if appropriate + # Process all the postponed scene changes + while self._postponed_scene_change_sources: + source = self._postponed_scene_change_sources.pop(0) + self._onSceneChanged(source) ## Called when the user changes the active view mode. def _onActiveViewChanged(self): diff --git a/plugins/LayerView/LayerPass.py b/plugins/LayerView/LayerPass.py old mode 100644 new mode 100755 index 4fc5f66793..b706f21877 --- a/plugins/LayerView/LayerPass.py +++ b/plugins/LayerView/LayerPass.py @@ -97,11 +97,11 @@ class LayerPass(RenderPass): # Create a new batch that is not range-limited batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid) - if self._layer_view._current_layer_mesh: - batch.addItem(node.getWorldTransformation(), self._layer_view._current_layer_mesh) + if self._layer_view.getCurrentLayerMesh(): + batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerMesh()) - if self._layer_view._current_layer_jumps: - batch.addItem(node.getWorldTransformation(), self._layer_view._current_layer_jumps) + if self._layer_view.getCurrentLayerJumps(): + batch.addItem(node.getWorldTransformation(), self._layer_view.getCurrentLayerJumps()) if len(batch.items) > 0: batch.render(self._scene.getActiveCamera()) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py old mode 100644 new mode 100755 index 0a315b5865..7e54cabacd --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -67,16 +67,14 @@ class LayerView(View): self._resetSettings() self._legend_items = None + self._show_travel_moves = False Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) Preferences.getInstance().addPreference("view/force_layer_view_compatibility_mode", False) Preferences.getInstance().addPreference("layerview/layer_view_type", 0) - Preferences.getInstance().addPreference("layerview/extruder0_opacity", 1.0) - Preferences.getInstance().addPreference("layerview/extruder1_opacity", 1.0) - Preferences.getInstance().addPreference("layerview/extruder2_opacity", 1.0) - Preferences.getInstance().addPreference("layerview/extruder3_opacity", 1.0) + Preferences.getInstance().addPreference("layerview/extruder_opacities", "") Preferences.getInstance().addPreference("layerview/show_travel_moves", False) Preferences.getInstance().addPreference("layerview/show_helpers", True) @@ -165,6 +163,8 @@ class LayerView(View): self._current_layer_num = 0 if self._current_layer_num > self._max_layers: self._current_layer_num = self._max_layers + if self._current_layer_num < self._minimum_layer_num: + self._minimum_layer_num = self._current_layer_num self._startUpdateTopLayers() @@ -175,6 +175,10 @@ class LayerView(View): self._minimum_layer_num = value if self._minimum_layer_num < 0: self._minimum_layer_num = 0 + if self._minimum_layer_num > self._max_layers: + self._minimum_layer_num = self._max_layers + if self._minimum_layer_num > self._current_layer_num: + self._current_layer_num = self._minimum_layer_num self._startUpdateTopLayers() @@ -196,8 +200,9 @@ class LayerView(View): # \param extruder_nr 0..3 # \param opacity 0.0 .. 1.0 def setExtruderOpacity(self, extruder_nr, opacity): - self._extruder_opacity[extruder_nr] = opacity - self.currentLayerNumChanged.emit() + if 0 <= extruder_nr <= 3: + self._extruder_opacity[extruder_nr] = opacity + self.currentLayerNumChanged.emit() def getExtruderOpacities(self): return self._extruder_opacity @@ -278,21 +283,17 @@ class LayerView(View): def endRendering(self): pass - def enableLegend(self): - Application.getInstance().setViewLegendItems(self._getLegendItems()) - - def disableLegend(self): - Application.getInstance().setViewLegendItems([]) - def event(self, event): modifiers = QApplication.keyboardModifiers() - ctrl_is_active = modifiers == Qt.ControlModifier + ctrl_is_active = modifiers & Qt.ControlModifier + shift_is_active = modifiers & Qt.ShiftModifier if event.type == Event.KeyPressEvent and ctrl_is_active: + amount = 10 if shift_is_active else 1 if event.key == KeyEvent.UpKey: - self.setLayer(self._current_layer_num + 1) + self.setLayer(self._current_layer_num + amount) return True if event.key == KeyEvent.DownKey: - self.setLayer(self._current_layer_num - 1) + self.setLayer(self._current_layer_num - amount) return True if event.type == Event.ViewActivateEvent: @@ -316,9 +317,6 @@ class LayerView(View): self._old_composite_shader = self._composite_pass.getCompositeShader() self._composite_pass.setCompositeShader(self._layerview_composite_shader) - if self.getLayerViewType() == self.LAYER_VIEW_TYPE_LINE_TYPE or self._compatibility_mode: - self.enableLegend() - elif event.type == Event.ViewDeactivateEvent: self._wireprint_warning_message.hide() Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged) @@ -328,7 +326,11 @@ class LayerView(View): self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader) - self.disableLegend() + def getCurrentLayerMesh(self): + return self._current_layer_mesh + + def getCurrentLayerJumps(self): + return self._current_layer_jumps def _onGlobalStackChanged(self): if self._global_container_stack: @@ -370,7 +372,8 @@ class LayerView(View): return self.resetLayerData() # Reset the layer data only when job is done. Doing it now prevents "blinking" data. self._current_layer_mesh = job.getResult().get("layers") - self._current_layer_jumps = job.getResult().get("jumps") + if self._show_travel_moves: + self._current_layer_jumps = job.getResult().get("jumps") self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot()) self._top_layers_job = None @@ -383,10 +386,12 @@ class LayerView(View): self.setLayerViewType(int(float(Preferences.getInstance().getValue("layerview/layer_view_type")))); - self.setExtruderOpacity(0, float(Preferences.getInstance().getValue("layerview/extruder0_opacity"))) - self.setExtruderOpacity(1, float(Preferences.getInstance().getValue("layerview/extruder1_opacity"))) - self.setExtruderOpacity(2, float(Preferences.getInstance().getValue("layerview/extruder2_opacity"))) - self.setExtruderOpacity(3, float(Preferences.getInstance().getValue("layerview/extruder3_opacity"))) + for extruder_nr, extruder_opacity in enumerate(Preferences.getInstance().getValue("layerview/extruder_opacities").split("|")): + try: + opacity = float(extruder_opacity) + except ValueError: + opacity = 1.0 + self.setExtruderOpacity(extruder_nr, opacity) self.setShowTravelMoves(bool(Preferences.getInstance().getValue("layerview/show_travel_moves"))) self.setShowHelpers(bool(Preferences.getInstance().getValue("layerview/show_helpers"))) @@ -402,10 +407,7 @@ class LayerView(View): "view/only_show_top_layers", "view/force_layer_view_compatibility_mode", "layerview/layer_view_type", - "layerview/extruder0_opacity", - "layerview/extruder1_opacity", - "layerview/extruder2_opacity", - "layerview/extruder3_opacity", + "layerview/extruder_opacities", "layerview/show_travel_moves", "layerview/show_helpers", "layerview/show_skin", @@ -415,24 +417,6 @@ class LayerView(View): self._updateWithPreferences() - def _getLegendItems(self): - if self._legend_items is None: - theme = Application.getInstance().getTheme() - self._legend_items = [ - {"color": theme.getColor("layerview_inset_0").name(), "title": catalog.i18nc("@label:layerview polygon type", "Outer Wall")}, # Inset0Type - {"color": theme.getColor("layerview_inset_x").name(), "title": catalog.i18nc("@label:layerview polygon type", "Inner Wall")}, # InsetXType - {"color": theme.getColor("layerview_skin").name(), "title": catalog.i18nc("@label:layerview polygon type", "Top / Bottom")}, # SkinType - {"color": theme.getColor("layerview_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Infill")}, # InfillType - {"color": theme.getColor("layerview_support").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Skin")}, # SupportType - {"color": theme.getColor("layerview_support_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Infill")}, # SupportInfillType - {"color": theme.getColor("layerview_support_interface").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Interface")}, # SupportInterfaceType - {"color": theme.getColor("layerview_skirt").name(), "title": catalog.i18nc("@label:layerview polygon type", "Build Plate Adhesion")}, # SkirtType - {"color": theme.getColor("layerview_move_combing").name(), "title": catalog.i18nc("@label:layerview polygon type", "Travel Move")}, # MoveCombingType - {"color": theme.getColor("layerview_move_retraction").name(), "title": catalog.i18nc("@label:layerview polygon type", "Retraction Move")}, # MoveRetractionType - #{"color": theme.getColor("layerview_none").name(), "title": catalog.i18nc("@label:layerview polygon type", "Unknown")} # NoneType - ] - return self._legend_items - class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index 9e51ab084d..cb8a27d55d 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -10,280 +10,193 @@ import UM 1.0 as UM Item { - width: UM.Theme.getSize("button").width - height: UM.Theme.getSize("slider_layerview_size").height - - Slider - { - id: sliderMinimumLayer - width: UM.Theme.getSize("slider_layerview_size").width - height: UM.Theme.getSize("slider_layerview_size").height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("slider_layerview_margin").width * 0.2 - orientation: Qt.Vertical - minimumValue: 0; - maximumValue: UM.LayerView.numLayers-1; - stepSize: 1 - - property real pixelsPerStep: ((height - UM.Theme.getSize("slider_handle").height) / (maximumValue - minimumValue)) * stepSize; - - value: UM.LayerView.minimumLayer - onValueChanged: { - UM.LayerView.setMinimumLayer(value) - if (value > UM.LayerView.currentLayer) { - UM.LayerView.setCurrentLayer(value); - } + width: { + if (UM.LayerView.compatibilityMode) { + return UM.Theme.getSize("layerview_menu_size_compatibility").width; + } else { + return UM.Theme.getSize("layerview_menu_size").width; } - - style: UM.Theme.styles.slider; } - - Slider - { - id: slider - width: UM.Theme.getSize("slider_layerview_size").width - height: UM.Theme.getSize("slider_layerview_size").height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("slider_layerview_margin").width * 0.8 - orientation: Qt.Vertical - minimumValue: 0; - maximumValue: UM.LayerView.numLayers; - stepSize: 1 - - property real pixelsPerStep: ((height - UM.Theme.getSize("slider_handle").height) / (maximumValue - minimumValue)) * stepSize; - - value: UM.LayerView.currentLayer - onValueChanged: { - UM.LayerView.setCurrentLayer(value); - if (value < UM.LayerView.minimumLayer) { - UM.LayerView.setMinimumLayer(value); - } - } - - style: UM.Theme.styles.slider; - - Rectangle - { - x: parent.width + UM.Theme.getSize("slider_layerview_background").width / 2; - y: parent.height - (parent.value * parent.pixelsPerStep) - UM.Theme.getSize("slider_handle").height * 1.25; - - height: UM.Theme.getSize("slider_handle").height + UM.Theme.getSize("default_margin").height - width: valueLabel.width + UM.Theme.getSize("default_margin").width - Behavior on height { NumberAnimation { duration: 50; } } - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("slider_groove_border") - color: UM.Theme.getColor("tool_panel_background") - - visible: UM.LayerView.layerActivity && Printer.platformActivity ? true : false - - TextField - { - id: valueLabel - property string maxValue: slider.maximumValue + 1 - text: slider.value + 1 - horizontalAlignment: TextInput.AlignRight; - onEditingFinished: - { - // Ensure that the cursor is at the first position. On some systems the text isn't fully visible - // Seems to have to do something with different dpi densities that QML doesn't quite handle. - // Another option would be to increase the size even further, but that gives pretty ugly results. - cursorPosition = 0; - if(valueLabel.text != '') - { - slider.value = valueLabel.text - 1; - } - } - validator: IntValidator { bottom: 1; top: slider.maximumValue + 1; } - - anchors.left: parent.left; - anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2; - anchors.verticalCenter: parent.verticalCenter; - - width: Math.max(UM.Theme.getSize("line").width * maxValue.length + 2, 20); - style: TextFieldStyle - { - textColor: UM.Theme.getColor("setting_control_text"); - font: UM.Theme.getFont("default"); - background: Item { } - } - } - - BusyIndicator - { - id: busyIndicator; - anchors.left: parent.right; - anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2; - anchors.verticalCenter: parent.verticalCenter; - - width: UM.Theme.getSize("slider_handle").height; - height: width; - - running: UM.LayerView.busy; - visible: UM.LayerView.busy; - } + height: { + if (UM.LayerView.compatibilityMode) { + return UM.Theme.getSize("layerview_menu_size_compatibility").height; + } else { + return UM.Theme.getSize("layerview_menu_size").height + UM.LayerView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height) } } Rectangle { - id: slider_background + id: layerViewMenu anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + width: parent.width + height: parent.height z: slider.z - 1 - width: UM.Theme.getSize("slider_layerview_background").width - height: slider.height + UM.Theme.getSize("default_margin").height * 2 - color: UM.Theme.getColor("tool_panel_background"); - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - MouseArea { - id: sliderMouseArea - property double manualStepSize: slider.maximumValue / 11 - anchors.fill: parent - onWheel: { - slider.value = wheel.angleDelta.y < 0 ? slider.value - sliderMouseArea.manualStepSize : slider.value + sliderMouseArea.manualStepSize - } - } - } - - Rectangle { - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.top: slider_background.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: UM.Theme.getSize("slider_layerview_background").width * 3 - height: slider.height + UM.Theme.getSize("default_margin").height * 2 - color: UM.Theme.getColor("tool_panel_background"); - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - ListModel // matches LayerView.py - { - id: layerViewTypes - ListElement { - text: "Material Color" - type_id: 0 - } - ListElement { - text: "Line Type" - type_id: 1 // these ids match the switching in the shader - } - } - - ComboBox - { - id: layerTypeCombobox - anchors.top: parent.top - anchors.left: parent.left - model: layerViewTypes - visible: !UM.LayerView.compatibilityMode - property int layer_view_type: UM.Preferences.getValue("layerview/layer_view_type") - currentIndex: layer_view_type // index matches type_id - onActivated: { - // Combobox selection - var type_id = layerViewTypes.get(index).type_id; - UM.Preferences.setValue("layerview/layer_view_type", type_id); - updateLegend(); - } - onModelChanged: { - updateLegend(); - } - // Update visibility of legend. - function updateLegend() { - var type_id = layerViewTypes.get(currentIndex).type_id; - if (UM.LayerView.compatibilityMode || (type_id == 1)) { - // Line type - UM.LayerView.enableLegend(); - } else { - UM.LayerView.disableLegend(); - } - } - } - - Label - { - id: compatibilityModeLabel - anchors.top: parent.top - anchors.left: parent.left - text: catalog.i18nc("@label","Compatibility Mode") - visible: UM.LayerView.compatibilityMode - } - - Connections { - target: UM.Preferences - onPreferenceChanged: - { - layerTypeCombobox.layer_view_type = UM.Preferences.getValue("layerview/layer_view_type"); - view_settings.extruder0_checked = UM.Preferences.getValue("layerview/extruder0_opacity") > 0.5; - view_settings.extruder1_checked = UM.Preferences.getValue("layerview/extruder1_opacity") > 0.5; - view_settings.extruder2_checked = UM.Preferences.getValue("layerview/extruder2_opacity") > 0.5; - view_settings.extruder3_checked = UM.Preferences.getValue("layerview/extruder3_opacity") > 0.5; - view_settings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves"); - view_settings.show_helpers = UM.Preferences.getValue("layerview/show_helpers"); - view_settings.show_skin = UM.Preferences.getValue("layerview/show_skin"); - view_settings.show_infill = UM.Preferences.getValue("layerview/show_infill"); - } - } + color: UM.Theme.getColor("tool_panel_background") ColumnLayout { id: view_settings - property bool extruder0_checked: UM.Preferences.getValue("layerview/extruder0_opacity") > 0.5 - property bool extruder1_checked: UM.Preferences.getValue("layerview/extruder1_opacity") > 0.5 - property bool extruder2_checked: UM.Preferences.getValue("layerview/extruder2_opacity") > 0.5 - property bool extruder3_checked: UM.Preferences.getValue("layerview/extruder3_opacity") > 0.5 + property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|") property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves") property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers") property bool show_skin: UM.Preferences.getValue("layerview/show_skin") property bool show_infill: UM.Preferences.getValue("layerview/show_infill") + property bool show_legend: UM.LayerView.compatibilityMode || UM.Preferences.getValue("layerview/layer_view_type") == 1 + property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") + property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") - anchors.top: UM.LayerView.compatibilityMode ? compatibilityModeLabel.bottom : layerTypeCombobox.bottom + anchors.top: parent.top anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("layerview_row_spacing").height - CheckBox { - checked: view_settings.extruder0_checked - onClicked: { - UM.Preferences.setValue("layerview/extruder0_opacity", checked ? 1.0 : 0.0); + Label + { + id: layersLabel + anchors.left: parent.left + text: catalog.i18nc("@label","View Mode: Layers") + font.bold: true + } + + Label + { + id: spaceLabel + anchors.left: parent.left + text: " " + font.pointSize: 0.5 + } + + Label + { + id: layerViewTypesLabel + anchors.left: parent.left + text: catalog.i18nc("@label","Color scheme") + visible: !UM.LayerView.compatibilityMode + Layout.fillWidth: true + } + + ListModel // matches LayerView.py + { + id: layerViewTypes + } + + Component.onCompleted: + { + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Material Color"), + type_id: 0 + }) + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Line Type"), + type_id: 1 // these ids match the switching in the shader + }) + } + + ComboBox + { + id: layerTypeCombobox + anchors.left: parent.left + Layout.fillWidth: true + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + model: layerViewTypes + visible: !UM.LayerView.compatibilityMode + + property int layer_view_type: UM.Preferences.getValue("layerview/layer_view_type") + currentIndex: layer_view_type // index matches type_id + onActivated: { + // Combobox selection + var type_id = index; + UM.Preferences.setValue("layerview/layer_view_type", type_id); + updateLegend(type_id); } - text: "Extruder 1" - visible: !UM.LayerView.compatibilityMode && (UM.LayerView.extruderCount >= 1) - } - CheckBox { - checked: view_settings.extruder1_checked - onClicked: { - UM.Preferences.setValue("layerview/extruder1_opacity", checked ? 1.0 : 0.0); + onModelChanged: { + updateLegend(UM.Preferences.getValue("layerview/layer_view_type")); } - text: "Extruder 2" - visible: !UM.LayerView.compatibilityMode && (UM.LayerView.extruderCount >= 2) - } - CheckBox { - checked: view_settings.extruder2_checked - onClicked: { - UM.Preferences.setValue("layerview/extruder2_opacity", checked ? 1.0 : 0.0); + + // Update visibility of legend. + function updateLegend(type_id) { + if (UM.LayerView.compatibilityMode || (type_id == 1)) { + // Line type + view_settings.show_legend = true; + } else { + view_settings.show_legend = false; + } } - text: "Extruder 3" - visible: !UM.LayerView.compatibilityMode && (UM.LayerView.etruderCount >= 3) } - CheckBox { - checked: view_settings.extruder3_checked - onClicked: { - UM.Preferences.setValue("layerview/extruder3_opacity", checked ? 1.0 : 0.0); + + Label + { + id: compatibilityModeLabel + anchors.left: parent.left + text: catalog.i18nc("@label","Compatibility Mode") + visible: UM.LayerView.compatibilityMode + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + } + + Label + { + id: space2Label + anchors.left: parent.left + text: " " + font.pointSize: 0.5 + } + + Connections { + target: UM.Preferences + onPreferenceChanged: + { + layerTypeCombobox.layer_view_type = UM.Preferences.getValue("layerview/layer_view_type"); + view_settings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|"); + view_settings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves"); + view_settings.show_helpers = UM.Preferences.getValue("layerview/show_helpers"); + view_settings.show_skin = UM.Preferences.getValue("layerview/show_skin"); + view_settings.show_infill = UM.Preferences.getValue("layerview/show_infill"); + view_settings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers"); + view_settings.top_layer_count = UM.Preferences.getValue("view/top_layer_count"); } - text: "Extruder 4" - visible: !UM.LayerView.compatibilityMode && (UM.LayerView.extruderCount >= 4) } - Label { - text: "Other extruders always visible" - visible: !UM.LayerView.compatibilityMode && (UM.LayerView.extruderCount >= 5) + + Repeater { + model: UM.LayerView.extruderCount + CheckBox { + checked: view_settings.extruder_opacities[index] > 0.5 || view_settings.extruder_opacities[index] == undefined || view_settings.extruder_opacities[index] == "" + onClicked: { + view_settings.extruder_opacities[index] = checked ? 1.0 : 0.0 + UM.Preferences.setValue("layerview/extruder_opacities", view_settings.extruder_opacities.join("|")); + } + text: catalog.i18nc("@label", "Extruder %1").arg(index + 1) + visible: !UM.LayerView.compatibilityMode + enabled: index + 1 <= 4 + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + } } + CheckBox { checked: view_settings.show_travel_moves onClicked: { UM.Preferences.setValue("layerview/show_travel_moves", checked); } - text: catalog.i18nc("@label", "Show Travel Moves") + text: catalog.i18nc("@label", "Show Travels") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_move_combing") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: view_settings.show_legend + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width } CheckBox { checked: view_settings.show_helpers @@ -291,6 +204,20 @@ Item UM.Preferences.setValue("layerview/show_helpers", checked); } text: catalog.i18nc("@label", "Show Helpers") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_support") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: view_settings.show_legend + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width } CheckBox { checked: view_settings.show_skin @@ -298,6 +225,20 @@ Item UM.Preferences.setValue("layerview/show_skin", checked); } text: catalog.i18nc("@label", "Show Shell") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_inset_0") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: view_settings.show_legend + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width } CheckBox { checked: view_settings.show_infill @@ -305,16 +246,374 @@ Item UM.Preferences.setValue("layerview/show_infill", checked); } text: catalog.i18nc("@label", "Show Infill") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_infill") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: view_settings.show_legend + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width } CheckBox { - checked: true + checked: view_settings.only_show_top_layers onClicked: { - CuraApplication.log("getting QVector3D"); - var v = CuraApplication.getQVector3D; - CuraApplication.log("getting QVector3D"); - CuraApplication.testQVector3D(v); + UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0); + } + text: catalog.i18nc("@label", "Only Show Top Layers") + visible: UM.LayerView.compatibilityMode + } + CheckBox { + checked: view_settings.top_layer_count == 5 + onClicked: { + UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1); + } + text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") + visible: UM.LayerView.compatibilityMode + } + + Label + { + id: topBottomLabel + anchors.left: parent.left + text: catalog.i18nc("@label","Top / Bottom") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_skin") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + visible: view_settings.show_legend + } + + Label + { + id: innerWallLabel + anchors.left: parent.left + text: catalog.i18nc("@label","Inner Wall") + Rectangle { + anchors.top: parent.top + anchors.topMargin: 2 + anchors.right: parent.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor("layerview_inset_x") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: view_settings.show_legend + } + Layout.fillWidth: true + Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + visible: view_settings.show_legend + } + + } + + Item + { + id: slider + width: handleSize + height: parent.height - 2*UM.Theme.getSize("slider_layerview_margin").height + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("slider_layerview_margin").height + anchors.right: layerViewMenu.right + anchors.rightMargin: UM.Theme.getSize("slider_layerview_margin").width + + property real handleSize: UM.Theme.getSize("slider_handle").width + property real handleRadius: handleSize / 2 + property real minimumRangeHandleSize: UM.Theme.getSize("slider_handle").width / 2 + property real trackThickness: UM.Theme.getSize("slider_groove").width + property real trackRadius: trackThickness / 2 + property real trackBorderWidth: UM.Theme.getSize("default_lining").width + property color upperHandleColor: UM.Theme.getColor("slider_handle") + property color lowerHandleColor: UM.Theme.getColor("slider_handle") + property color rangeHandleColor: UM.Theme.getColor("slider_groove_fill") + property color trackColor: UM.Theme.getColor("slider_groove") + property color trackBorderColor: UM.Theme.getColor("slider_groove_border") + + property real maximumValue: UM.LayerView.numLayers + property real minimumValue: 0 + property real minimumRange: 0 + property bool roundValues: true + + property var activeHandle: upperHandle + property bool layersVisible: UM.LayerView.layerActivity && Printer.platformActivity ? true : false + + function getUpperValueFromHandle() + { + var result = upperHandle.y / (height - (2 * handleSize + minimumRangeHandleSize)); + result = maximumValue + result * (minimumValue - (maximumValue - minimumRange)); + result = roundValues ? Math.round(result) | 0 : result; + return result; + } + + function getLowerValueFromHandle() + { + var result = (lowerHandle.y - (handleSize + minimumRangeHandleSize)) / (height - (2 * handleSize + minimumRangeHandleSize)); + result = maximumValue - minimumRange + result * (minimumValue - (maximumValue - minimumRange)); + result = roundValues ? Math.round(result) : result; + return result; + } + + function setUpperValue(value) + { + var value = (value - maximumValue) / (minimumValue - maximumValue); + var new_upper_y = Math.round(value * (height - (2 * handleSize + minimumRangeHandleSize))); + + if(new_upper_y != upperHandle.y) + { + upperHandle.y = new_upper_y; + } + rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height); + } + + function setLowerValue(value) + { + var value = (value - maximumValue) / (minimumValue - maximumValue); + var new_lower_y = Math.round((handleSize + minimumRangeHandleSize) + value * (height - (2 * handleSize + minimumRangeHandleSize))); + + if(new_lower_y != lowerHandle.y) + { + lowerHandle.y = new_lower_y; + } + rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height); + } + + Connections + { + target: UM.LayerView + onMinimumLayerChanged: slider.setLowerValue(UM.LayerView.minimumLayer) + onCurrentLayerChanged: slider.setUpperValue(UM.LayerView.currentLayer) + } + + Rectangle { + width: parent.trackThickness + height: parent.height - parent.handleSize + radius: parent.trackRadius + anchors.centerIn: parent + color: parent.trackColor + border.width: parent.trackBorderWidth; + border.color: parent.trackBorderColor; + } + + Item { + id: rangeHandle + y: upperHandle.y + upperHandle.height + width: parent.handleSize + height: parent.minimumRangeHandleSize + anchors.horizontalCenter: parent.horizontalCenter + + visible: slider.layersVisible + + property real value: UM.LayerView.currentLayer + function setValue(value) + { + var range = upperHandle.value - lowerHandle.value; + value = Math.min(value, slider.maximumValue); + value = Math.max(value, slider.minimumValue + range); + UM.LayerView.setCurrentLayer(value); + UM.LayerView.setMinimumLayer(value - range); + } + + Rectangle { + anchors.centerIn: parent + width: parent.parent.trackThickness - 2 * parent.parent.trackBorderWidth + height: parent.height + parent.parent.handleSize + color: parent.parent.rangeHandleColor + } + + MouseArea { + anchors.fill: parent + + drag.target: parent + drag.axis: Drag.YAxis + drag.minimumY: upperHandle.height + drag.maximumY: parent.parent.height - (parent.height + lowerHandle.height) + + onPressed: parent.parent.activeHandle = rangeHandle + onPositionChanged: + { + upperHandle.y = parent.y - upperHandle.height + lowerHandle.y = parent.y + parent.height + + var upper_value = slider.getUpperValueFromHandle(); + var lower_value = upper_value - (upperHandle.value - lowerHandle.value); + UM.LayerView.setCurrentLayer(upper_value); + UM.LayerView.setMinimumLayer(lower_value); + } + } + } + + Rectangle { + id: upperHandle + y: parent.height - (parent.minimumRangeHandleSize + 2 * parent.handleSize) + width: parent.handleSize + height: parent.handleSize + anchors.horizontalCenter: parent.horizontalCenter + radius: parent.handleRadius + color: parent.upperHandleColor + + visible: slider.layersVisible + + property real value: UM.LayerView.currentLayer + function setValue(value) + { + UM.LayerView.setCurrentLayer(value); + } + + MouseArea { + anchors.fill: parent + + drag.target: parent + drag.axis: Drag.YAxis + drag.minimumY: 0 + drag.maximumY: parent.parent.height - (2 * parent.parent.handleSize + parent.parent.minimumRangeHandleSize) + + onPressed: parent.parent.activeHandle = upperHandle + onPositionChanged: + { + if(lowerHandle.y - (upperHandle.y + upperHandle.height) < parent.parent.minimumRangeHandleSize) + { + lowerHandle.y = upperHandle.y + upperHandle.height + parent.parent.minimumRangeHandleSize; + } + rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height); + + UM.LayerView.setCurrentLayer(slider.getUpperValueFromHandle()); + } + } + } + + Rectangle { + id: lowerHandle + y: parent.height - parent.handleSize + width: parent.handleSize + height: parent.handleSize + anchors.horizontalCenter: parent.horizontalCenter + radius: parent.handleRadius + color: parent.lowerHandleColor + + visible: slider.layersVisible + + property real value: UM.LayerView.minimumLayer + function setValue(value) + { + UM.LayerView.setMinimumLayer(value); + } + + MouseArea { + anchors.fill: parent + + drag.target: parent + drag.axis: Drag.YAxis + drag.minimumY: upperHandle.height + parent.parent.minimumRangeHandleSize + drag.maximumY: parent.parent.height - parent.height + + onPressed: parent.parent.activeHandle = lowerHandle + onPositionChanged: + { + if(lowerHandle.y - (upperHandle.y + upperHandle.height) < parent.parent.minimumRangeHandleSize) + { + upperHandle.y = lowerHandle.y - (upperHandle.height + parent.parent.minimumRangeHandleSize); + } + rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height) + + UM.LayerView.setMinimumLayer(slider.getLowerValueFromHandle()); + } + } + } + + UM.PointingRectangle + { + x: parent.width + UM.Theme.getSize("slider_layerview_background").width / 2; + y: Math.floor(slider.activeHandle.y + slider.activeHandle.height / 2 - height / 2); + + target: Qt.point(0, slider.activeHandle.y + slider.activeHandle.height / 2) + arrowSize: UM.Theme.getSize("default_arrow").width + + height: (Math.floor(UM.Theme.getSize("slider_handle").height + UM.Theme.getSize("default_margin").height) / 2) * 2 // Make sure height has an integer middle so drawing a pointy border is easier + width: valueLabel.width + UM.Theme.getSize("default_margin").width + Behavior on height { NumberAnimation { duration: 50; } } + + color: UM.Theme.getColor("lining"); + + visible: slider.layersVisible + + UM.PointingRectangle + { + color: UM.Theme.getColor("tool_panel_background") + target: Qt.point(0, height / 2 + UM.Theme.getSize("default_lining").width) + arrowSize: UM.Theme.getSize("default_arrow").width + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_lining").width + + MouseArea //Catch all mouse events (so scene doesnt handle them) + { + anchors.fill: parent + } + } + + TextField + { + id: valueLabel + property string maxValue: slider.maximumValue + 1 + text: slider.activeHandle.value + 1 + horizontalAlignment: TextInput.AlignRight; + onEditingFinished: + { + // Ensure that the cursor is at the first position. On some systems the text isn't fully visible + // Seems to have to do something with different dpi densities that QML doesn't quite handle. + // Another option would be to increase the size even further, but that gives pretty ugly results. + cursorPosition = 0; + if(valueLabel.text != '') + { + slider.activeHandle.setValue(valueLabel.text - 1); + } + } + validator: IntValidator { bottom: 1; top: slider.maximumValue + 1; } + + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2; + anchors.verticalCenter: parent.verticalCenter; + + width: Math.max(UM.Theme.getSize("line").width * maxValue.length + 2, 20); + style: TextFieldStyle + { + textColor: UM.Theme.getColor("setting_control_text"); + font: UM.Theme.getFont("default"); + background: Item { } + } + + Keys.onUpPressed: slider.activeHandle.setValue(slider.activeHandle.value + ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + Keys.onDownPressed: slider.activeHandle.setValue(slider.activeHandle.value - ((event.modifiers & Qt.ShiftModifier) ? 10 : 1)) + } + + BusyIndicator + { + id: busyIndicator; + anchors.left: parent.right; + anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2; + anchors.verticalCenter: parent.verticalCenter; + + width: UM.Theme.getSize("slider_handle").height; + height: width; + + running: UM.LayerView.busy; + visible: UM.LayerView.busy; } - text: catalog.i18nc("@label", "test") } } } diff --git a/plugins/LayerView/LayerViewProxy.py b/plugins/LayerView/LayerViewProxy.py index d214f36407..bc372aeaf8 100644 --- a/plugins/LayerView/LayerViewProxy.py +++ b/plugins/LayerView/LayerViewProxy.py @@ -30,8 +30,7 @@ class LayerViewProxy(QObject): active_view = self._controller.getActiveView() if type(active_view) == LayerView.LayerView.LayerView: return active_view.getMaxLayers() - #return 100 - + @pyqtProperty(int, notify = currentLayerChanged) def currentLayer(self): active_view = self._controller.getActiveView() @@ -124,25 +123,13 @@ class LayerViewProxy(QObject): return active_view.getExtruderCount() return 0 - @pyqtSlot() - def enableLegend(self): - active_view = self._controller.getActiveView() - if type(active_view) == LayerView.LayerView.LayerView: - active_view.enableLegend() - - @pyqtSlot() - def disableLegend(self): - active_view = self._controller.getActiveView() - if type(active_view) == LayerView.LayerView.LayerView: - active_view.disableLegend() - def _layerActivityChanged(self): self.activityChanged.emit() def _onLayerChanged(self): self.currentLayerChanged.emit() self._layerActivityChanged() - + def _onMaxLayersChanged(self): self.maxLayersChanged.emit() diff --git a/plugins/LayerView/layers.shader b/plugins/LayerView/layers.shader old mode 100644 new mode 100755 diff --git a/plugins/LayerView/layers3d.shader b/plugins/LayerView/layers3d.shader old mode 100644 new mode 100755 index 5bc6066152..6f5e986eec --- a/plugins/LayerView/layers3d.shader +++ b/plugins/LayerView/layers3d.shader @@ -16,7 +16,7 @@ vertex41core = in lowp vec4 a_material_color; in highp vec4 a_normal; in highp vec2 a_line_dim; // line width and thickness - in highp int a_extruder; // Note: cannot use this in compatibility, int is only available in newer OpenGL. + in highp float a_extruder; in highp float a_line_type; out lowp vec4 v_color; @@ -53,7 +53,7 @@ vertex41core = v_vertex = world_space_vert.xyz; v_normal = (u_normalMatrix * normalize(a_normal)).xyz; v_line_dim = a_line_dim; - v_extruder = a_extruder; + v_extruder = int(a_extruder); v_line_type = a_line_type; v_extruder_opacity = u_extruder_opacity; @@ -128,7 +128,7 @@ geometry41core = if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) { // fixed size for movements - size_x = 0.2; + size_x = 0.05; } else { size_x = v_line_dim[0].x / 2 + 0.01; // radius, and make it nicely overlapping } diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 9811316948..cb65da635b 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -209,6 +209,8 @@ Item { { case "int": return settingTextField + case "[int]": + return settingTextField case "float": return settingTextField case "enum": diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py old mode 100644 new mode 100755 index 05f7c0e6f5..50ca1dbd06 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -90,9 +90,8 @@ class SliceInfo(Extension): # Listing all files placed on the buildplate modelhashes = [] for node in DepthFirstIterator(CuraApplication.getInstance().getController().getScene().getRoot()): - if type(node) is not SceneNode or not node.getMeshData(): - continue - modelhashes.append(node.getMeshData().getHash()) + if node.callDecoration("isSliceable"): + modelhashes.append(node.getMeshData().getHash()) # Creating md5sums and formatting them as discussed on JIRA modelhash_formatted = ",".join(modelhashes) diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index a7223128b4..b89ed58f18 100644 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -327,6 +327,9 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): ## Set the authentication state. # \param auth_state \type{AuthState} Enum value representing the new auth state def setAuthenticationState(self, auth_state): + if auth_state == self._authentication_state: + return # Nothing to do here. + if auth_state == AuthState.AuthenticationRequested: Logger.log("d", "Authentication state changed to authentication requested.") self.setAcceptsCommands(False) @@ -367,9 +370,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._authentication_timer.stop() self._authentication_counter = 0 - if auth_state != self._authentication_state: - self._authentication_state = auth_state - self.authenticationStateChanged.emit() + self._authentication_state = auth_state + self.authenticationStateChanged.emit() authenticationStateChanged = pyqtSignal() @@ -558,7 +560,6 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._preheat_bed_timer.stop() self.preheatBedRemainingTimeChanged.emit() - def close(self): Logger.log("d", "Closing connection of printer %s with ip %s", self._key, self._address) self._updateJobState("") @@ -601,10 +602,6 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): # \param filter_by_machine Whether to filter MIME types by machine. This # is ignored. def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None): - if self._progress != 0: - self._error_message = Message(i18n_catalog.i18nc("@info:status", "Unable to start a new print job because the printer is busy. Please check the printer.")) - self._error_message.show() - return if self._printer_state != "idle": self._error_message = Message( i18n_catalog.i18nc("@info:status", "Unable to start a new print job, printer is busy. Current printer status is %s.") % self._printer_state) @@ -1064,17 +1061,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): if status_code in [200, 201, 202, 204]: pass # Request was successful! else: - operation_type = "Unknown" - if reply.operation() == QNetworkAccessManager.GetOperation: - operation_type = "Get" - elif reply.operation() == QNetworkAccessManager.PutOperation: - operation_type = "Put" - elif reply.operation() == QNetworkAccessManager.PostOperation: - operation_type = "Post" - elif reply.operation() == QNetworkAccessManager.DeleteOperation: - operation_type = "Delete" - - Logger.log("d", "Something went wrong when trying to update data of API (%s). Message: %s Statuscode: %s, operation: %s", reply_url, reply.readAll(), status_code, operation_type) + Logger.log("d", "Something went wrong when trying to update data of API (%s). Message: %s Statuscode: %s", reply_url, reply.readAll(), status_code) else: Logger.log("d", "NetworkPrinterOutputDevice got an unhandled operation %s", reply.operation()) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 238a13bf61..0428c0f5c2 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -45,7 +45,7 @@ class UMOUpgradeSelection(MachineAction): def _createDefinitionChangesContainer(self, global_container_stack): # Create a definition_changes container to store the settings in and add it to the stack - definition_changes_container = UM.Settings.InstanceContainer(global_container_stack.getName() + "_settings") + definition_changes_container = UM.Settings.InstanceContainer.InstanceContainer(global_container_stack.getName() + "_settings") definition = global_container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c8e2afd8f7..f045d6410e 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -93,6 +93,7 @@ "description": "Whether to wait until the nozzle temperature is reached at the start.", "default_value": true, "type": "bool", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false @@ -103,6 +104,7 @@ "description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.", "default_value": true, "type": "bool", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false @@ -249,6 +251,17 @@ "settable_per_extruder": true, "settable_per_meshgroup": false }, + "machine_nozzle_temp_enabled": + { + "label": "Enable Nozzle Temperature Control", + "description": "Whether to control temperature from Cura. Turn this off to control nozzle temperature from outside of Cura.", + "default_value": true, + "value": "machine_gcode_flavor != \"UltiGCode\"", + "type": "bool", + "settable_per_mesh": false, + "settable_per_extruder": true, + "settable_per_meshgroup": false + }, "machine_nozzle_heat_up_speed": { "label": "Heat up speed", @@ -256,6 +269,7 @@ "default_value": 2.0, "unit": "°C/s", "type": "float", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -266,6 +280,7 @@ "default_value": 2.0, "unit": "°C/s", "type": "float", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -276,6 +291,7 @@ "default_value": 50.0, "unit": "s", "type": "float", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -908,6 +924,15 @@ "value": "top_bottom_pattern", "settable_per_mesh": true }, + "skin_angles": + { + "label": "Top/Bottom Line Directions", + "description": "A list of integer line directions to use when the top/bottom layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).", + "type": "[int]", + "default_value": "[ ]", + "enabled": "top_bottom_pattern != 'concentric'", + "settable_per_mesh": true + }, "wall_0_inset": { "label": "Outer Wall Inset", @@ -1089,6 +1114,16 @@ "value": "'lines' if infill_sparse_density > 25 else 'grid'", "settable_per_mesh": true }, + "infill_angles": + { + "label": "Infill Line Directions", + "description": "A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees for the lines and zig zag patterns and 45 degrees for all other patterns).", + "type": "[int]", + "default_value": "[ ]", + "enabled": "infill_pattern != 'concentric' and infill_pattern != 'concentric_3d' and infill_pattern != 'cubicsubdiv'", + "enabled": "infill_sparse_density > 0", + "settable_per_mesh": true + }, "spaghetti_infill_enabled": { "label": "Spaghetti Infill", @@ -1267,7 +1302,7 @@ "minimum_value": "0", "maximum_value_warning": "4", "maximum_value": "(20 - math.log(infill_line_distance) / math.log(2)) if infill_line_distance > 0 and not spaghetti_infill_enabled else 0", - "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' not spaghetti_infill_enabled", + "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled", "settable_per_mesh": true }, "gradual_infill_step_height": @@ -1301,6 +1336,75 @@ "minimum_value": "0", "default_value": 0, "settable_per_mesh": true + }, + "expand_skins_into_infill": + { + "label": "Expand Skins Into Infill", + "description": "Expand skin areas of top and/or bottom skin of flat surfaces. By default, skins stop under the wall lines that surround infill but this can lead to holes appearing when the infill density is low. This setting extends the skins beyond the wall lines so that the infill on the next layer rests on skin.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true, + "children": + { + "expand_upper_skins": + { + "label": "Expand Upper Skins", + "description": "Expand upper skin areas (areas with air above) so that they support infill above.", + "type": "bool", + "default_value": false, + "value": "expand_skins_into_infill", + "settable_per_mesh": true + }, + "expand_lower_skins": + { + "label": "Expand Lower Skins", + "description": "Expand lower skin areas (areas with air below) so that they are anchored by the infill layers above and below.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true + } + } + }, + "expand_skins_expand_distance": + { + "label": "Skin Expand Distance", + "description": "The distance the skins are expanded into the infill. The default distance is enough to bridge the gap between the infill lines and will stop holes appearing in the skin where it meets the wall when the infill density is low. A smaller distance will often be sufficient.", + "unit": "mm", + "type": "float", + "default_value": 2.8, + "value": "infill_line_distance * 1.4", + "minimum_value": "0", + "enabled": "expand_upper_skins or expand_lower_skins", + "settable_per_mesh": true + }, + "max_skin_angle_for_expansion": + { + "label": "Maximum Skin Angle for Expansion", + "description": "Top and or bottom surfaces of your object with an angle larger than this setting, won't have their top/bottom skin expanded. This avoids expanding the narrow skin areas that are created when the model surface has a near vertical slope. An angle of 0° is horizontal, while an angle of 90° is vertical.", + "unit": "°", + "type": "float", + "minimum_value": "0", + "minimum_value_warning": "2", + "maximum_value_warning": "45", + "maximum_value": "90", + "default_value": 20, + "enabled": "expand_upper_skins or expand_lower_skins", + "settable_per_mesh": true, + "children": + { + "min_skin_width_for_expansion": + { + "label": "Minimum Skin Width for Expansion", + "description": "Skin areas narrower than this are not expanded. This avoids expanding the narrow skin areas that are created when the model surface has a slope close to the vertical.", + "unit": "mm", + "type": "float", + "default_value": 2.24, + "value": "top_layers * layer_height / math.tan(math.radians(max_skin_angle_for_expansion))", + "minimum_value": "0", + "enabled": "expand_upper_skins or expand_lower_skins", + "settable_per_mesh": true + } + } } } }, @@ -1318,7 +1422,7 @@ "description": "Change the temperature for each layer automatically with the average flow speed of that layer.", "type": "bool", "default_value": false, - "enabled": "False", + "enabled": "machine_nozzle_temp_enabled and False", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1329,22 +1433,24 @@ "unit": "°C", "type": "float", "default_value": 210, - "enabled": false, + "minimum_value_warning": "0", + "maximum_value_warning": "285", + "enabled": "machine_nozzle_temp_enabled", "settable_per_extruder": true, "minimum_value": "-273.15" }, "material_print_temperature": { "label": "Printing Temperature", - "description": "The temperature used for printing. If this is 0, the extruder will not heat up for this print.", + "description": "The temperature used for printing.", "unit": "°C", "type": "float", "default_value": 210, "value": "default_material_print_temperature", "minimum_value": "-273.15", "minimum_value_warning": "0", - "maximum_value_warning": "270", - "enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\"", + "maximum_value_warning": "285", + "enabled": "machine_nozzle_temp_enabled and not (material_flow_dependent_temperature)", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1355,11 +1461,11 @@ "unit": "°C", "type": "float", "default_value": 215, - "value": "material_print_temperature + 5", + "value": "material_print_temperature", "minimum_value": "-273.15", "minimum_value_warning": "0", - "maximum_value_warning": "260", - "enabled": "machine_gcode_flavor != \"UltiGCode\"", + "maximum_value_warning": "285", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1374,7 +1480,7 @@ "minimum_value": "-273.15", "minimum_value_warning": "material_standby_temperature", "maximum_value_warning": "material_print_temperature", - "enabled": "machine_gcode_flavor != \"UltiGCode\"", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1389,7 +1495,7 @@ "minimum_value": "-273.15", "minimum_value_warning": "material_standby_temperature", "maximum_value_warning": "material_print_temperature", - "enabled": "machine_gcode_flavor != \"UltiGCode\"", + "enabled": "machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1400,8 +1506,7 @@ "unit": "[[mm³,°C]]", "type": "str", "default_value": "[[3.5,200],[7.0,240]]", - "enabled": "False", - "comments": "old enabled function: material_flow_dependent_temperature", + "enabled": "False and machine_nozzle_temp_enabled and material_flow_dependent_temperature", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1415,8 +1520,7 @@ "minimum_value": "0", "maximum_value_warning": "10.0", "maximum_value": "machine_nozzle_heat_up_speed", - "enabled": "False", - "comments": "old enabled function: material_flow_dependent_temperature or machine_extruder_count > 1", + "enabled": "material_flow_dependent_temperature or (machine_extruder_count > 1 and material_final_print_temperature != material_print_temperature)", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -1430,7 +1534,7 @@ "default_value": 60, "minimum_value": "-273.15", "minimum_value_warning": "0", - "maximum_value_warning": "260", + "maximum_value_warning": "130", "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1447,7 +1551,7 @@ "value": "resolveOrValue('material_bed_temperature')", "minimum_value": "-273.15", "minimum_value_warning": "max(extruderValues('material_bed_temperature'))", - "maximum_value_warning": "260", + "maximum_value_warning": "130", "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"", "settable_per_mesh": false, "settable_per_extruder": false, @@ -1623,7 +1727,7 @@ "minimum_value": "-273.15", "minimum_value_warning": "0", "maximum_value_warning": "260", - "enabled": "machine_extruder_count > 1 and machine_gcode_flavor != \"UltiGCode\"", + "enabled": "machine_extruder_count > 1 and machine_nozzle_temp_enabled", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2476,6 +2580,16 @@ "settable_per_mesh": false, "settable_per_extruder": false }, + "travel_retract_before_outer_wall": + { + "label": "Retract Before Outer Wall", + "description": "Always retract when moving to start an outer wall.", + "type": "bool", + "default_value": false, + "enabled": "retraction_enable", + "settable_per_mesh": false, + "settable_per_extruder": false + }, "travel_avoid_other_parts": { "label": "Avoid Printed Parts When Traveling", @@ -3841,7 +3955,7 @@ "unit": "mm", "type": "float", "default_value": 2, - "value": "max(2 * min(extruderValues('prime_tower_line_width')), 0.5 * (resolveOrValue('prime_tower_size') - math.sqrt(max(0, resolveOrValue('prime_tower_size') ** 2 - max(extruderValues('prime_tower_min_volume')) / resolveOrValue('layer_height')))))", + "value": "round(max(2 * min(extruderValues('prime_tower_line_width')), 0.5 * (resolveOrValue('prime_tower_size') - math.sqrt(max(0, resolveOrValue('prime_tower_size') ** 2 - max(extruderValues('prime_tower_min_volume')) / resolveOrValue('layer_height'))))), 3)", "resolve": "max(extruderValues('prime_tower_wall_thickness'))", "minimum_value": "0.001", "minimum_value_warning": "2 * min(extruderValues('prime_tower_line_width'))", diff --git a/resources/definitions/renkforce_rf100.def.json b/resources/definitions/renkforce_rf100.def.json index 7df1fa46fd..bdbc44ea8c 100644 --- a/resources/definitions/renkforce_rf100.def.json +++ b/resources/definitions/renkforce_rf100.def.json @@ -18,8 +18,8 @@ "bottom_thickness": { "value": "0.5" }, - "brim_line_count": { - "value": "20.0" + "brim_width": { + "value": "2.0" }, "cool_fan_enabled": { "value": "True" @@ -81,6 +81,9 @@ "material_diameter": { "value": "1.75" }, + "material_flow": { + "value": "110" + }, "material_print_temperature": { "value": "210.0" }, @@ -151,13 +154,13 @@ "value": "3.0" }, "skirt_line_count": { - "value": "1.0" + "value": "3" }, "speed_infill": { "value": "50.0" }, "speed_layer_0": { - "value": "30.0" + "value": "15.0" }, "speed_print": { "value": "50.0" diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 84e09113f3..a52075fe5e 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -100,6 +100,9 @@ }, "machine_acceleration": { "default_value": 3000 + }, + "machine_nozzle_temp_enabled": { + "default_value": false } } } diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index d7245e5178..27db3f19c7 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -106,6 +106,7 @@ "line_width": { "value": "machine_nozzle_size * 0.875" }, "machine_min_cool_heat_time_window": { "value": "15" }, "default_material_print_temperature": { "value": "200" }, + "material_print_temperature_layer_0": { "value": "material_print_temperature + 5" }, "material_bed_temperature": { "maximum_value": "115" }, "material_bed_temperature_layer_0": { "maximum_value": "115" }, "material_standby_temperature": { "value": "100" }, diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index 03b3b50a08..f3f188dd48 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -62,7 +62,7 @@ "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" + "value": "'M104 S0 ;extruder heater off' + ('\\nM140 S0 ;heated bed heater off' if machine_heated_bed else '') + '\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning'" } } } diff --git a/resources/definitions/vertex_k8400.def.json b/resources/definitions/vertex_k8400.def.json new file mode 100644 index 0000000000..3d1ca2d1a9 --- /dev/null +++ b/resources/definitions/vertex_k8400.def.json @@ -0,0 +1,84 @@ +{ + "id": "vertex_k8400", + "version": 2, + "name": "Vertex K8400", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "manufacturer": "Velleman", + "category": "Other", + "file_formats": "text/x-gcode", + "icon": "icon_ultimaker2", + "platform": "Vertex_build_panel.stl", + "platform_offset": [0, -2, 0], + "supports_usb_connection": true, + "supported_actions": ["MachineSettingsAction"] + }, + "overrides": { + "machine_name": { "default_value": "Vertex K8400" }, + "machine_heated_bed": { + "default_value": true + }, + "material_bed_temperature": { + "default_value": 0 + }, + "material_bed_temperature_layer_0": { + "default_value": 0 + }, + "machine_width": { + "default_value": 200 + }, + "machine_height": { + "default_value": 190 + }, + "machine_depth": { + "default_value": 200 + }, + "machine_disallowed_areas": { "default_value": [ + [[-100,100],[-100,80],[100,80],[100,100]] + ]}, + "machine_center_is_zero": { + "default_value": false + }, + "machine_nozzle_size": { + "default_value": 0.35 + }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_head_polygon": { + "default_value": [ + [-60, -18], + [-60, 40], + [18, 40], + [18, -18] + ] + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-60, -40], + [-60, 40], + [18, 40], + [18, -40] + ] + }, + "gantry_height": { + "default_value": 18 + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." + }, + "machine_end_gcode": { + "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" + } + } +} \ No newline at end of file diff --git a/resources/definitions/vertex_k8400_dual.def.json b/resources/definitions/vertex_k8400_dual.def.json new file mode 100644 index 0000000000..7b5efcee9c --- /dev/null +++ b/resources/definitions/vertex_k8400_dual.def.json @@ -0,0 +1,92 @@ +{ + "id": "vertex_k8400_dual", + "version": 2, + "name": "Vertex K8400 Dual", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "manufacturer": "Velleman", + "category": "Other", + "file_formats": "text/x-gcode", + "icon": "icon_ultimaker2", + "platform": "Vertex_build_panel.stl", + "platform_offset": [0, -2, 0], + "machine_extruder_trains": { + "0": "vertex_k8400_dual_1st", + "1": "vertex_k8400_dual_2nd" + } + }, + "overrides": { + "machine_name": { "default_value": "Vertex K8400 Dual" }, + "machine_heated_bed": { + "default_value": true + }, + "material_bed_temperature": { + "default_value": 0 + }, + "material_bed_temperature_layer_0": { + "default_value": 0 + }, + "machine_width": { + "default_value": 223.7 + }, + "machine_height": { + "default_value": 190 + }, + "machine_depth": { + "default_value": 200 + }, + "machine_disallowed_areas": { "default_value": [ + [[-111.85,100],[111.85,100],[-111.85,80],[111.85,80]] + ]}, + "machine_center_is_zero": { + "default_value": false + }, + "machine_use_extruder_offset_to_offset_coords": { + "default_value": true + }, + "machine_nozzle_size": { + "default_value": 0.35 + }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_head_polygon": { + "default_value": [ + [-60, -18], + [-60, 40], + [18, 40], + [18, -18] + ] + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-60, -40], + [-60, 40], + [18, 40], + [18, -40] + ] + }, + "gantry_height": { + "default_value": 18 + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "machine_extruder_count": { + "default_value": 2 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." + }, + "machine_end_gcode": { + "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" + } + } +} \ No newline at end of file diff --git a/resources/extruders/vertex_k8400_dual_1st.def.json b/resources/extruders/vertex_k8400_dual_1st.def.json new file mode 100644 index 0000000000..74a9c557a5 --- /dev/null +++ b/resources/extruders/vertex_k8400_dual_1st.def.json @@ -0,0 +1,26 @@ +{ + "id": "vertex_k8400_dual_1st", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "vertex_k8400_dual", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { "default_value": 23.7 }, + "machine_nozzle_offset_y": { "default_value": 0.0 }, + + "machine_extruder_start_pos_abs": { "default_value": true }, + "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, + "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" } + } +} diff --git a/resources/extruders/vertex_k8400_dual_2nd.def.json b/resources/extruders/vertex_k8400_dual_2nd.def.json new file mode 100644 index 0000000000..ffa4b77a1e --- /dev/null +++ b/resources/extruders/vertex_k8400_dual_2nd.def.json @@ -0,0 +1,26 @@ +{ + "id": "vertex_k8400_dual_2nd", + "version": 2, + "name": "Extruder 2", + "inherits": "fdmextruder", + "metadata": { + "machine": "vertex_k8400_dual", + "position": "1" + }, + + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 }, + + "machine_extruder_start_pos_abs": { "default_value": true }, + "machine_extruder_start_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_start_pos_y": { "value": "prime_tower_position_y" }, + "machine_extruder_end_pos_abs": { "default_value": true }, + "machine_extruder_end_pos_x": { "value": "prime_tower_position_x" }, + "machine_extruder_end_pos_y": { "value": "prime_tower_position_y" } + } +} diff --git a/resources/meshes/Vertex_build_panel.stl b/resources/meshes/Vertex_build_panel.stl new file mode 100644 index 0000000000..bb50da0625 Binary files /dev/null and b/resources/meshes/Vertex_build_panel.stl differ diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 8d74f1b67c..1a345deafa 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -262,6 +262,7 @@ Item id: reloadAllAction; text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models"); iconName: "document-revert"; + shortcut: "F5" onTriggered: Printer.reloadAll(); } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml old mode 100644 new mode 100755 index b73bd21600..1d998f0fee --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -266,11 +266,11 @@ UM.MainWindow anchors.fill: parent; onDropped: { - if(drop.urls.length > 0) + if (drop.urls.length > 0) { // Import models var imported_model = -1; - for(var i in drop.urls) + for (var i in drop.urls) { // There is no endsWith in this version of JS... if ((drop.urls[i].length <= 12) || (drop.urls[i].substring(drop.urls[i].length-12) !== ".curaprofile")) { @@ -287,7 +287,7 @@ UM.MainWindow var import_result = Cura.ContainerManager.importProfiles(drop.urls); if (import_result.message !== "") { messageDialog.text = import_result.message - if(import_result.status == "ok") + if (import_result.status == "ok") { messageDialog.icon = StandardIcon.Information } @@ -306,18 +306,6 @@ UM.MainWindow } } - Legend - { - id: legend - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("default_margin").height - right: sidebar.left - rightMargin: UM.Theme.getSize("default_margin").width - } - } - JobSpecs { id: jobSpecs @@ -882,6 +870,21 @@ UM.MainWindow } } + DiscardOrKeepProfileChangesDialog + { + id: discardOrKeepProfileChangesDialog + } + + Connections + { + target: Printer + onShowDiscardOrKeepProfileChanges: + { + discardOrKeepProfileChangesDialog.show() + } + + } + Connections { target: Cura.Actions.addMachine diff --git a/resources/qml/DiscardOrKeepProfileChangesDialog.qml b/resources/qml/DiscardOrKeepProfileChangesDialog.qml new file mode 100644 index 0000000000..23fabb8d6c --- /dev/null +++ b/resources/qml/DiscardOrKeepProfileChangesDialog.qml @@ -0,0 +1,208 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.1 as Cura + +UM.Dialog +{ + id: base + title: catalog.i18nc("@title:window", "Discard or Keep changes") + + width: 800 + height: 400 + property var changesModel: Cura.UserChangesModel{ id: userChangesModel} + onVisibilityChanged: + { + if(visible) + { + changesModel.forceUpdate() + } + + discardOrKeepProfileChangesDropDownButton.currentIndex = UM.Preferences.getValue("cura/choice_on_profile_override") + } + + Column + { + anchors.fill: parent + spacing: UM.Theme.getSize("default_margin").width + + UM.I18nCatalog + { + id: catalog; + name: "cura" + } + + Row + { + height: childrenRect.height + anchors.margins: UM.Theme.getSize("default_margin").width + anchors.left: parent.left + anchors.right: parent.right + spacing: UM.Theme.getSize("default_margin").width + + Label + { + text: "You have customized some profile settings.\nWould you like to keep or discard those settings?" + anchors.margins: UM.Theme.getSize("default_margin").width + font: UM.Theme.getFont("default") + wrapMode: Text.WordWrap + } + } + + TableView + { + anchors.margins: UM.Theme.getSize("default_margin").width + anchors.left: parent.left + anchors.right: parent.right + height: base.height - 200 + id: tableView + Component + { + id: labelDelegate + Label + { + property var extruder_name: userChangesModel.getItem(styleData.row).extruder + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + font: UM.Theme.getFont("system") + text: + { + var result = styleData.value + if (extruder_name != "") + { + result += " (" + extruder_name + ")" + } + return result + } + } + } + + Component + { + id: defaultDelegate + Label + { + text: styleData.value + font: UM.Theme.getFont("system") + color: UM.Theme.getColor("setting_control_disabled_text") + } + } + + TableViewColumn + { + role: "label" + title: catalog.i18nc("@title:column", "Profile settings") + delegate: labelDelegate + width: tableView.width * 0.4 + } + TableViewColumn + { + role: "original_value" + title: catalog.i18nc("@title:column", "Default") + width: tableView.width * 0.3 + delegate: defaultDelegate + } + TableViewColumn + { + role: "user_value" + title: catalog.i18nc("@title:column", "Customized") + width: tableView.width * 0.3 - 1 + } + section.property: "category" + section.delegate: Label + { + text: section + font.bold: true + } + + model: base.changesModel + } + + Item + { + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: UM.Theme.getSize("default_margin").width + height:childrenRect.height + + ComboBox + { + id: discardOrKeepProfileChangesDropDownButton + model: [ + catalog.i18nc("@option:discardOrKeep", "Always ask me this"), + catalog.i18nc("@option:discardOrKeep", "Discard and never ask again"), + catalog.i18nc("@option:discardOrKeep", "Keep and never ask again") + ] + width: 300 + currentIndex: UM.Preferences.getValue("cura/choice_on_profile_override") + onCurrentIndexChanged: + { + UM.Preferences.setValue("cura/choice_on_profile_override", currentIndex) + if (currentIndex == 1) { + // 1 == "Discard and never ask again", so only enable the "Discard" button + discardButton.enabled = true + keepButton.enabled = false + } + else if (currentIndex == 2) { + // 2 == "Keep and never ask again", so only enable the "Keep" button + keepButton.enabled = true + discardButton.enabled = false + } + else { + // 0 == "Always ask me this", so show both + keepButton.enabled = true + discardButton.enabled = true + } + } + } + } + + Item + { + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: UM.Theme.getSize("default_margin").width + height:childrenRect.height + + Button + { + id: discardButton + text: catalog.i18nc("@action:button", "Discard"); + anchors.right: parent.right + onClicked: + { + Printer.discardOrKeepProfileChangesClosed("discard") + base.hide() + } + isDefault: true + } + + Button + { + id: keepButton + text: catalog.i18nc("@action:button", "Keep"); + anchors.right: discardButton.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + onClicked: + { + Printer.discardOrKeepProfileChangesClosed("keep") + base.hide() + } + } + + Button + { + id: createNewProfileButton + text: catalog.i18nc("@action:button", "Create New Profile"); + anchors.left: parent.left + action: Cura.Actions.addProfile + onClicked: base.hide() + } + } + } +} \ No newline at end of file diff --git a/resources/qml/Legend.qml b/resources/qml/Legend.qml deleted file mode 100644 index 94eeb1e903..0000000000 --- a/resources/qml/Legend.qml +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2015 Ultimaker B.V. -// Cura is released under the terms of the AGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.1 as UM -import Cura 1.0 as Cura - -Item { - id: base - - UM.I18nCatalog { id: catalog; name:"cura"} - - width: childrenRect.width - height: childrenRect.height - - Connections - { - target: Printer - onViewLegendItemsChanged: - { - legendItemRepeater.model = items - } - } - - Column - { - Repeater - { - id: legendItemRepeater - - Item { - anchors.right: parent.right - height: childrenRect.height - width: childrenRect.width - - Rectangle { - id: swatch - - anchors.right: parent.right - anchors.verticalCenter: label.verticalCenter - height: UM.Theme.getSize("setting_control").height / 2 - width: height - - color: modelData.color - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("text_subtext") - } - Label { - id: label - - text: modelData.title - font: UM.Theme.getFont("small") - color: UM.Theme.getColor("text_subtext") - - anchors.right: swatch.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2 - } - } - } - } -} diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml old mode 100644 new mode 100755 index 017de45521..d9170ec597 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -47,6 +47,8 @@ UM.PreferencesPage centerOnSelectCheckbox.checked = boolCheck(UM.Preferences.getValue("view/center_on_select")) UM.Preferences.resetPreference("view/top_layer_count"); topLayerCountCheckbox.checked = boolCheck(UM.Preferences.getValue("view/top_layer_count")) + UM.Preferences.resetPreference("cura/choice_on_profile_override") + choiceOnProfileOverrideDropDownButton.currentIndex = UM.Preferences.getValue("cura/choice_on_profile_override") if (plugins.find("id", "SliceInfoPlugin") > -1) { UM.Preferences.resetPreference("info/send_slice_info") @@ -257,44 +259,6 @@ UM.PreferencesPage } } - UM.TooltipArea { - width: childrenRect.width; - height: childrenRect.height; - text: catalog.i18nc("@info:tooltip","Display 5 top layers in layer view or only the top-most layer. Rendering 5 layers takes longer, but may show more information.") - - CheckBox - { - id: topLayerCountCheckbox - text: catalog.i18nc("@action:button","Display five top layers in layer view compatibility mode"); - checked: UM.Preferences.getValue("view/top_layer_count") == 5 - onClicked: - { - if(UM.Preferences.getValue("view/top_layer_count") == 5) - { - UM.Preferences.setValue("view/top_layer_count", 1) - } - else - { - UM.Preferences.setValue("view/top_layer_count", 5) - } - } - } - } - - UM.TooltipArea { - width: childrenRect.width - height: childrenRect.height - text: catalog.i18nc("@info:tooltip", "Should only the top layers be displayed in layerview?") - - CheckBox - { - id: topLayersOnlyCheckbox - text: catalog.i18nc("@option:check", "Only display top layer(s) in layer view compatibility mode") - checked: boolCheck(UM.Preferences.getValue("view/only_show_top_layers")) - onCheckedChanged: UM.Preferences.setValue("view/only_show_top_layers", checked) - } - } - UM.TooltipArea { width: childrenRect.width height: childrenRect.height @@ -377,6 +341,40 @@ UM.PreferencesPage } } + Item + { + //: Spacer + height: UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("default_margin").width + } + + Label + { + font.bold: true + text: catalog.i18nc("@label", "Override Profile") + } + + UM.TooltipArea + { + width: childrenRect.width; + height: childrenRect.height; + + text: catalog.i18nc("@info:tooltip", "When you have made changes to a profile and switched to a different one, a dialog will be shown asking whether you want to keep your modifications or not, or you can choose a default behaviour and never show that dialog again.") + + ComboBox + { + id: choiceOnProfileOverrideDropDownButton + + model: [ + catalog.i18nc("@option:discardOrKeep", "Always ask me this"), + catalog.i18nc("@option:discardOrKeep", "Discard and never ask again"), + catalog.i18nc("@option:discardOrKeep", "Keep and never ask again") + ] + width: 300 + currentIndex: UM.Preferences.getValue("cura/choice_on_profile_override") + onCurrentIndexChanged: UM.Preferences.setValue("cura/choice_on_profile_override", currentIndex) + } + } Item { diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index e6ddef7979..239e1a2aad 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -89,15 +89,20 @@ UM.ManagementPage id: machineActionRepeater model: base.currentItem ? Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id)) : null - Button + Item { - text: machineActionRepeater.model[index].label - onClicked: + width: childrenRect.width + 2 + height: childrenRect.height + Button { - actionDialog.content = machineActionRepeater.model[index].displayItem; - machineActionRepeater.model[index].displayItem.reset(); - actionDialog.title = machineActionRepeater.model[index].label; - actionDialog.show(); + text: machineActionRepeater.model[index].label + onClicked: + { + actionDialog.content = machineActionRepeater.model[index].displayItem; + machineActionRepeater.model[index].displayItem.reset(); + actionDialog.title = machineActionRepeater.model[index].label; + actionDialog.show(); + } } } } diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 17f76466ab..ba36674b40 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -155,8 +155,10 @@ TabView decimals: 2 maximumValue: 1000 - onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) - onValueChanged: updateCostPerMeter() + onValueChanged: { + base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) + updateCostPerMeter() + } } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } @@ -170,8 +172,10 @@ TabView decimals: 0 maximumValue: 10000 - onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) - onValueChanged: updateCostPerMeter() + onValueChanged: { + base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) + updateCostPerMeter() + } } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index da24f0f521..ce376e1c77 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -98,9 +98,9 @@ SettingItem selectByMouse: true; - maximumLength: 10; + maximumLength: (definition.type == "[int]") ? 20 : 10; - validator: RegExpValidator { regExp: (definition.type == "int") ? /^-?[0-9]{0,10}$/ : /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } // definition.type property from parent loader used to disallow fractional number entry + validator: RegExpValidator { regExp: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } // definition.type property from parent loader used to disallow fractional number entry Binding { diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 24022891c3..7138d4acd3 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -217,6 +217,8 @@ Item { case "int": return "SettingTextField.qml" + case "[int]": + return "SettingTextField.qml" case "float": return "SettingTextField.qml" case "enum": diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg index f99c3997f7..f8090e057c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg @@ -17,6 +17,9 @@ infill_wipe_dist = 0 layer_height = 0.2 machine_nozzle_cool_down_speed = 0.9 machine_nozzle_heat_up_speed = 1.4 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 +material_print_temperature = =default_material_print_temperature + 5 prime_tower_size = 17 retraction_combing = off retraction_hop = 0.2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg index c03e072c8e..a8d989fbae 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg @@ -17,6 +17,9 @@ infill_wipe_dist = 0 layer_height = 0.15 machine_nozzle_cool_down_speed = 0.9 machine_nozzle_heat_up_speed = 1.4 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 +material_print_temperature = =default_material_print_temperature + 5 prime_tower_size = 17 retraction_combing = off retraction_hop = 0.2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg index c88fe1a56a..5495276f1c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg @@ -14,7 +14,9 @@ brim_width = 7 cool_min_speed = 5 infill_wipe_dist = 0 layer_height = 0.06 -material_print_temperature = =default_material_print_temperature + 2 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 +material_print_temperature = =default_material_print_temperature - 3 prime_tower_size = 17 retraction_combing = off retraction_hop = 0.2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg index 9aaceb3a7a..d18b878a4f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -13,7 +13,9 @@ weight = 0 brim_width = 7 cool_min_speed = 7 infill_wipe_dist = 0 -material_print_temperature = =default_material_print_temperature + 5 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 +material_print_temperature = =default_material_print_temperature prime_tower_size = 17 retraction_combing = off retraction_hop = 0.2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg index 0cc074b7a0..e9370877e7 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg @@ -18,6 +18,9 @@ cool_min_speed = 6 infill_line_width = =round(line_width * 0.4 / 0.35, 2) infill_overlap_mm = 0.05 layer_height = 0.2 +material_final_print_temperature = =material_print_temperature - 10 +material_initial_print_temperature = =material_print_temperature - 5 +material_print_temperature = =default_material_print_temperature + 10 material_print_temperature_layer_0 = =material_print_temperature + 5 ooze_shield_angle = 40 raft_airgap = 0.25 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg index c887fb283d..cdb37b8f12 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg @@ -19,6 +19,9 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) infill_overlap = =0 infill_overlap_mm = 0.05 layer_height = 0.15 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 +material_print_temperature = =default_material_print_temperature + 10 material_print_temperature_layer_0 = =material_print_temperature + 5 ooze_shield_angle = 40 raft_airgap = 0.25 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg index 6555c13f74..f5e91fa71b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg @@ -17,6 +17,8 @@ cool_min_speed = 8 infill_line_width = =round(line_width * 0.4 / 0.35, 2) infill_overlap_mm = 0.05 layer_height = 0.06 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 material_print_temperature = =default_material_print_temperature - 10 material_print_temperature_layer_0 = =material_print_temperature + 5 ooze_shield_angle = 40 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg index eeea96cd18..d391e9df4f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg @@ -16,6 +16,8 @@ cool_min_layer_time_fan_speed_max = 5 cool_min_speed = 5 infill_line_width = =round(line_width * 0.4 / 0.35, 2) infill_overlap_mm = 0.05 +material_initial_print_temperature = =material_print_temperature - 5 +material_final_print_temperature = =material_print_temperature - 10 material_print_temperature = =default_material_print_temperature material_print_temperature_layer_0 = =material_print_temperature + 5 ooze_shield_angle = 40 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg index bcdd8044b8..03ea216f32 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg @@ -20,8 +20,11 @@ infill_pattern = tetrahedral infill_sparse_density = 96 layer_height = 0.2 line_width = =machine_nozzle_size * 0.95 +material_final_print_temperature = =material_print_temperature - 10 material_flow = 106 -material_print_temperature_layer_0 = =default_material_print_temperature + 2 +material_initial_print_temperature = =material_print_temperature - 5 +material_print_temperature = =default_material_print_temperature + 2 +material_print_temperature_layer_0 = =material_print_temperature retraction_count_max = 12 retraction_extra_prime_amount = 0.8 skin_overlap = 15 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index 567d9273b5..b29483a44f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -20,8 +20,11 @@ infill_pattern = tetrahedral infill_sparse_density = 96 layer_height = 0.15 line_width = =machine_nozzle_size * 0.95 +material_final_print_temperature = =material_print_temperature - 10 material_flow = 106 -material_print_temperature_layer_0 = =default_material_print_temperature + 2 +material_initial_print_temperature = =material_print_temperature - 5 +material_print_temperature = =default_material_print_temperature + 2 +material_print_temperature_layer_0 = =material_print_temperature retraction_amount = 7 retraction_count_max = 12 retraction_extra_prime_amount = 0.8 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg index 75d76a32f2..99bd3a90da 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg @@ -19,10 +19,11 @@ infill_line_width = =round(line_width * 0.38 / 0.38, 2) infill_pattern = tetrahedral infill_sparse_density = 96 line_width = =machine_nozzle_size * 0.95 +material_final_print_temperature = =material_print_temperature - 10 material_flow = 106 material_initial_print_temperature = =material_print_temperature - 10 material_print_temperature = =default_material_print_temperature -material_print_temperature_layer_0 = =default_material_print_temperature +material_print_temperature_layer_0 = =material_print_temperature retraction_count_max = 12 retraction_extra_prime_amount = 0.8 skin_overlap = 15 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg new file mode 100644 index 0000000000..eb69e804c0 --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg @@ -0,0 +1,26 @@ +[general] +version = 2 +name = Draft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = draft +material = generic_nylon_ultimaker3_AA_0.8 +weight = -2 + +[values] +brim_width = 8.0 +cool_fan_full_at_height = =layer_height_0 + 4 * layer_height +cool_min_layer_time_fan_speed_max = 20 +infill_before_walls = True +infill_pattern = triangles +machine_nozzle_cool_down_speed = 0.9 +material_standby_temperature = 100 +raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) +raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) +switch_extruder_retraction_amount = 30 +switch_extruder_retraction_speeds = 40 +wall_line_width_x = =wall_line_width + diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg new file mode 100644 index 0000000000..4a226996b3 --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 2 +name = Superdraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = superdraft +material = generic_nylon_ultimaker3_AA_0.8 +weight = -2 + +[values] +brim_width = 8.0 +cool_fan_full_at_height = =layer_height_0 + 4 * layer_height +cool_min_layer_time_fan_speed_max = 20 +infill_before_walls = True +infill_pattern = triangles +layer_height = 0.4 +machine_nozzle_cool_down_speed = 0.9 +material_standby_temperature = 100 +raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) +raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) +switch_extruder_retraction_amount = 30 +switch_extruder_retraction_speeds = 40 +wall_line_width_x = =wall_line_width + diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg new file mode 100644 index 0000000000..444aac8eda --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg @@ -0,0 +1,27 @@ +[general] +version = 2 +name = Verydraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = verydraft +material = generic_nylon_ultimaker3_AA_0.8 +weight = -2 + +[values] +brim_width = 8.0 +cool_fan_full_at_height = =layer_height_0 + 4 * layer_height +cool_min_layer_time_fan_speed_max = 20 +infill_before_walls = True +infill_pattern = triangles +layer_height = 0.3 +machine_nozzle_cool_down_speed = 0.9 +material_standby_temperature = 100 +raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) +raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) +switch_extruder_retraction_amount = 30 +switch_extruder_retraction_speeds = 40 +wall_line_width_x = =wall_line_width + diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg new file mode 100644 index 0000000000..74f7f47a4d --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 2 +name = Draft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = draft +material = generic_pla_ultimaker3_AA_0.8 +weight = 0 + +[values] +brim_line_count = =math.ceil(brim_width / skirt_brim_line_width) +cool_fan_speed_max = =cool_fan_speed +cool_min_speed = 2 +gradual_infill_step_height = =3 * layer_height +gradual_infill_steps = 4 +infill_line_width = =round(line_width * 0.535 / 0.75, 2) +infill_sparse_density = 80 +line_width = =machine_nozzle_size * 0.9375 +machine_nozzle_heat_up_speed = 1.6 +material_final_print_temperature = =max(-273.15, material_print_temperature - 15) +material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) +material_print_temperature = =default_material_print_temperature + 10 +material_standby_temperature = 100 +ooze_shield_angle = 60 +raft_acceleration = =acceleration_print +raft_jerk = =jerk_print +raft_margin = 15 +switch_extruder_prime_speed = =switch_extruder_retraction_speeds +top_bottom_thickness = =layer_height * 4 +wall_line_width = =round(line_width * 0.75 / 0.75, 2) +wall_thickness = =wall_line_width_0 + wall_line_width_x + diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg new file mode 100644 index 0000000000..4702d382c7 --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg @@ -0,0 +1,35 @@ +[general] +version = 2 +name = Superdraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = superdraft +material = generic_pla_ultimaker3_AA_0.8 +weight = 1 + +[values] +brim_line_count = =math.ceil(brim_width / skirt_brim_line_width) +cool_fan_speed_max = =cool_fan_speed +cool_min_speed = 2 +gradual_infill_step_height = =3 * layer_height +gradual_infill_steps = 4 +infill_line_width = =round(line_width * 0.535 / 0.75, 2) +infill_sparse_density = 80 +layer_height = 0.4 +line_width = =machine_nozzle_size * 0.9375 +machine_nozzle_heat_up_speed = 1.6 +material_final_print_temperature = =max(-273.15, material_print_temperature - 15) +material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) +material_print_temperature = =default_material_print_temperature + 15 +material_standby_temperature = 100 +ooze_shield_angle = 60 +raft_acceleration = =acceleration_print +raft_jerk = =jerk_print +raft_margin = 15 +switch_extruder_prime_speed = =switch_extruder_retraction_speeds +top_bottom_thickness = =layer_height * 4 +wall_line_width = =round(line_width * 0.75 / 0.75, 2) +wall_thickness = =wall_line_width_0 + wall_line_width_x + diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg new file mode 100644 index 0000000000..174882aa68 --- /dev/null +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg @@ -0,0 +1,35 @@ +[general] +version = 2 +name = Verydraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = verydraft +material = generic_pla_ultimaker3_AA_0.8 +weight = 1 + +[values] +brim_line_count = =math.ceil(brim_width / skirt_brim_line_width) +cool_fan_speed_max = =cool_fan_speed +cool_min_speed = 2 +gradual_infill_step_height = =3 * layer_height +gradual_infill_steps = 4 +infill_line_width = =round(line_width * 0.535 / 0.75, 2) +infill_sparse_density = 80 +layer_height = 0.3 +line_width = =machine_nozzle_size * 0.9375 +machine_nozzle_heat_up_speed = 1.6 +material_final_print_temperature = =max(-273.15, material_print_temperature - 15) +material_initial_print_temperature = =max(-273.15, material_print_temperature - 10) +material_print_temperature = =default_material_print_temperature + 10 +material_standby_temperature = 100 +ooze_shield_angle = 60 +raft_acceleration = =acceleration_print +raft_jerk = =jerk_print +raft_margin = 15 +switch_extruder_prime_speed = =switch_extruder_retraction_speeds +top_bottom_thickness = =layer_height * 4 +wall_line_width = =round(line_width * 0.75 / 0.75, 2) +wall_thickness = =wall_line_width_0 + wall_line_width_x + diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg new file mode 100644 index 0000000000..c02e307b47 --- /dev/null +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg @@ -0,0 +1,14 @@ +[general] +version = 2 +name = Draft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = draft +weight = -2 +material = generic_pva_ultimaker3_BB_0.8 + +[values] +material_print_temperature = =default_material_print_temperature + 5 + diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg new file mode 100644 index 0000000000..84075aa4b9 --- /dev/null +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg @@ -0,0 +1,14 @@ +[general] +version = 2 +name = Superdraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = superdraft +weight = -2 +material = generic_pva_ultimaker3_BB_0.8 + +[values] +layer_height = 0.4 + diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg new file mode 100644 index 0000000000..db10f3d848 --- /dev/null +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg @@ -0,0 +1,14 @@ +[general] +version = 2 +name = Verydraft Print +definition = ultimaker3 + +[metadata] +type = quality +quality_type = verydraft +weight = -2 +material = generic_pva_ultimaker3_BB_0.8 + +[values] +layer_height = 0.3 + diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index acce27b74e..fa4bf2ee92 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -290,9 +290,15 @@ "slider_groove": [0.5, 0.5], "slider_handle": [1.5, 1.5], - "slider_layerview_size": [1.0, 16.0], + "slider_layerview_size": [1.0, 22.0], "slider_layerview_background": [4.0, 0.0], - "slider_layerview_margin": [3.0, 3.0], + "slider_layerview_margin": [1.0, 1.0], + + "layerview_menu_size": [16.5, 21.0], + "layerview_menu_size_compatibility": [22, 23.0], + "layerview_legend_size": [1.0, 1.0], + "layerview_row": [11.0, 1.5], + "layerview_row_spacing": [0.0, 0.5], "checkbox": [2.0, 2.0], diff --git a/resources/variants/cartesio_0.25.inst.cfg b/resources/variants/cartesio_0.25.inst.cfg index 1e46c1eb0e..1aa490beff 100644 --- a/resources/variants/cartesio_0.25.inst.cfg +++ b/resources/variants/cartesio_0.25.inst.cfg @@ -45,19 +45,19 @@ speed_travel_layer_0 = =round(speed_travel) speed_support_interface = =round(speed_topbottom) retraction_combing = off -retraction_hop_enabled = true +retraction_hop_enabled = True retraction_hop = 1 support_z_distance = 0 support_xy_distance = 0.5 support_join_distance = 10 -support_interface_enable = true +support_interface_enable = True adhesion_type = skirt skirt_gap = 0.5 skirt_brim_minimal_length = 50 -coasting_enable = true +coasting_enable = True coasting_volume = 0.1 coasting_min_volume = 0.17 coasting_speed = 90 diff --git a/resources/variants/cartesio_0.4.inst.cfg b/resources/variants/cartesio_0.4.inst.cfg index c3ba238726..3a818469b9 100644 --- a/resources/variants/cartesio_0.4.inst.cfg +++ b/resources/variants/cartesio_0.4.inst.cfg @@ -45,19 +45,19 @@ speed_travel_layer_0 = =round(speed_travel) speed_support_interface = =round(speed_topbottom) retraction_combing = off -retraction_hop_enabled = true +retraction_hop_enabled = True retraction_hop = 1 support_z_distance = 0 support_xy_distance = 0.5 support_join_distance = 10 -support_interface_enable = true +support_interface_enable = True adhesion_type = skirt skirt_gap = 0.5 skirt_brim_minimal_length = 50 -coasting_enable = true +coasting_enable = True coasting_volume = 0.1 coasting_min_volume = 0.17 coasting_speed = 90 diff --git a/resources/variants/cartesio_0.8.inst.cfg b/resources/variants/cartesio_0.8.inst.cfg index 851a36256b..3f6502667c 100644 --- a/resources/variants/cartesio_0.8.inst.cfg +++ b/resources/variants/cartesio_0.8.inst.cfg @@ -46,19 +46,19 @@ speed_travel_layer_0 = =round(speed_travel) speed_support_interface = =round(speed_topbottom) retraction_combing = off -retraction_hop_enabled = true +retraction_hop_enabled = True retraction_hop = 1 support_z_distance = 0 support_xy_distance = 0.5 support_join_distance = 10 -support_interface_enable = true +support_interface_enable = True adhesion_type = skirt skirt_gap = 0.5 skirt_brim_minimal_length = 50 -coasting_enable = true +coasting_enable = True coasting_volume = 0.1 coasting_min_volume = 0.17 coasting_speed = 90 diff --git a/resources/variants/ultimaker3_aa0.8.inst.cfg b/resources/variants/ultimaker3_aa0.8.inst.cfg new file mode 100644 index 0000000000..c73e22db20 --- /dev/null +++ b/resources/variants/ultimaker3_aa0.8.inst.cfg @@ -0,0 +1,64 @@ +[general] +name = AA 0.8 +version = 2 +definition = ultimaker3 + +[metadata] +author = ultimaker +type = variant + +[values] +acceleration_enabled = True +acceleration_print = 4000 +brim_line_count = 7 +brim_width = 7 +cool_fan_full_at_height = =layer_height_0 + 2 * layer_height +cool_fan_speed = 100 +cool_fan_speed_max = 100 +default_material_print_temperature = 200 +infill_before_walls = False +infill_overlap = 0 +infill_pattern = cubic +infill_wipe_dist = 0 +jerk_enabled = True +jerk_print = 25 +jerk_topbottom = =math.ceil(jerk_print * 25 / 25) +jerk_wall = =math.ceil(jerk_print * 25 / 25) +jerk_wall_0 = =math.ceil(jerk_wall * 25 / 25) +layer_height = 0.2 +machine_min_cool_heat_time_window = 15 +machine_nozzle_cool_down_speed = 0.75 +machine_nozzle_size = 0.8 +material_final_print_temperature = =material_print_temperature - 10 +material_initial_print_temperature = =material_print_temperature - 5 +material_standby_temperature = 100 +multiple_mesh_overlap = 0 +ooze_shield_angle = 40 +raft_acceleration = =acceleration_layer_0 +raft_margin = 10 +retract_at_layer_change = True +retraction_count_max = 25 +retraction_extrusion_window = 1 +retraction_hop = 2 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +skin_overlap = 5 +speed_equalize_flow_enabled = True +speed_layer_0 = 20 +speed_print = 35 +speed_topbottom = =math.ceil(speed_print * 25 / 35) +speed_wall_0 = =math.ceil(speed_wall * 25 / 30) +support_angle = 70 +support_bottom_distance = =support_z_distance / 2 +support_line_width = =line_width * 0.75 +support_top_distance = =support_z_distance +support_xy_distance = =wall_line_width_0 * 1.5 +support_z_distance = =layer_height * 2 +switch_extruder_prime_speed = 30 +switch_extruder_retraction_amount = 16.5 +top_bottom_thickness = 1.4 +travel_avoid_distance = 3 +wall_0_inset = 0 +wall_line_width_x = =round(wall_line_width * 0.625 / 0.75, 2) +wall_thickness = 2 + diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg new file mode 100644 index 0000000000..a88c3ef6b7 --- /dev/null +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -0,0 +1,74 @@ +[general] +name = BB 0.8 +version = 2 +definition = ultimaker3 + +[metadata] +author = ultimaker +type = variant + +[values] +acceleration_enabled = True +acceleration_print = 4000 +acceleration_support_interface = =math.ceil(acceleration_topbottom * 100 / 500) +brim_width = 3 +cool_fan_speed = 50 +cool_min_speed = 5 +infill_line_width = =round(line_width * 0.8 / 0.7, 2) +infill_overlap = 0 +infill_pattern = triangles +infill_wipe_dist = 0 +jerk_enabled = True +jerk_print = 25 +jerk_support_interface = =math.ceil(jerk_topbottom * 1 / 5) +layer_height = 0.2 +machine_min_cool_heat_time_window = 15 +machine_nozzle_heat_up_speed = 1.5 +machine_nozzle_size = 0.8 +material_print_temperature = =default_material_print_temperature + 10 +material_standby_temperature = 100 +multiple_mesh_overlap = 0 +raft_acceleration = =acceleration_layer_0 +raft_airgap = 0 +raft_base_speed = 20 +raft_base_thickness = 0.3 +raft_interface_line_spacing = 0.5 +raft_interface_line_width = 0.5 +raft_interface_speed = 20 +raft_interface_thickness = 0.2 +raft_margin = 10 +raft_speed = 25 +raft_surface_layers = 1 +retraction_amount = 4.5 +retraction_count_max = 15 +retraction_hop = 2 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 5 +retraction_prime_speed = 15 +skin_overlap = 5 +speed_layer_0 = 20 +speed_print = 35 +speed_support_interface = =math.ceil(speed_topbottom * 15 / 20) +speed_wall_0 = =math.ceil(speed_wall * 25 / 30) +support_angle = 60 +support_bottom_height = =layer_height * 2 +support_bottom_stair_step_height = =layer_height +support_infill_rate = 25 +support_interface_enable = True +support_interface_height = =layer_height * 5 +support_join_distance = 3 +support_line_width = =round(line_width * 0.4 / 0.35, 2) +support_offset = 1.5 +support_pattern = triangles +support_use_towers = False +support_xy_distance = =wall_line_width_0 / 2 +support_xy_distance_overhang = =wall_line_width_0 / 4 +support_z_distance = 0 +switch_extruder_prime_speed = 15 +switch_extruder_retraction_amount = 12 +top_bottom_thickness = 1 +travel_avoid_distance = 3 +wall_0_inset = 0 +wall_thickness = 1 + diff --git a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg new file mode 100644 index 0000000000..98860889b3 --- /dev/null +++ b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg @@ -0,0 +1,64 @@ +[general] +name = AA 0.8 +version = 2 +definition = ultimaker3_extended + +[metadata] +author = ultimaker +type = variant + +[values] +acceleration_enabled = True +acceleration_print = 4000 +brim_line_count = 7 +brim_width = 7 +cool_fan_full_at_height = =layer_height_0 + 2 * layer_height +cool_fan_speed = 100 +cool_fan_speed_max = 100 +default_material_print_temperature = 200 +infill_before_walls = False +infill_overlap = 0 +infill_pattern = cubic +infill_wipe_dist = 0 +jerk_enabled = True +jerk_print = 25 +jerk_topbottom = =math.ceil(jerk_print * 25 / 25) +jerk_wall = =math.ceil(jerk_print * 25 / 25) +jerk_wall_0 = =math.ceil(jerk_wall * 25 / 25) +layer_height = 0.2 +machine_min_cool_heat_time_window = 15 +machine_nozzle_cool_down_speed = 0.75 +machine_nozzle_size = 0.8 +material_final_print_temperature = =material_print_temperature - 10 +material_initial_print_temperature = =material_print_temperature - 5 +material_standby_temperature = 100 +multiple_mesh_overlap = 0 +ooze_shield_angle = 40 +raft_acceleration = =acceleration_layer_0 +raft_margin = 10 +retract_at_layer_change = True +retraction_count_max = 25 +retraction_extrusion_window = 1 +retraction_hop = 2 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +skin_overlap = 5 +speed_equalize_flow_enabled = True +speed_layer_0 = 20 +speed_print = 35 +speed_topbottom = =math.ceil(speed_print * 25 / 35) +speed_wall_0 = =math.ceil(speed_wall * 25 / 30) +support_angle = 70 +support_bottom_distance = =support_z_distance / 2 +support_line_width = =line_width * 0.75 +support_top_distance = =support_z_distance +support_xy_distance = =wall_line_width_0 * 1.5 +support_z_distance = =layer_height * 2 +switch_extruder_prime_speed = 30 +switch_extruder_retraction_amount = 16.5 +top_bottom_thickness = 1.4 +travel_avoid_distance = 3 +wall_0_inset = 0 +wall_line_width_x = =round(wall_line_width * 0.625 / 0.75, 2) +wall_thickness = 2 + diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg new file mode 100644 index 0000000000..ea12c850ef --- /dev/null +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -0,0 +1,74 @@ +[general] +name = BB 0.8 +version = 2 +definition = ultimaker3_extended + +[metadata] +author = ultimaker +type = variant + +[values] +acceleration_enabled = True +acceleration_print = 4000 +acceleration_support_interface = =math.ceil(acceleration_topbottom * 100 / 500) +brim_width = 3 +cool_fan_speed = 50 +cool_min_speed = 5 +infill_line_width = =round(line_width * 0.8 / 0.7, 2) +infill_overlap = 0 +infill_pattern = triangles +infill_wipe_dist = 0 +jerk_enabled = True +jerk_print = 25 +jerk_support_interface = =math.ceil(jerk_topbottom * 1 / 5) +layer_height = 0.2 +machine_min_cool_heat_time_window = 15 +machine_nozzle_heat_up_speed = 1.5 +machine_nozzle_size = 0.8 +material_print_temperature = =default_material_print_temperature + 10 +material_standby_temperature = 100 +multiple_mesh_overlap = 0 +raft_acceleration = =acceleration_layer_0 +raft_airgap = 0 +raft_base_speed = 20 +raft_base_thickness = 0.3 +raft_interface_line_spacing = 0.5 +raft_interface_line_width = 0.5 +raft_interface_speed = 20 +raft_interface_thickness = 0.2 +raft_margin = 10 +raft_speed = 25 +raft_surface_layers = 1 +retraction_amount = 4.5 +retraction_count_max = 15 +retraction_hop = 2 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 5 +retraction_prime_speed = 15 +skin_overlap = 5 +speed_layer_0 = 20 +speed_print = 35 +speed_support_interface = =math.ceil(speed_topbottom * 15 / 20) +speed_wall_0 = =math.ceil(speed_wall * 25 / 30) +support_angle = 60 +support_bottom_height = =layer_height * 2 +support_bottom_stair_step_height = =layer_height +support_infill_rate = 25 +support_interface_enable = True +support_interface_height = =layer_height * 5 +support_join_distance = 3 +support_line_width = =round(line_width * 0.4 / 0.35, 2) +support_offset = 1.5 +support_pattern = triangles +support_use_towers = False +support_xy_distance = =wall_line_width_0 / 2 +support_xy_distance_overhang = =wall_line_width_0 / 4 +support_z_distance = 0 +switch_extruder_prime_speed = 15 +switch_extruder_retraction_amount = 12 +top_bottom_thickness = 1 +travel_avoid_distance = 3 +wall_0_inset = 0 +wall_thickness = 1 +