mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-21 20:19:32 +08:00
Merge branch 'master' of github.com:Ultimaker/Cura into cura_connect_UI_rework
This commit is contained in:
commit
d46c754616
@ -13,6 +13,7 @@ from cura.Backups.BackupsManager import BackupsManager
|
||||
# api = CuraAPI()
|
||||
# api.backups.createBackup()
|
||||
# api.backups.restoreBackup(my_zip_file, {"cura_release": "3.1"})``
|
||||
|
||||
class Backups:
|
||||
manager = BackupsManager() # Re-used instance of the backups manager.
|
||||
|
||||
|
33
cura/API/Interface/Settings.py
Normal file
33
cura/API/Interface/Settings.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
## The Interface.Settings API provides a version-proof bridge between Cura's
|
||||
# (currently) sidebar UI and plug-ins that hook into it.
|
||||
#
|
||||
# Usage:
|
||||
# ``from cura.API import CuraAPI
|
||||
# api = CuraAPI()
|
||||
# api.interface.settings.getContextMenuItems()
|
||||
# data = {
|
||||
# "name": "My Plugin Action",
|
||||
# "iconName": "my-plugin-icon",
|
||||
# "actions": my_menu_actions,
|
||||
# "menu_item": MyPluginAction(self)
|
||||
# }
|
||||
# api.interface.settings.addContextMenuItem(data)``
|
||||
|
||||
class Settings:
|
||||
# Re-used instance of Cura:
|
||||
application = CuraApplication.getInstance() # type: CuraApplication
|
||||
|
||||
## Add items to the sidebar context menu.
|
||||
# \param menu_item dict containing the menu item to add.
|
||||
def addContextMenuItem(self, menu_item: dict) -> None:
|
||||
self.application.addSidebarCustomMenuItem(menu_item)
|
||||
|
||||
## Get all custom items currently added to the sidebar context menu.
|
||||
# \return List containing all custom context menu items.
|
||||
def getContextMenuItems(self) -> list:
|
||||
return self.application.getSidebarCustomMenuItems()
|
24
cura/API/Interface/__init__.py
Normal file
24
cura/API/Interface/__init__.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.API.Interface.Settings import Settings
|
||||
|
||||
## The Interface class serves as a common root for the specific API
|
||||
# methods for each interface element.
|
||||
#
|
||||
# Usage:
|
||||
# ``from cura.API import CuraAPI
|
||||
# api = CuraAPI()
|
||||
# api.interface.settings.addContextMenuItem()
|
||||
# api.interface.viewport.addOverlay() # Not implemented, just a hypothetical
|
||||
# api.interface.toolbar.getToolButtonCount() # Not implemented, just a hypothetical
|
||||
# # etc.``
|
||||
|
||||
class Interface:
|
||||
|
||||
# For now we use the same API version to be consistent.
|
||||
VERSION = PluginRegistry.APIVersion
|
||||
|
||||
# API methods specific to the settings portion of the UI
|
||||
settings = Settings()
|
@ -2,6 +2,7 @@
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from cura.API.Backups import Backups
|
||||
from cura.API.Interface import Interface
|
||||
|
||||
## The official Cura API that plug-ins can use to interact with Cura.
|
||||
#
|
||||
@ -9,10 +10,14 @@ from cura.API.Backups import Backups
|
||||
# this API provides a version-safe interface with proper deprecation warnings
|
||||
# etc. Usage of any other methods than the ones provided in this API can cause
|
||||
# plug-ins to be unstable.
|
||||
|
||||
class CuraAPI:
|
||||
|
||||
# For now we use the same API version to be consistent.
|
||||
VERSION = PluginRegistry.APIVersion
|
||||
|
||||
# Backups API.
|
||||
# Backups API
|
||||
backups = Backups()
|
||||
|
||||
# Interface API
|
||||
interface = Interface()
|
||||
|
@ -104,6 +104,7 @@ from cura.Settings.UserChangesModel import UserChangesModel
|
||||
from cura.Settings.ExtrudersModel import ExtrudersModel
|
||||
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
||||
from cura.Settings.ContainerManager import ContainerManager
|
||||
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
||||
|
||||
from cura.ObjectsModel import ObjectsModel
|
||||
|
||||
@ -117,11 +118,12 @@ if TYPE_CHECKING:
|
||||
numpy.seterr(all = "ignore")
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode
|
||||
from cura.CuraVersion import CuraVersion, CuraBuildType, CuraDebugMode, CuraSDKVersion
|
||||
except ImportError:
|
||||
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
|
||||
CuraBuildType = ""
|
||||
CuraDebugMode = False
|
||||
CuraSDKVerion = ""
|
||||
|
||||
|
||||
class CuraApplication(QtApplication):
|
||||
@ -226,6 +228,8 @@ class CuraApplication(QtApplication):
|
||||
|
||||
self._need_to_show_user_agreement = True
|
||||
|
||||
self._sidebar_custom_menu_items = [] # type: list # Keeps list of custom menu items for the side bar
|
||||
|
||||
self._plugins_loaded = False
|
||||
|
||||
# Backups
|
||||
@ -627,7 +631,7 @@ class CuraApplication(QtApplication):
|
||||
# Cura has multiple locations where instance containers need to be saved, so we need to handle this differently.
|
||||
def saveSettings(self):
|
||||
if not self.started or not self._save_data_enabled:
|
||||
# Do not do saving during application start or when data should not be safed on quit.
|
||||
# Do not do saving during application start or when data should not be saved on quit.
|
||||
return
|
||||
ContainerRegistry.getInstance().saveDirtyContainers()
|
||||
self.savePreferences()
|
||||
@ -910,6 +914,7 @@ class CuraApplication(QtApplication):
|
||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||
engine.rootContext().setContextProperty("CuraSDKVersion", CuraSDKVersion)
|
||||
|
||||
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
|
||||
|
||||
@ -944,6 +949,7 @@ class CuraApplication(QtApplication):
|
||||
qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator")
|
||||
qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel")
|
||||
qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance)
|
||||
qmlRegisterType(SidebarCustomMenuItemsModel, "Cura", 1, 0, "SidebarCustomMenuItemsModel")
|
||||
|
||||
# As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work.
|
||||
actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")))
|
||||
@ -1731,3 +1737,10 @@ class CuraApplication(QtApplication):
|
||||
@pyqtSlot()
|
||||
def showMoreInformationDialogForAnonymousDataCollection(self):
|
||||
cast(SliceInfo, self._plugin_registry.getPluginObject("SliceInfoPlugin")).showMoreInfoDialog()
|
||||
|
||||
def addSidebarCustomMenuItem(self, menu_item: dict) -> None:
|
||||
self._sidebar_custom_menu_items.append(menu_item)
|
||||
|
||||
def getSidebarCustomMenuItems(self) -> list:
|
||||
return self._sidebar_custom_menu_items
|
||||
|
||||
|
@ -4,8 +4,7 @@
|
||||
from collections import defaultdict, OrderedDict
|
||||
import copy
|
||||
import uuid
|
||||
from typing import Dict, cast
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
from typing import Dict, Optional, TYPE_CHECKING
|
||||
|
||||
from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot
|
||||
|
||||
@ -18,6 +17,7 @@ from UM.Util import parseBool
|
||||
|
||||
from .MaterialNode import MaterialNode
|
||||
from .MaterialGroup import MaterialGroup
|
||||
from .VariantType import VariantType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
@ -47,7 +47,7 @@ class MaterialManager(QObject):
|
||||
|
||||
self._fallback_materials_map = dict() # material_type -> generic material metadata
|
||||
self._material_group_map = dict() # root_material_id -> MaterialGroup
|
||||
self._diameter_machine_variant_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict() # approximate diameter str -> dict(machine_definition_id -> MaterialNode)
|
||||
|
||||
# We're using these two maps to convert between the specific diameter material id and the generic material id
|
||||
# because the generic material ids are used in qualities and definitions, while the specific diameter material is meant
|
||||
@ -187,52 +187,78 @@ class MaterialManager(QObject):
|
||||
self._diameter_material_map[root_material_id] = default_root_material_id
|
||||
|
||||
# Map #4
|
||||
# "machine" -> "variant_name" -> "root material ID" -> specific material InstanceContainer
|
||||
# Construct the "machine" -> "variant" -> "root material ID" -> specific material InstanceContainer
|
||||
self._diameter_machine_variant_material_map = dict()
|
||||
# "machine" -> "nozzle name" -> "buildplate name" -> "root material ID" -> specific material InstanceContainer
|
||||
self._diameter_machine_nozzle_buildplate_material_map = dict()
|
||||
for material_metadata in material_metadatas.values():
|
||||
# We don't store empty material in the lookup tables
|
||||
if material_metadata["id"] == "empty_material":
|
||||
continue
|
||||
|
||||
root_material_id = material_metadata["base_file"]
|
||||
definition = material_metadata["definition"]
|
||||
approximate_diameter = material_metadata["approximate_diameter"]
|
||||
|
||||
if approximate_diameter not in self._diameter_machine_variant_material_map:
|
||||
self._diameter_machine_variant_material_map[approximate_diameter] = {}
|
||||
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[approximate_diameter]
|
||||
if definition not in machine_variant_material_map:
|
||||
machine_variant_material_map[definition] = MaterialNode()
|
||||
|
||||
machine_node = machine_variant_material_map[definition]
|
||||
variant_name = material_metadata.get("variant_name")
|
||||
if not variant_name:
|
||||
# if there is no variant, this material is for the machine, so put its metadata in the machine node.
|
||||
machine_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
else:
|
||||
# this material is variant-specific, so we save it in a variant-specific node under the
|
||||
# machine-specific node
|
||||
|
||||
# Check first if the variant exist in the manager
|
||||
existing_variant = self._application.getVariantManager().getVariantNode(definition, variant_name)
|
||||
if existing_variant is not None:
|
||||
if variant_name not in machine_node.children_map:
|
||||
machine_node.children_map[variant_name] = MaterialNode()
|
||||
|
||||
variant_node = machine_node.children_map[variant_name]
|
||||
if root_material_id in variant_node.material_map: # We shouldn't have duplicated variant-specific materials for the same machine.
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
|
||||
continue
|
||||
variant_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
else:
|
||||
# Add this container id to the wrong containers list in the registry
|
||||
Logger.log("w", "Not adding {id} to the material manager because the variant does not exist.".format(id = material_metadata["id"]))
|
||||
self._container_registry.addWrongContainerId(material_metadata["id"])
|
||||
self.__addMaterialMetadataIntoLookupTree(material_metadata)
|
||||
|
||||
self.materialsUpdated.emit()
|
||||
|
||||
def __addMaterialMetadataIntoLookupTree(self, material_metadata: dict) -> None:
|
||||
material_id = material_metadata["id"]
|
||||
|
||||
# We don't store empty material in the lookup tables
|
||||
if material_id == "empty_material":
|
||||
return
|
||||
|
||||
root_material_id = material_metadata["base_file"]
|
||||
definition = material_metadata["definition"]
|
||||
approximate_diameter = material_metadata["approximate_diameter"]
|
||||
|
||||
if approximate_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
self._diameter_machine_nozzle_buildplate_material_map[approximate_diameter] = {}
|
||||
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[
|
||||
approximate_diameter]
|
||||
if definition not in machine_nozzle_buildplate_material_map:
|
||||
machine_nozzle_buildplate_material_map[definition] = MaterialNode()
|
||||
|
||||
# This is a list of information regarding the intermediate nodes:
|
||||
# nozzle -> buildplate
|
||||
nozzle_name = material_metadata.get("variant_name")
|
||||
buildplate_name = material_metadata.get("buildplate_name")
|
||||
intermediate_node_info_list = [(nozzle_name, VariantType.NOZZLE),
|
||||
(buildplate_name, VariantType.BUILD_PLATE),
|
||||
]
|
||||
|
||||
variant_manager = self._application.getVariantManager()
|
||||
|
||||
machine_node = machine_nozzle_buildplate_material_map[definition]
|
||||
current_node = machine_node
|
||||
current_intermediate_node_info_idx = 0
|
||||
error_message = None # type: Optional[str]
|
||||
while current_intermediate_node_info_idx < len(intermediate_node_info_list):
|
||||
variant_name, variant_type = intermediate_node_info_list[current_intermediate_node_info_idx]
|
||||
if variant_name is not None:
|
||||
# The new material has a specific variant, so it needs to be added to that specific branch in the tree.
|
||||
variant = variant_manager.getVariantNode(definition, variant_name, variant_type)
|
||||
if variant is None:
|
||||
error_message = "Material {id} contains a variant {name} that does not exist.".format(
|
||||
id = material_metadata["id"], name = variant_name)
|
||||
break
|
||||
|
||||
# Update the current node to advance to a more specific branch
|
||||
if variant_name not in current_node.children_map:
|
||||
current_node.children_map[variant_name] = MaterialNode()
|
||||
current_node = current_node.children_map[variant_name]
|
||||
|
||||
current_intermediate_node_info_idx += 1
|
||||
|
||||
if error_message is not None:
|
||||
Logger.log("e", "%s It will not be added into the material lookup tree.", error_message)
|
||||
self._container_registry.addWrongContainerId(material_metadata["id"])
|
||||
return
|
||||
|
||||
# Add the material to the current tree node, which is the deepest (the most specific) branch we can find.
|
||||
# Sanity check: Make sure that there is no duplicated materials.
|
||||
if root_material_id in current_node.material_map:
|
||||
Logger.log("e", "Duplicated material [%s] with root ID [%s]. It has already been added.",
|
||||
material_id, root_material_id)
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(root_material_id)
|
||||
return
|
||||
|
||||
current_node.material_map[root_material_id] = MaterialNode(material_metadata)
|
||||
|
||||
def _updateMaps(self):
|
||||
Logger.log("i", "Updating material lookup data ...")
|
||||
self.initialize()
|
||||
@ -263,45 +289,52 @@ class MaterialManager(QObject):
|
||||
#
|
||||
# Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup.
|
||||
#
|
||||
def getAvailableMaterials(self, machine_definition: "DefinitionContainer", extruder_variant_name: Optional[str],
|
||||
diameter: float) -> Dict[str, MaterialNode]:
|
||||
def getAvailableMaterials(self, machine_definition: "DefinitionContainer", nozzle_name: Optional[str],
|
||||
buildplate_name: Optional[str], diameter: float) -> Dict[str, MaterialNode]:
|
||||
# round the diameter to get the approximate diameter
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_variant_material_map:
|
||||
if rounded_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s])", diameter, rounded_diameter)
|
||||
return dict()
|
||||
|
||||
machine_definition_id = machine_definition.getId()
|
||||
|
||||
# If there are variant materials, get the variant material
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
||||
default_machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
||||
variant_node = None
|
||||
if extruder_variant_name is not None and machine_node is not None:
|
||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
||||
# If there are nozzle-and-or-buildplate materials, get the nozzle-and-or-buildplate material
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||
default_machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||
nozzle_node = None
|
||||
buildplate_node = None
|
||||
if nozzle_name is not None and machine_node is not None:
|
||||
nozzle_node = machine_node.getChildNode(nozzle_name)
|
||||
# Get buildplate node if possible
|
||||
if nozzle_node is not None and buildplate_name is not None:
|
||||
buildplate_node = nozzle_node.getChildNode(buildplate_name)
|
||||
|
||||
nodes_to_check = [variant_node, machine_node, default_machine_node]
|
||||
nodes_to_check = [buildplate_node, nozzle_node, machine_node, default_machine_node]
|
||||
|
||||
# Fallback mechanism of finding materials:
|
||||
# 1. variant-specific material
|
||||
# 2. machine-specific material
|
||||
# 3. generic material (for fdmprinter)
|
||||
# 1. buildplate-specific material
|
||||
# 2. nozzle-specific material
|
||||
# 3. machine-specific material
|
||||
# 4. generic material (for fdmprinter)
|
||||
machine_exclude_materials = machine_definition.getMetaDataEntry("exclude_materials", [])
|
||||
|
||||
material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
|
||||
for node in nodes_to_check:
|
||||
if node is not None:
|
||||
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
||||
# Do not exclude other materials that are of the same type.
|
||||
for material_id, node in node.material_map.items():
|
||||
if material_id in machine_exclude_materials:
|
||||
Logger.log("d", "Exclude material [%s] for machine [%s]",
|
||||
material_id, machine_definition.getId())
|
||||
continue
|
||||
material_id_metadata_dict = dict() # type: Dict[str, MaterialNode]
|
||||
for current_node in nodes_to_check:
|
||||
if current_node is None:
|
||||
continue
|
||||
|
||||
if material_id not in material_id_metadata_dict:
|
||||
material_id_metadata_dict[material_id] = node
|
||||
# Only exclude the materials that are explicitly specified in the "exclude_materials" field.
|
||||
# Do not exclude other materials that are of the same type.
|
||||
for material_id, node in current_node.material_map.items():
|
||||
if material_id in machine_exclude_materials:
|
||||
Logger.log("d", "Exclude material [%s] for machine [%s]",
|
||||
material_id, machine_definition.getId())
|
||||
continue
|
||||
|
||||
if material_id not in material_id_metadata_dict:
|
||||
material_id_metadata_dict[material_id] = node
|
||||
|
||||
return material_id_metadata_dict
|
||||
|
||||
@ -310,13 +343,14 @@ class MaterialManager(QObject):
|
||||
#
|
||||
def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack",
|
||||
extruder_stack: "ExtruderStack") -> Optional[dict]:
|
||||
variant_name = None
|
||||
buildplate_name = machine.getBuildplateName()
|
||||
nozzle_name = None
|
||||
if extruder_stack.variant.getId() != "empty_variant":
|
||||
variant_name = extruder_stack.variant.getName()
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
diameter = extruder_stack.approximateMaterialDiameter
|
||||
|
||||
# Fetch the available materials (ContainerNode) for the current active machine and extruder setup.
|
||||
return self.getAvailableMaterials(machine.definition, variant_name, diameter)
|
||||
return self.getAvailableMaterials(machine.definition, nozzle_name, buildplate_name, diameter)
|
||||
|
||||
#
|
||||
# Gets MaterialNode for the given extruder and machine with the given material name.
|
||||
@ -324,32 +358,36 @@ class MaterialManager(QObject):
|
||||
# 1. the given machine doesn't have materials;
|
||||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNode(self, machine_definition_id: str, extruder_variant_name: Optional[str],
|
||||
diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||
def getMaterialNode(self, machine_definition_id: str, nozzle_name: Optional[str],
|
||||
buildplate_name: Optional[str], diameter: float, root_material_id: str) -> Optional["InstanceContainer"]:
|
||||
# round the diameter to get the approximate diameter
|
||||
rounded_diameter = str(round(diameter))
|
||||
if rounded_diameter not in self._diameter_machine_variant_material_map:
|
||||
if rounded_diameter not in self._diameter_machine_nozzle_buildplate_material_map:
|
||||
Logger.log("i", "Cannot find materials with diameter [%s] (rounded to [%s]) for root material id [%s]",
|
||||
diameter, rounded_diameter, root_material_id)
|
||||
return None
|
||||
|
||||
# If there are variant materials, get the variant material
|
||||
machine_variant_material_map = self._diameter_machine_variant_material_map[rounded_diameter]
|
||||
machine_node = machine_variant_material_map.get(machine_definition_id)
|
||||
variant_node = None
|
||||
# If there are nozzle materials, get the nozzle-specific material
|
||||
machine_nozzle_buildplate_material_map = self._diameter_machine_nozzle_buildplate_material_map[rounded_diameter]
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(machine_definition_id)
|
||||
nozzle_node = None
|
||||
buildplate_node = None
|
||||
|
||||
# Fallback for "fdmprinter" if the machine-specific materials cannot be found
|
||||
if machine_node is None:
|
||||
machine_node = machine_variant_material_map.get(self._default_machine_definition_id)
|
||||
if machine_node is not None and extruder_variant_name is not None:
|
||||
variant_node = machine_node.getChildNode(extruder_variant_name)
|
||||
machine_node = machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)
|
||||
if machine_node is not None and nozzle_name is not None:
|
||||
nozzle_node = machine_node.getChildNode(nozzle_name)
|
||||
if nozzle_node is not None and buildplate_name is not None:
|
||||
buildplate_node = nozzle_node.getChildNode(buildplate_name)
|
||||
|
||||
# Fallback mechanism of finding materials:
|
||||
# 1. variant-specific material
|
||||
# 2. machine-specific material
|
||||
# 3. generic material (for fdmprinter)
|
||||
nodes_to_check = [variant_node, machine_node,
|
||||
machine_variant_material_map.get(self._default_machine_definition_id)]
|
||||
# 1. buildplate-specific material
|
||||
# 2. nozzle-specific material
|
||||
# 3. machine-specific material
|
||||
# 4. generic material (for fdmprinter)
|
||||
nodes_to_check = [buildplate_node, nozzle_node, machine_node,
|
||||
machine_nozzle_buildplate_material_map.get(self._default_machine_definition_id)]
|
||||
|
||||
material_node = None
|
||||
for node in nodes_to_check:
|
||||
@ -366,7 +404,8 @@ class MaterialManager(QObject):
|
||||
# 1. the given machine doesn't have materials;
|
||||
# 2. cannot find any material InstanceContainers with the given settings.
|
||||
#
|
||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, extruder_variant_name: str, material_guid: str) -> Optional["MaterialNode"]:
|
||||
def getMaterialNodeByType(self, global_stack: "GlobalStack", position: str, nozzle_name: str,
|
||||
buildplate_name: Optional[str], material_guid: str) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
machine_definition = global_stack.definition
|
||||
extruder_definition = global_stack.extruders[position].definition
|
||||
@ -385,7 +424,7 @@ class MaterialManager(QObject):
|
||||
Logger.log("i", "Cannot find materials with guid [%s] ", material_guid)
|
||||
return None
|
||||
|
||||
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
|
||||
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
@ -413,13 +452,17 @@ class MaterialManager(QObject):
|
||||
else:
|
||||
return None
|
||||
|
||||
## Get default material for given global stack, extruder position and extruder variant name
|
||||
## Get default material for given global stack, extruder position and extruder nozzle name
|
||||
# you can provide the extruder_definition and then the position is ignored (useful when building up global stack in CuraStackBuilder)
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", position: str, extruder_variant_name: Optional[str], extruder_definition: Optional["DefinitionContainer"] = None) -> Optional["MaterialNode"]:
|
||||
def getDefaultMaterial(self, global_stack: "GlobalStack", position: str, nozzle_name: Optional[str],
|
||||
extruder_definition: Optional["DefinitionContainer"] = None) -> Optional["MaterialNode"]:
|
||||
node = None
|
||||
|
||||
buildplate_name = global_stack.getBuildplateName()
|
||||
machine_definition = global_stack.definition
|
||||
if extruder_definition is None:
|
||||
extruder_definition = global_stack.extruders[position].definition
|
||||
|
||||
if extruder_definition and parseBool(global_stack.getMetaDataEntry("has_materials", False)):
|
||||
# At this point the extruder_definition is not None
|
||||
material_diameter = extruder_definition.getProperty("material_diameter", "value")
|
||||
@ -428,7 +471,7 @@ class MaterialManager(QObject):
|
||||
approximate_material_diameter = str(round(material_diameter))
|
||||
root_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
|
||||
node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name,
|
||||
node = self.getMaterialNode(machine_definition.getId(), nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
return node
|
||||
|
||||
@ -515,8 +558,8 @@ class MaterialManager(QObject):
|
||||
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
|
||||
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
|
||||
if container_to_copy.getMetaDataEntry("variant_name"):
|
||||
variant_name = container_to_copy.getMetaDataEntry("variant_name")
|
||||
new_id += "_" + variant_name.replace(" ", "_")
|
||||
nozzle_name = container_to_copy.getMetaDataEntry("variant_name")
|
||||
new_id += "_" + nozzle_name.replace(" ", "_")
|
||||
|
||||
new_container = copy.deepcopy(container_to_copy)
|
||||
new_container.getMetaData()["id"] = new_id
|
||||
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
|
||||
class BuildPlateModel(ListModel):
|
||||
|
@ -8,6 +8,8 @@ from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
|
||||
class NozzleModel(ListModel):
|
||||
IdRole = Qt.UserRole + 1
|
||||
@ -43,7 +45,6 @@ class NozzleModel(ListModel):
|
||||
self.setItems([])
|
||||
return
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_node_dict = self._variant_manager.getVariantNodes(global_stack, VariantType.NOZZLE)
|
||||
if not variant_node_dict:
|
||||
self.setItems([])
|
||||
|
@ -45,7 +45,7 @@ class QualityManager(QObject):
|
||||
self._empty_quality_container = self._application.empty_quality_container
|
||||
self._empty_quality_changes_container = self._application.empty_quality_changes_container
|
||||
|
||||
self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||
|
||||
self._default_machine_definition_id = "fdmprinter"
|
||||
@ -64,10 +64,10 @@ class QualityManager(QObject):
|
||||
|
||||
def initialize(self):
|
||||
# Initialize the lookup tree for quality profiles with following structure:
|
||||
# <machine> -> <variant> -> <material>
|
||||
# -> <material>
|
||||
# <machine> -> <nozzle> -> <buildplate> -> <material>
|
||||
# <machine> -> <material>
|
||||
|
||||
self._machine_variant_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict = {} # for quality lookup
|
||||
self._machine_quality_type_to_quality_changes_dict = {} # for quality_changes lookup
|
||||
|
||||
quality_metadata_list = self._container_registry.findContainersMetadata(type = "quality")
|
||||
@ -79,53 +79,41 @@ class QualityManager(QObject):
|
||||
quality_type = metadata["quality_type"]
|
||||
|
||||
root_material_id = metadata.get("material")
|
||||
variant_name = metadata.get("variant")
|
||||
nozzle_name = metadata.get("variant")
|
||||
buildplate_name = metadata.get("buildplate")
|
||||
is_global_quality = metadata.get("global_quality", False)
|
||||
is_global_quality = is_global_quality or (root_material_id is None and variant_name is None)
|
||||
is_global_quality = is_global_quality or (root_material_id is None and nozzle_name is None and buildplate_name is None)
|
||||
|
||||
# Sanity check: material+variant and is_global_quality cannot be present at the same time
|
||||
if is_global_quality and (root_material_id or variant_name):
|
||||
if is_global_quality and (root_material_id or nozzle_name):
|
||||
ConfigurationErrorMessage.getInstance().addFaultyContainers(metadata["id"])
|
||||
continue
|
||||
|
||||
if definition_id not in self._machine_variant_material_quality_type_to_quality_dict:
|
||||
self._machine_variant_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
machine_node = cast(QualityNode, self._machine_variant_material_quality_type_to_quality_dict[definition_id])
|
||||
if definition_id not in self._machine_nozzle_buildplate_material_quality_type_to_quality_dict:
|
||||
self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id] = QualityNode()
|
||||
machine_node = cast(QualityNode, self._machine_nozzle_buildplate_material_quality_type_to_quality_dict[definition_id])
|
||||
|
||||
if is_global_quality:
|
||||
# For global qualities, save data in the machine node
|
||||
machine_node.addQualityMetadata(quality_type, metadata)
|
||||
continue
|
||||
|
||||
if variant_name is not None:
|
||||
# If variant_name is specified in the quality/quality_changes profile, check if material is specified,
|
||||
# too.
|
||||
if variant_name not in machine_node.children_map:
|
||||
machine_node.children_map[variant_name] = QualityNode()
|
||||
variant_node = cast(QualityNode, machine_node.children_map[variant_name])
|
||||
current_node = machine_node
|
||||
intermediate_node_info_list = [nozzle_name, buildplate_name, root_material_id]
|
||||
current_intermediate_node_info_idx = 0
|
||||
|
||||
if root_material_id is None:
|
||||
# If only variant_name is specified but material is not, add the quality/quality_changes metadata
|
||||
# into the current variant node.
|
||||
variant_node.addQualityMetadata(quality_type, metadata)
|
||||
else:
|
||||
# If only variant_name and material are both specified, go one level deeper: create a material node
|
||||
# under the current variant node, and then add the quality/quality_changes metadata into the
|
||||
# material node.
|
||||
if root_material_id not in variant_node.children_map:
|
||||
variant_node.children_map[root_material_id] = QualityNode()
|
||||
material_node = cast(QualityNode, variant_node.children_map[root_material_id])
|
||||
while current_intermediate_node_info_idx < len(intermediate_node_info_list):
|
||||
node_name = intermediate_node_info_list[current_intermediate_node_info_idx]
|
||||
if node_name is not None:
|
||||
# There is specific information, update the current node to go deeper so we can add this quality
|
||||
# at the most specific branch in the lookup tree.
|
||||
if node_name not in current_node.children_map:
|
||||
current_node.children_map[node_name] = QualityNode()
|
||||
current_node = cast(QualityNode, current_node.children_map[node_name])
|
||||
|
||||
material_node.addQualityMetadata(quality_type, metadata)
|
||||
current_intermediate_node_info_idx += 1
|
||||
|
||||
else:
|
||||
# If variant_name is not specified, check if material is specified.
|
||||
if root_material_id is not None:
|
||||
if root_material_id not in machine_node.children_map:
|
||||
machine_node.children_map[root_material_id] = QualityNode()
|
||||
material_node = cast(QualityNode, machine_node.children_map[root_material_id])
|
||||
|
||||
material_node.addQualityMetadata(quality_type, metadata)
|
||||
current_node.addQualityMetadata(quality_type, metadata)
|
||||
|
||||
# Initialize the lookup tree for quality_changes profiles with following structure:
|
||||
# <machine> -> <quality_type> -> <name>
|
||||
@ -217,8 +205,8 @@ class QualityManager(QObject):
|
||||
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||
# (1) the machine-specific node
|
||||
# (2) the generic node
|
||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
|
||||
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(self._default_machine_definition_id)
|
||||
nodes_to_check = [machine_node, default_machine_node]
|
||||
|
||||
# Iterate over all quality_types in the machine node
|
||||
@ -238,16 +226,19 @@ class QualityManager(QObject):
|
||||
quality_group_dict[quality_type] = quality_group
|
||||
break
|
||||
|
||||
buildplate_name = machine.getBuildplateName()
|
||||
|
||||
# Iterate over all extruders to find quality containers for each extruder
|
||||
for position, extruder in machine.extruders.items():
|
||||
variant_name = None
|
||||
nozzle_name = None
|
||||
if extruder.variant.getId() != "empty_variant":
|
||||
variant_name = extruder.variant.getName()
|
||||
nozzle_name = extruder.variant.getName()
|
||||
|
||||
# This is a list of root material IDs to use for searching for suitable quality profiles.
|
||||
# The root material IDs in this list are in prioritized order.
|
||||
root_material_id_list = []
|
||||
has_material = False # flag indicating whether this extruder has a material assigned
|
||||
root_material_id = None
|
||||
if extruder.material.getId() != "empty_material":
|
||||
has_material = True
|
||||
root_material_id = extruder.material.getMetaDataEntry("base_file")
|
||||
@ -264,34 +255,39 @@ class QualityManager(QObject):
|
||||
# Here we construct a list of nodes we want to look for qualities with the highest priority first.
|
||||
# The use case is that, when we look for qualities for a machine, we first want to search in the following
|
||||
# order:
|
||||
# 1. machine-variant-and-material-specific qualities if exist
|
||||
# 2. machine-variant-specific qualities if exist
|
||||
# 3. machine-material-specific qualities if exist
|
||||
# 4. machine-specific qualities if exist
|
||||
# 5. generic qualities if exist
|
||||
# 1. machine-nozzle-buildplate-and-material-specific qualities if exist
|
||||
# 2. machine-nozzle-and-material-specific qualities if exist
|
||||
# 3. machine-nozzle-specific qualities if exist
|
||||
# 4. machine-material-specific qualities if exist
|
||||
# 5. machine-specific qualities if exist
|
||||
# 6. generic qualities if exist
|
||||
# Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into
|
||||
# the list with priorities as the order. Later, we just need to loop over each node in this list and fetch
|
||||
# qualities from there.
|
||||
node_info_list_0 = [nozzle_name, buildplate_name, root_material_id]
|
||||
nodes_to_check = []
|
||||
|
||||
if variant_name:
|
||||
# In this case, we have both a specific variant and a specific material
|
||||
variant_node = machine_node.getChildNode(variant_name)
|
||||
if variant_node and has_material:
|
||||
for root_material_id in root_material_id_list:
|
||||
material_node = variant_node.getChildNode(root_material_id)
|
||||
if material_node:
|
||||
nodes_to_check.append(material_node)
|
||||
break
|
||||
nodes_to_check.append(variant_node)
|
||||
# This function tries to recursively find the deepest (the most specific) branch and add those nodes to
|
||||
# the search list in the order described above. So, by iterating over that search node list, we first look
|
||||
# in the more specific branches and then the less specific (generic) ones.
|
||||
def addNodesToCheck(node, nodes_to_check_list, node_info_list, node_info_idx):
|
||||
if node_info_idx < len(node_info_list):
|
||||
node_name = node_info_list[node_info_idx]
|
||||
if node_name is not None:
|
||||
current_node = node.getChildNode(node_name)
|
||||
if current_node is not None and has_material:
|
||||
addNodesToCheck(current_node, nodes_to_check_list, node_info_list, node_info_idx + 1)
|
||||
|
||||
# In this case, we only have a specific material but NOT a variant
|
||||
if has_material:
|
||||
for root_material_id in root_material_id_list:
|
||||
material_node = machine_node.getChildNode(root_material_id)
|
||||
if material_node:
|
||||
nodes_to_check.append(material_node)
|
||||
break
|
||||
if has_material:
|
||||
for rmid in root_material_id_list:
|
||||
material_node = node.getChildNode(rmid)
|
||||
if material_node:
|
||||
nodes_to_check_list.append(material_node)
|
||||
break
|
||||
|
||||
nodes_to_check_list.append(node)
|
||||
|
||||
addNodesToCheck(machine_node, nodes_to_check, node_info_list_0, 0)
|
||||
|
||||
nodes_to_check += [machine_node, default_machine_node]
|
||||
for node in nodes_to_check:
|
||||
@ -309,8 +305,8 @@ class QualityManager(QObject):
|
||||
quality_group_dict[quality_type] = quality_group
|
||||
|
||||
quality_group = quality_group_dict[quality_type]
|
||||
quality_group.nodes_for_extruders[position] = quality_node
|
||||
break
|
||||
if position not in quality_group.nodes_for_extruders:
|
||||
quality_group.nodes_for_extruders[position] = quality_node
|
||||
|
||||
# Update availabilities for each quality group
|
||||
self._updateQualityGroupsAvailability(machine, quality_group_dict.values())
|
||||
@ -323,8 +319,8 @@ class QualityManager(QObject):
|
||||
# To find the quality container for the GlobalStack, check in the following fall-back manner:
|
||||
# (1) the machine-specific node
|
||||
# (2) the generic node
|
||||
machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_variant_material_quality_type_to_quality_dict.get(
|
||||
machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(machine_definition_id)
|
||||
default_machine_node = self._machine_nozzle_buildplate_material_quality_type_to_quality_dict.get(
|
||||
self._default_machine_definition_id)
|
||||
nodes_to_check = [machine_node, default_machine_node]
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from enum import Enum
|
||||
from collections import OrderedDict
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
@ -11,20 +10,13 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Util import parseBool
|
||||
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
from cura.Machines.VariantType import VariantType, ALL_VARIANT_TYPES
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
|
||||
class VariantType(Enum):
|
||||
BUILD_PLATE = "buildplate"
|
||||
NOZZLE = "nozzle"
|
||||
|
||||
|
||||
ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
|
||||
|
||||
#
|
||||
# VariantManager is THE place to look for a specific variant. It maintains two variant lookup tables with the following
|
||||
# structure:
|
||||
|
15
cura/Machines/VariantType.py
Normal file
15
cura/Machines/VariantType.py
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class VariantType(Enum):
|
||||
BUILD_PLATE = "buildplate"
|
||||
NOZZLE = "nozzle"
|
||||
|
||||
|
||||
ALL_VARIANT_TYPES = (VariantType.BUILD_PLATE, VariantType.NOZZLE)
|
||||
|
||||
|
||||
__all__ = ["VariantType", "ALL_VARIANT_TYPES"]
|
@ -1,112 +1,147 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Scene.Iterator import Iterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from functools import cmp_to_key
|
||||
from UM.Application import Application
|
||||
import sys
|
||||
|
||||
from shapely import affinity
|
||||
from shapely.geometry import Polygon
|
||||
|
||||
from UM.Scene.Iterator.Iterator import Iterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
|
||||
|
||||
# Iterator that determines the object print order when one-at a time mode is enabled.
|
||||
#
|
||||
# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can
|
||||
# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration:
|
||||
#
|
||||
# +--------------------------------+
|
||||
# | |
|
||||
# | |
|
||||
# | | - Rectangle represents the complete print head including fans, etc.
|
||||
# | X X | y - X's are the nozzles
|
||||
# | (1) (2) | ^
|
||||
# | | |
|
||||
# +--------------------------------+ +--> x
|
||||
#
|
||||
# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the
|
||||
# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print
|
||||
# head will not collide into an object on its top-right side, which is a very large unused area. Following the same
|
||||
# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side.
|
||||
#
|
||||
# This iterator determines the print order following the rules above.
|
||||
#
|
||||
class OneAtATimeIterator(Iterator):
|
||||
|
||||
## Iterator that returns a list of nodes in the order that they need to be printed
|
||||
# If there is no solution an empty list is returned.
|
||||
# Take note that the list of nodes can have children (that may or may not contain mesh data)
|
||||
class OneAtATimeIterator(Iterator.Iterator):
|
||||
def __init__(self, scene_node):
|
||||
super().__init__(scene_node) # Call super to make multiple inheritence work.
|
||||
self._hit_map = [[]]
|
||||
from cura.CuraApplication import CuraApplication
|
||||
self._global_stack = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
self._original_node_list = []
|
||||
|
||||
|
||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
||||
|
||||
def getMachineNearestCornerToExtruder(self, global_stack):
|
||||
head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates()
|
||||
|
||||
used_extruder = None
|
||||
for extruder in global_stack.extruders.values():
|
||||
if extruder.isEnabled:
|
||||
used_extruder = extruder
|
||||
break
|
||||
|
||||
extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"),
|
||||
used_extruder.getProperty("machine_nozzle_offset_y", "value")]
|
||||
|
||||
# find the corner that's closest to the origin
|
||||
min_distance2 = sys.maxsize
|
||||
min_coord = None
|
||||
for coord in head_and_fans_coordinates:
|
||||
x = coord[0] - extruder_offsets[0]
|
||||
y = coord[1] - extruder_offsets[1]
|
||||
|
||||
distance2 = x**2 + y**2
|
||||
if distance2 <= min_distance2:
|
||||
min_distance2 = distance2
|
||||
min_coord = coord
|
||||
|
||||
return min_coord
|
||||
|
||||
def _checkForCollisions(self) -> bool:
|
||||
all_nodes = []
|
||||
for node in self._scene_node.getChildren():
|
||||
if not issubclass(type(node), SceneNode):
|
||||
continue
|
||||
convex_hull = node.callDecoration("getConvexHullHead")
|
||||
if not convex_hull:
|
||||
continue
|
||||
|
||||
bounding_box = node.getBoundingBox()
|
||||
from UM.Math.Polygon import Polygon
|
||||
bounding_box_polygon = Polygon([[bounding_box.left, bounding_box.front],
|
||||
[bounding_box.left, bounding_box.back],
|
||||
[bounding_box.right, bounding_box.back],
|
||||
[bounding_box.right, bounding_box.front]])
|
||||
|
||||
all_nodes.append({"node": node,
|
||||
"bounding_box": bounding_box_polygon,
|
||||
"convex_hull": convex_hull})
|
||||
|
||||
has_collisions = False
|
||||
for i, node_dict in enumerate(all_nodes):
|
||||
for j, other_node_dict in enumerate(all_nodes):
|
||||
if i == j:
|
||||
continue
|
||||
if node_dict["bounding_box"].intersectsPolygon(other_node_dict["convex_hull"]):
|
||||
has_collisions = True
|
||||
break
|
||||
|
||||
if has_collisions:
|
||||
break
|
||||
|
||||
return has_collisions
|
||||
|
||||
def _fillStack(self):
|
||||
min_coord = self.getMachineNearestCornerToExtruder(self._global_stack)
|
||||
transform_x = -int(round(min_coord[0] / abs(min_coord[0])))
|
||||
transform_y = -int(round(min_coord[1] / abs(min_coord[1])))
|
||||
|
||||
machine_size = [self._global_stack.getProperty("machine_width", "value"),
|
||||
self._global_stack.getProperty("machine_depth", "value")]
|
||||
|
||||
def flip_x(polygon):
|
||||
tm2 = [-1, 0, 0, 1, 0, 0]
|
||||
return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2)
|
||||
|
||||
def flip_y(polygon):
|
||||
tm2 = [1, 0, 0, -1, 0, 0]
|
||||
return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2)
|
||||
|
||||
if self._checkForCollisions():
|
||||
self._node_stack = []
|
||||
return
|
||||
|
||||
node_list = []
|
||||
for node in self._scene_node.getChildren():
|
||||
if not issubclass(type(node), SceneNode):
|
||||
continue
|
||||
|
||||
if node.callDecoration("getConvexHull"):
|
||||
node_list.append(node)
|
||||
convex_hull = node.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
xmin = min(x for x, _ in convex_hull._points)
|
||||
xmax = max(x for x, _ in convex_hull._points)
|
||||
ymin = min(y for _, y in convex_hull._points)
|
||||
ymax = max(y for _, y in convex_hull._points)
|
||||
|
||||
convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax)
|
||||
if transform_x < 0:
|
||||
convex_hull_polygon = flip_x(convex_hull_polygon)
|
||||
if transform_y < 0:
|
||||
convex_hull_polygon = flip_y(convex_hull_polygon)
|
||||
|
||||
if len(node_list) < 2:
|
||||
self._node_stack = node_list[:]
|
||||
return
|
||||
node_list.append({"node": node,
|
||||
"min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]],
|
||||
})
|
||||
|
||||
# Copy the list
|
||||
self._original_node_list = node_list[:]
|
||||
|
||||
## Initialise the hit map (pre-compute all hits between all objects)
|
||||
self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list]
|
||||
|
||||
# Check if we have to files that block eachother. If this is the case, there is no solution!
|
||||
for a in range(0,len(node_list)):
|
||||
for b in range(0,len(node_list)):
|
||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return
|
||||
|
||||
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||
|
||||
todo_node_list = [_ObjectOrder([], node_list)]
|
||||
while len(todo_node_list) > 0:
|
||||
current = todo_node_list.pop()
|
||||
for node in current.todo:
|
||||
# Check if the object can be placed with what we have and still allows for a solution in the future
|
||||
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
|
||||
# We found a possible result. Create new todo & order list.
|
||||
new_todo_list = current.todo[:]
|
||||
new_todo_list.remove(node)
|
||||
new_order = current.order[:] + [node]
|
||||
if len(new_todo_list) == 0:
|
||||
# We have no more nodes to check, so quit looking.
|
||||
todo_node_list = None
|
||||
self._node_stack = new_order
|
||||
|
||||
return
|
||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||
self._node_stack = [] #No result found!
|
||||
|
||||
|
||||
# Check if first object can be printed before the provided list (using the hit map)
|
||||
def _checkHitMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[node_index][other_node_index]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkBlockMultiple(self, node, other_nodes):
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
|
||||
return True
|
||||
return False
|
||||
|
||||
## Calculate score simply sums the number of other objects it 'blocks'
|
||||
def _calculateScore(self, a, b):
|
||||
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||
return score_a - score_b
|
||||
|
||||
# Checks if A can be printed before B
|
||||
def _checkHit(self, a, b):
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull"))
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
## Internal object used to keep track of a possible order in which to print objects.
|
||||
class _ObjectOrder():
|
||||
def __init__(self, order, todo):
|
||||
"""
|
||||
:param order: List of indexes in which to print objects, ordered by printing order.
|
||||
:param todo: List of indexes which are not yet inserted into the order list.
|
||||
"""
|
||||
self.order = order
|
||||
self.todo = todo
|
||||
node_list = sorted(node_list, key = lambda d: d["min_coord"])
|
||||
|
||||
self._node_stack = [d["node"] for d in node_list]
|
||||
|
@ -229,7 +229,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
||||
return offset_hull
|
||||
|
||||
def _getHeadAndFans(self):
|
||||
return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
||||
return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32))
|
||||
|
||||
def _compute2DConvexHeadFull(self):
|
||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||
|
@ -8,7 +8,7 @@ from UM.Logger import Logger
|
||||
from UM.Settings.Interfaces import DefinitionContainerInterface
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
from cura.Machines.VariantType import VariantType
|
||||
from .GlobalStack import GlobalStack
|
||||
from .ExtruderStack import ExtruderStack
|
||||
|
||||
|
@ -55,6 +55,12 @@ class GlobalStack(CuraContainerStack):
|
||||
return "machine_stack"
|
||||
return configuration_type
|
||||
|
||||
def getBuildplateName(self) -> Optional[str]:
|
||||
name = None
|
||||
if self.variant.getId() != "empty_variant":
|
||||
name = self.variant.getName()
|
||||
return name
|
||||
|
||||
## Add an extruder to the list of extruders of this stack.
|
||||
#
|
||||
# \param extruder The extruder to add.
|
||||
@ -96,6 +102,9 @@ class GlobalStack(CuraContainerStack):
|
||||
|
||||
# Handle the "resolve" property.
|
||||
#TODO: Why the hell does this involve threading?
|
||||
# Answer: Because if multiple threads start resolving properties that have the same underlying properties that's
|
||||
# related, without taking a note of which thread a resolve paths belongs to, they can bump into each other and
|
||||
# generate unexpected behaviours.
|
||||
if self._shouldResolve(key, property_name, context):
|
||||
current_thread = threading.current_thread()
|
||||
self._resolving_settings[current_thread.name].add(key)
|
||||
@ -172,6 +181,9 @@ class GlobalStack(CuraContainerStack):
|
||||
return False
|
||||
return True
|
||||
|
||||
def getHeadAndFansCoordinates(self):
|
||||
return self.getProperty("machine_head_with_fans_polygon", "value")
|
||||
|
||||
|
||||
## private:
|
||||
global_stack_mime = MimeType(
|
||||
|
@ -21,9 +21,6 @@ from UM.Settings.SettingFunction import SettingFunction
|
||||
from UM.Signal import postponeSignals, CompressTechnique
|
||||
|
||||
import cura.CuraApplication
|
||||
from cura.Machines.ContainerNode import ContainerNode #For typing.
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup #For typing.
|
||||
from cura.Machines.QualityGroup import QualityGroup #For typing.
|
||||
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from cura.PrinterOutput.ConfigurationModel import ConfigurationModel
|
||||
@ -44,12 +41,16 @@ if TYPE_CHECKING:
|
||||
from cura.Machines.MaterialManager import MaterialManager
|
||||
from cura.Machines.QualityManager import QualityManager
|
||||
from cura.Machines.VariantManager import VariantManager
|
||||
from cura.Machines.ContainerNode import ContainerNode
|
||||
from cura.Machines.QualityChangesGroup import QualityChangesGroup
|
||||
from cura.Machines.QualityGroup import QualityGroup
|
||||
|
||||
|
||||
class MachineManager(QObject):
|
||||
def __init__(self, parent: QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self._active_container_stack = None # type: Optional[ExtruderManager]
|
||||
self._active_container_stack = None # type: Optional[ExtruderStack]
|
||||
self._global_container_stack = None # type: Optional[GlobalStack]
|
||||
|
||||
self._current_root_material_id = {} # type: Dict[str, str]
|
||||
@ -1087,7 +1088,7 @@ class MachineManager(QObject):
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setQualityGroup(self, quality_group: Optional[QualityGroup], empty_quality_changes: bool = True) -> None:
|
||||
def _setQualityGroup(self, quality_group: Optional["QualityGroup"], empty_quality_changes: bool = True) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if quality_group is None:
|
||||
@ -1118,7 +1119,7 @@ class MachineManager(QObject):
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: QualityChangesGroup) -> None:
|
||||
def _fixQualityChangesGroupToNotSupported(self, quality_changes_group: "QualityChangesGroup") -> None:
|
||||
nodes = [quality_changes_group.node_for_global] + list(quality_changes_group.nodes_for_extruders.values())
|
||||
containers = [n.getContainer() for n in nodes if n is not None]
|
||||
for container in containers:
|
||||
@ -1126,7 +1127,7 @@ class MachineManager(QObject):
|
||||
container.setMetaDataEntry("quality_type", "not_supported")
|
||||
quality_changes_group.quality_type = "not_supported"
|
||||
|
||||
def _setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup) -> None:
|
||||
def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None:
|
||||
if self._global_container_stack is None:
|
||||
return #Can't change that.
|
||||
quality_type = quality_changes_group.quality_type
|
||||
@ -1170,20 +1171,20 @@ class MachineManager(QObject):
|
||||
self.activeQualityGroupChanged.emit()
|
||||
self.activeQualityChangesGroupChanged.emit()
|
||||
|
||||
def _setVariantNode(self, position: str, container_node: ContainerNode) -> None:
|
||||
def _setVariantNode(self, position: str, container_node: "ContainerNode") -> None:
|
||||
if container_node.getContainer() is None or self._global_container_stack is None:
|
||||
return
|
||||
self._global_container_stack.extruders[position].variant = container_node.getContainer()
|
||||
self.activeVariantChanged.emit()
|
||||
|
||||
def _setGlobalVariant(self, container_node: ContainerNode) -> None:
|
||||
def _setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
self._global_container_stack.variant = container_node.getContainer()
|
||||
if not self._global_container_stack.variant:
|
||||
self._global_container_stack.variant = self._application.empty_variant_container
|
||||
|
||||
def _setMaterial(self, position: str, container_node: ContainerNode = None) -> None:
|
||||
def _setMaterial(self, position: str, container_node: Optional["ContainerNode"] = None) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
if container_node and container_node.getContainer():
|
||||
@ -1256,13 +1257,17 @@ class MachineManager(QObject):
|
||||
else:
|
||||
position_list = [position]
|
||||
|
||||
buildplate_name = None
|
||||
if self._global_container_stack.variant.getId() != "empty_variant":
|
||||
buildplate_name = self._global_container_stack.variant.getName()
|
||||
|
||||
for position_item in position_list:
|
||||
extruder = self._global_container_stack.extruders[position_item]
|
||||
|
||||
current_material_base_name = extruder.material.getMetaDataEntry("base_file")
|
||||
current_variant_name = None
|
||||
current_nozzle_name = None
|
||||
if extruder.variant.getId() != self._empty_variant_container.getId():
|
||||
current_variant_name = extruder.variant.getMetaDataEntry("name")
|
||||
current_nozzle_name = extruder.variant.getMetaDataEntry("name")
|
||||
|
||||
from UM.Settings.Interfaces import PropertyEvaluationContext
|
||||
from cura.Settings.CuraContainerStack import _ContainerIndexes
|
||||
@ -1271,7 +1276,8 @@ class MachineManager(QObject):
|
||||
material_diameter = extruder.getProperty("material_diameter", "value", context)
|
||||
candidate_materials = self._material_manager.getAvailableMaterials(
|
||||
self._global_container_stack.definition,
|
||||
current_variant_name,
|
||||
current_nozzle_name,
|
||||
buildplate_name,
|
||||
material_diameter)
|
||||
|
||||
if not candidate_materials:
|
||||
@ -1284,7 +1290,7 @@ class MachineManager(QObject):
|
||||
continue
|
||||
|
||||
# The current material is not available, find the preferred one
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, position_item, current_variant_name)
|
||||
material_node = self._material_manager.getDefaultMaterial(self._global_container_stack, position_item, current_nozzle_name)
|
||||
if material_node is not None:
|
||||
self._setMaterial(position_item, material_node)
|
||||
|
||||
@ -1326,7 +1332,12 @@ class MachineManager(QObject):
|
||||
for extruder_configuration in configuration.extruderConfigurations:
|
||||
position = str(extruder_configuration.position)
|
||||
variant_container_node = self._variant_manager.getVariantNode(self._global_container_stack.definition.getId(), extruder_configuration.hotendID)
|
||||
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack, position, extruder_configuration.hotendID, extruder_configuration.material.guid)
|
||||
material_container_node = self._material_manager.getMaterialNodeByType(self._global_container_stack,
|
||||
position,
|
||||
extruder_configuration.hotendID,
|
||||
configuration.buildplateConfiguration,
|
||||
extruder_configuration.material.guid)
|
||||
|
||||
if variant_container_node:
|
||||
self._setVariantNode(position, variant_container_node)
|
||||
else:
|
||||
@ -1378,7 +1389,7 @@ class MachineManager(QObject):
|
||||
return bool(containers)
|
||||
|
||||
@pyqtSlot("QVariant")
|
||||
def setGlobalVariant(self, container_node: ContainerNode) -> None:
|
||||
def setGlobalVariant(self, container_node: "ContainerNode") -> None:
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setGlobalVariant(container_node)
|
||||
@ -1389,12 +1400,17 @@ class MachineManager(QObject):
|
||||
def setMaterialById(self, position: str, root_material_id: str) -> None:
|
||||
if self._global_container_stack is None:
|
||||
return
|
||||
buildplate_name = None
|
||||
if self._global_container_stack.variant.getId() != "empty_variant":
|
||||
buildplate_name = self._global_container_stack.variant.getName()
|
||||
|
||||
machine_definition_id = self._global_container_stack.definition.id
|
||||
position = str(position)
|
||||
extruder_stack = self._global_container_stack.extruders[position]
|
||||
variant_name = extruder_stack.variant.getName()
|
||||
nozzle_name = extruder_stack.variant.getName()
|
||||
material_diameter = extruder_stack.approximateMaterialDiameter
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, variant_name, material_diameter, root_material_id)
|
||||
material_node = self._material_manager.getMaterialNode(machine_definition_id, nozzle_name, buildplate_name,
|
||||
material_diameter, root_material_id)
|
||||
self.setMaterial(position, material_node)
|
||||
|
||||
## global_stack: if you want to provide your own global_stack instead of the current active one
|
||||
@ -1423,7 +1439,7 @@ class MachineManager(QObject):
|
||||
self.setVariant(position, variant_node)
|
||||
|
||||
@pyqtSlot(str, "QVariant")
|
||||
def setVariant(self, position: str, container_node: ContainerNode) -> None:
|
||||
def setVariant(self, position: str, container_node: "ContainerNode") -> None:
|
||||
position = str(position)
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
@ -1447,7 +1463,7 @@ class MachineManager(QObject):
|
||||
## Optionally provide global_stack if you want to use your own
|
||||
# The active global_stack is treated differently.
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityGroup(self, quality_group: QualityGroup, no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
def setQualityGroup(self, quality_group: "QualityGroup", no_dialog: bool = False, global_stack: Optional["GlobalStack"] = None) -> None:
|
||||
if global_stack is not None and global_stack != self._global_container_stack:
|
||||
if quality_group is None:
|
||||
Logger.log("e", "Could not set quality group because quality group is None")
|
||||
@ -1474,11 +1490,11 @@ class MachineManager(QObject):
|
||||
self._application.discardOrKeepProfileChanges()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityGroup, notify = activeQualityGroupChanged)
|
||||
def activeQualityGroup(self) -> Optional[QualityGroup]:
|
||||
def activeQualityGroup(self) -> Optional["QualityGroup"]:
|
||||
return self._current_quality_group
|
||||
|
||||
@pyqtSlot(QObject)
|
||||
def setQualityChangesGroup(self, quality_changes_group: QualityChangesGroup, no_dialog: bool = False) -> None:
|
||||
def setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", no_dialog: bool = False) -> None:
|
||||
self.blurSettings.emit()
|
||||
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||
self._setQualityChangesGroup(quality_changes_group)
|
||||
@ -1497,7 +1513,7 @@ class MachineManager(QObject):
|
||||
stack.userChanges.clear()
|
||||
|
||||
@pyqtProperty(QObject, fset = setQualityChangesGroup, notify = activeQualityChangesGroupChanged)
|
||||
def activeQualityChangesGroup(self) -> Optional[QualityChangesGroup]:
|
||||
def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]:
|
||||
return self._current_quality_changes_group
|
||||
|
||||
@pyqtProperty(str, notify = activeQualityGroupChanged)
|
||||
|
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
41
cura/Settings/SidebarCustomMenuItemsModel.py
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from PyQt5.QtCore import pyqtSlot, Qt
|
||||
|
||||
|
||||
class SidebarCustomMenuItemsModel(ListModel):
|
||||
name_role = Qt.UserRole + 1
|
||||
actions_role = Qt.UserRole + 2
|
||||
menu_item_role = Qt.UserRole + 3
|
||||
menu_item_icon_name_role = Qt.UserRole + 5
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.addRoleName(self.name_role, "name")
|
||||
self.addRoleName(self.actions_role, "actions")
|
||||
self.addRoleName(self.menu_item_role, "menu_item")
|
||||
self.addRoleName(self.menu_item_icon_name_role, "iconName")
|
||||
self._updateExtensionList()
|
||||
|
||||
def _updateExtensionList(self)-> None:
|
||||
from cura.CuraApplication import CuraApplication
|
||||
for menu_item in CuraApplication.getInstance().getSidebarCustomMenuItems():
|
||||
|
||||
self.appendItem({
|
||||
"name": menu_item["name"],
|
||||
"icon_name": menu_item["icon_name"],
|
||||
"actions": menu_item["actions"],
|
||||
"menu_item": menu_item["menu_item"]
|
||||
})
|
||||
|
||||
@pyqtSlot(str, "QVariantList", "QVariantMap")
|
||||
def callMenuItemMethod(self, menu_item_name: str, menu_item_actions: list, kwargs: Any) -> None:
|
||||
for item in self._items:
|
||||
if menu_item_name == item["name"]:
|
||||
for method in menu_item_actions:
|
||||
getattr(item["menu_item"], method)(kwargs)
|
||||
break
|
@ -24,6 +24,7 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
from cura.Machines.VariantType import VariantType
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
@ -889,7 +890,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
parser = self._machine_info.variant_info.parser
|
||||
variant_name = parser["general"]["name"]
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType.BUILD_PLATE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
@ -905,7 +905,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
parser = extruder_info.variant_info.parser
|
||||
|
||||
variant_name = parser["general"]["name"]
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType.NOZZLE
|
||||
|
||||
node = variant_manager.getVariantNode(global_stack.definition.getId(), variant_name, variant_type)
|
||||
@ -929,12 +928,16 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||
root_material_id = extruder_info.root_material_id
|
||||
root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
|
||||
|
||||
build_plate_id = global_stack.variant.getId()
|
||||
|
||||
# get material diameter of this extruder
|
||||
machine_material_diameter = extruder_stack.materialDiameter
|
||||
material_node = material_manager.getMaterialNode(global_stack.definition.getId(),
|
||||
extruder_stack.variant.getName(),
|
||||
build_plate_id,
|
||||
machine_material_diameter,
|
||||
root_material_id)
|
||||
|
||||
if material_node is not None and material_node.getContainer() is not None:
|
||||
extruder_stack.material = material_node.getContainer()
|
||||
|
||||
|
@ -41,7 +41,7 @@ class StartJobResult(IntEnum):
|
||||
|
||||
## Formatter class that handles token expansion in start/end gcode
|
||||
class GcodeStartEndFormatter(Formatter):
|
||||
def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
def get_value(self, key: str, args: str, kwargs: dict, default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class]
|
||||
# The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key),
|
||||
# and a default_extruder_nr to use when no extruder_nr is specified
|
||||
|
||||
|
@ -40,33 +40,37 @@ Item {
|
||||
|
||||
property bool layersVisible: true
|
||||
|
||||
function getUpperValueFromSliderHandle () {
|
||||
function getUpperValueFromSliderHandle() {
|
||||
return upperHandle.getValue()
|
||||
}
|
||||
|
||||
function setUpperValue (value) {
|
||||
function setUpperValue(value) {
|
||||
upperHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function getLowerValueFromSliderHandle () {
|
||||
function getLowerValueFromSliderHandle() {
|
||||
return lowerHandle.getValue()
|
||||
}
|
||||
|
||||
function setLowerValue (value) {
|
||||
function setLowerValue(value) {
|
||||
lowerHandle.setValue(value)
|
||||
updateRangeHandle()
|
||||
}
|
||||
|
||||
function updateRangeHandle () {
|
||||
function updateRangeHandle() {
|
||||
rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height)
|
||||
}
|
||||
|
||||
// set the active handle to show only one label at a time
|
||||
function setActiveHandle (handle) {
|
||||
function setActiveHandle(handle) {
|
||||
activeHandle = handle
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
@ -188,6 +192,8 @@ Item {
|
||||
|
||||
// set the slider position based on the upper value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentLayer(value)
|
||||
|
||||
@ -274,6 +280,8 @@ Item {
|
||||
|
||||
// set the slider position based on the lower value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setMinimumLayer(value)
|
||||
|
||||
|
@ -29,6 +29,7 @@ Item {
|
||||
|
||||
// value properties
|
||||
property real maximumValue: 100
|
||||
property real minimumValue: 0
|
||||
property bool roundValues: true
|
||||
property real handleValue: maximumValue
|
||||
|
||||
@ -47,6 +48,10 @@ Item {
|
||||
rangeHandle.width = handle.x - sliderRoot.handleSize
|
||||
}
|
||||
|
||||
function normalizeValue(value) {
|
||||
return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue)
|
||||
}
|
||||
|
||||
// slider track
|
||||
Rectangle {
|
||||
id: track
|
||||
@ -110,6 +115,8 @@ Item {
|
||||
|
||||
// set the slider position based on the value
|
||||
function setValue (value) {
|
||||
// Normalize values between range, since using arrow keys will create out-of-the-range values
|
||||
value = sliderRoot.normalizeValue(value)
|
||||
|
||||
UM.SimulationView.setCurrentPath(value)
|
||||
|
||||
|
@ -25,10 +25,12 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||
|
||||
from UM.Settings.SettingInstance import SettingInstance
|
||||
|
||||
import numpy
|
||||
|
||||
class SupportEraser(Tool):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._shortcut_key = Qt.Key_G
|
||||
self._shortcut_key = Qt.Key_E
|
||||
self._controller = self.getController()
|
||||
|
||||
self._selection_pass = None
|
||||
@ -96,8 +98,7 @@ class SupportEraser(Tool):
|
||||
|
||||
node.setName("Eraser")
|
||||
node.setSelectable(True)
|
||||
mesh = MeshBuilder()
|
||||
mesh.addCube(10,10,10)
|
||||
mesh = self._createCube(10)
|
||||
node.setMeshData(mesh.build())
|
||||
|
||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
@ -160,3 +161,28 @@ class SupportEraser(Tool):
|
||||
self._skip_press = False
|
||||
|
||||
self._had_selection = has_selection
|
||||
|
||||
def _createCube(self, size):
|
||||
mesh = MeshBuilder()
|
||||
|
||||
# Can't use MeshBuilder.addCube() because that does not get per-vertex normals
|
||||
# Per-vertex normals require duplication of vertices
|
||||
s = size / 2
|
||||
verts = [ # 6 faces with 4 corners each
|
||||
[-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s],
|
||||
[-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s],
|
||||
[ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s],
|
||||
[-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s],
|
||||
[-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s],
|
||||
[ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s]
|
||||
]
|
||||
mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32))
|
||||
|
||||
indices = []
|
||||
for i in range(0, 24, 4): # All 6 quads (12 triangles)
|
||||
indices.append([i, i+2, i+1])
|
||||
indices.append([i, i+3, i+2])
|
||||
mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32))
|
||||
|
||||
mesh.calculateNormals()
|
||||
return mesh
|
||||
|
@ -16,9 +16,9 @@ Item
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: OutputDevice.setActivePrinter(null)
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
onClicked: OutputDevice.setActivePrinter(null)
|
||||
z: 0
|
||||
}
|
||||
|
||||
Button
|
||||
@ -28,7 +28,7 @@ Item
|
||||
anchors.bottomMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.right: cameraImage.right
|
||||
|
||||
// TODO: Harcoded sizes
|
||||
// TODO: Hardcoded sizes
|
||||
width: 20 * screenScaleFactor
|
||||
height: 20 * screenScaleFactor
|
||||
|
||||
|
@ -39,12 +39,12 @@ class SendMaterialJob(Job):
|
||||
try:
|
||||
remote_materials_list = json.loads(remote_materials_list)
|
||||
except json.JSONDecodeError:
|
||||
Logger.log("e", "Current material storage on printer was a corrupted reply.")
|
||||
Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.")
|
||||
return
|
||||
try:
|
||||
remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID.
|
||||
except KeyError:
|
||||
Logger.log("e", "Current material storage on printer was an invalid reply (missing GUIDs).")
|
||||
Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.")
|
||||
return
|
||||
|
||||
container_registry = ContainerRegistry.getInstance()
|
||||
|
@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
||||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||
from .avr_isp import stk500v2, intelHex
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl
|
||||
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
@ -146,8 +146,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||
|
||||
@pyqtSlot(str)
|
||||
def updateFirmware(self, file):
|
||||
# the file path is qurl encoded.
|
||||
self._firmware_location = file.replace("file://", "")
|
||||
# the file path could be url-encoded.
|
||||
if file.startswith("file://"):
|
||||
self._firmware_location = QUrl(file).toLocalFile()
|
||||
else:
|
||||
self._firmware_location = file
|
||||
self.showFirmwareInterface()
|
||||
self.setFirmwareUpdateState(FirmwareUpdateState.updating)
|
||||
self._update_firmware_thread.start()
|
||||
|
@ -6,21 +6,22 @@ import io
|
||||
import json #To parse the product-to-id mapping file.
|
||||
import os.path #To find the product-to-id mapping.
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
from typing import Any, Dict, List, Optional, Tuple, cast
|
||||
import xml.etree.ElementTree as ET
|
||||
from typing import Dict
|
||||
from typing import Iterator
|
||||
|
||||
from UM.Resources import Resources
|
||||
from UM.Logger import Logger
|
||||
from cura.CuraApplication import CuraApplication
|
||||
import UM.Dictionary
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Machines.VariantType import VariantType
|
||||
|
||||
from .XmlMaterialValidator import XmlMaterialValidator
|
||||
|
||||
|
||||
## Handles serializing and deserializing material containers from an XML file
|
||||
class XmlMaterialProfile(InstanceContainer):
|
||||
CurrentFdmMaterialVersion = "1.3"
|
||||
@ -269,7 +270,6 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
buildplate_dict = {} # type: Dict[str, Any]
|
||||
for variant_name, variant_dict in machine_variant_map[definition_id].items():
|
||||
variant_type = variant_dict["variant_node"].metadata["hardware_type"]
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_type = VariantType(variant_type)
|
||||
if variant_type == VariantType.NOZZLE:
|
||||
# The hotend identifier is not the containers name, but its "name".
|
||||
@ -693,74 +693,38 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
if buildplate_id is None:
|
||||
continue
|
||||
|
||||
from cura.Machines.VariantManager import VariantType
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, buildplate_id,
|
||||
variant_type = VariantType.BUILD_PLATE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
buildplate_compatibility = machine_compatibility
|
||||
buildplate_recommended = machine_compatibility
|
||||
settings = buildplate.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__unmapped_settings:
|
||||
if key == "hardware compatible":
|
||||
buildplate_compatibility = self._parseCompatibleValue(entry.text)
|
||||
elif key == "hardware recommended":
|
||||
buildplate_recommended = self._parseCompatibleValue(entry.text)
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
_, buildplate_unmapped_settings_dict = self._getSettingsDictForNode(buildplate)
|
||||
|
||||
buildplate_compatibility = buildplate_unmapped_settings_dict.get("hardware compatible",
|
||||
machine_compatibility)
|
||||
buildplate_recommended = buildplate_unmapped_settings_dict.get("hardware recommended",
|
||||
machine_compatibility)
|
||||
|
||||
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
|
||||
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
|
||||
|
||||
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
||||
for hotend in hotends:
|
||||
# The "id" field for hotends in material profiles are actually
|
||||
# The "id" field for hotends in material profiles is actually name
|
||||
hotend_name = hotend.get("id")
|
||||
if hotend_name is None:
|
||||
continue
|
||||
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, hotend_name)
|
||||
variant_node = variant_manager.getVariantNode(machine_id, hotend_name, VariantType.NOZZLE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
hotend_compatibility = machine_compatibility
|
||||
hotend_setting_values = {}
|
||||
settings = hotend.iterfind("./um:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
key = entry.get("key")
|
||||
if key in self.__material_settings_setting_map:
|
||||
if key == "processing temperature graph": #This setting has no setting text but subtags.
|
||||
graph_nodes = entry.iterfind("./um:point", self.__namespaces)
|
||||
graph_points = []
|
||||
for graph_node in graph_nodes:
|
||||
flow = float(graph_node.get("flow"))
|
||||
temperature = float(graph_node.get("temperature"))
|
||||
graph_points.append([flow, temperature])
|
||||
hotend_setting_values[self.__material_settings_setting_map[key]] = str(graph_points)
|
||||
else:
|
||||
hotend_setting_values[self.__material_settings_setting_map[key]] = entry.text
|
||||
elif key in self.__unmapped_settings:
|
||||
if key == "hardware compatible":
|
||||
hotend_compatibility = self._parseCompatibleValue(entry.text)
|
||||
else:
|
||||
Logger.log("d", "Unsupported material setting %s", key)
|
||||
|
||||
# Add namespaced Cura-specific settings
|
||||
settings = hotend.iterfind("./cura:setting", self.__namespaces)
|
||||
for entry in settings:
|
||||
value = entry.text
|
||||
if value.lower() == "yes":
|
||||
value = True
|
||||
elif value.lower() == "no":
|
||||
value = False
|
||||
key = entry.get("key")
|
||||
hotend_setting_values[key] = value
|
||||
hotend_mapped_settings, hotend_unmapped_settings = self._getSettingsDictForNode(hotend)
|
||||
hotend_compatibility = hotend_unmapped_settings.get("hardware compatible", machine_compatibility)
|
||||
|
||||
# Generate container ID for the hotend-specific material container
|
||||
new_hotend_specific_material_id = self.getId() + "_" + machine_id + "_" + hotend_name.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||
@ -785,7 +749,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||
|
||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
||||
cached_hotend_setting_properties.update(hotend_setting_values)
|
||||
cached_hotend_setting_properties.update(hotend_mapped_settings)
|
||||
|
||||
new_hotend_material.setCachedValues(cached_hotend_setting_properties)
|
||||
|
||||
@ -794,6 +758,61 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
if is_new_material:
|
||||
containers_to_add.append(new_hotend_material)
|
||||
|
||||
#
|
||||
# Build plates in hotend
|
||||
#
|
||||
buildplates = hotend.iterfind("./um:buildplate", self.__namespaces)
|
||||
for buildplate in buildplates:
|
||||
# The "id" field for buildplate in material profiles is actually name
|
||||
buildplate_name = buildplate.get("id")
|
||||
if buildplate_name is None:
|
||||
continue
|
||||
|
||||
variant_manager = CuraApplication.getInstance().getVariantManager()
|
||||
variant_node = variant_manager.getVariantNode(machine_id, buildplate_name, VariantType.BUILD_PLATE)
|
||||
if not variant_node:
|
||||
continue
|
||||
|
||||
buildplate_mapped_settings, buildplate_unmapped_settings = self._getSettingsDictForNode(buildplate)
|
||||
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
|
||||
buildplate_map["buildplate_compatible"])
|
||||
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
|
||||
buildplate_map["buildplate_recommended"])
|
||||
|
||||
# Generate container ID for the hotend-and-buildplate-specific material container
|
||||
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(" ", "_")
|
||||
|
||||
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||
if ContainerRegistry.getInstance().isLoaded(new_hotend_and_buildplate_specific_material_id):
|
||||
new_hotend_and_buildplate_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_and_buildplate_specific_material_id)[0]
|
||||
is_new_material = False
|
||||
else:
|
||||
new_hotend_and_buildplate_material = XmlMaterialProfile(new_hotend_and_buildplate_specific_material_id)
|
||||
is_new_material = True
|
||||
|
||||
new_hotend_and_buildplate_material.setMetaData(copy.deepcopy(new_hotend_material.getMetaData()))
|
||||
new_hotend_and_buildplate_material.getMetaData()["id"] = new_hotend_and_buildplate_specific_material_id
|
||||
new_hotend_and_buildplate_material.getMetaData()["name"] = self.getName()
|
||||
new_hotend_and_buildplate_material.getMetaData()["variant_name"] = hotend_name
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_name"] = buildplate_name
|
||||
new_hotend_and_buildplate_material.setDefinition(machine_id)
|
||||
# Don't use setMetadata, as that overrides it for all materials with same base file
|
||||
new_hotend_and_buildplate_material.getMetaData()["compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||
new_hotend_and_buildplate_material.getMetaData()["definition"] = machine_id
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material.getMetaData()["buildplate_recommended"] = buildplate_recommended
|
||||
|
||||
cached_hotend_and_buildplate_setting_properties = cached_hotend_setting_properties.copy()
|
||||
cached_hotend_and_buildplate_setting_properties.update(buildplate_mapped_settings)
|
||||
|
||||
new_hotend_and_buildplate_material.setCachedValues(cached_hotend_and_buildplate_setting_properties)
|
||||
|
||||
new_hotend_and_buildplate_material._dirty = False
|
||||
|
||||
if is_new_material:
|
||||
containers_to_add.append(new_hotend_and_buildplate_material)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
@ -801,6 +820,54 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
for container_to_add in containers_to_add:
|
||||
ContainerRegistry.getInstance().addContainer(container_to_add)
|
||||
|
||||
@classmethod
|
||||
def _getSettingsDictForNode(cls, node) -> Tuple[dict, dict]:
|
||||
node_mapped_settings_dict = dict()
|
||||
node_unmapped_settings_dict = dict()
|
||||
|
||||
# Fetch settings in the "um" namespace
|
||||
um_settings = node.iterfind("./um:setting", cls.__namespaces)
|
||||
for um_setting_entry in um_settings:
|
||||
setting_key = um_setting_entry.get("key")
|
||||
|
||||
# Mapped settings
|
||||
if setting_key in cls.__material_settings_setting_map:
|
||||
if setting_key == "processing temperature graph": # This setting has no setting text but subtags.
|
||||
graph_nodes = um_setting_entry.iterfind("./um:point", cls.__namespaces)
|
||||
graph_points = []
|
||||
for graph_node in graph_nodes:
|
||||
flow = float(graph_node.get("flow"))
|
||||
temperature = float(graph_node.get("temperature"))
|
||||
graph_points.append([flow, temperature])
|
||||
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = str(
|
||||
graph_points)
|
||||
else:
|
||||
node_mapped_settings_dict[cls.__material_settings_setting_map[setting_key]] = um_setting_entry.text
|
||||
|
||||
# Unmapped settings
|
||||
elif setting_key in cls.__unmapped_settings:
|
||||
if setting_key in ("hardware compatible", "hardware recommended"):
|
||||
node_unmapped_settings_dict[setting_key] = cls._parseCompatibleValue(um_setting_entry.text)
|
||||
|
||||
# Unknown settings
|
||||
else:
|
||||
Logger.log("w", "Unsupported material setting %s", setting_key)
|
||||
|
||||
# Fetch settings in the "cura" namespace
|
||||
cura_settings = node.iterfind("./cura:setting", cls.__namespaces)
|
||||
for cura_setting_entry in cura_settings:
|
||||
value = cura_setting_entry.text
|
||||
if value.lower() == "yes":
|
||||
value = True
|
||||
elif value.lower() == "no":
|
||||
value = False
|
||||
key = cura_setting_entry.get("key")
|
||||
|
||||
# Cura settings are all mapped
|
||||
node_mapped_settings_dict[key] = value
|
||||
|
||||
return node_mapped_settings_dict, node_unmapped_settings_dict
|
||||
|
||||
@classmethod
|
||||
def deserializeMetadata(cls, serialized: str, container_id: str) -> List[Dict[str, Any]]:
|
||||
result_metadata = [] #All the metadata that we found except the base (because the base is returned).
|
||||
@ -983,6 +1050,36 @@ class XmlMaterialProfile(InstanceContainer):
|
||||
|
||||
result_metadata.append(new_hotend_material_metadata)
|
||||
|
||||
#
|
||||
# Buildplates in Hotends
|
||||
#
|
||||
buildplates = hotend.iterfind("./um:buildplate", cls.__namespaces)
|
||||
for buildplate in buildplates:
|
||||
# The "id" field for buildplate in material profiles is actually name
|
||||
buildplate_name = buildplate.get("id")
|
||||
if buildplate_name is None:
|
||||
continue
|
||||
|
||||
buildplate_mapped_settings, buildplate_unmapped_settings = cls._getSettingsDictForNode(buildplate)
|
||||
buildplate_compatibility = buildplate_unmapped_settings.get("hardware compatible",
|
||||
buildplate_map["buildplate_compatible"])
|
||||
buildplate_recommended = buildplate_unmapped_settings.get("hardware recommended",
|
||||
buildplate_map["buildplate_recommended"])
|
||||
|
||||
# Generate container ID for the hotend-and-buildplate-specific material container
|
||||
new_hotend_and_buildplate_specific_material_id = new_hotend_specific_material_id + "_" + buildplate_name.replace(
|
||||
" ", "_")
|
||||
|
||||
new_hotend_and_buildplate_material_metadata = {}
|
||||
new_hotend_and_buildplate_material_metadata.update(new_hotend_material_metadata)
|
||||
new_hotend_and_buildplate_material_metadata["id"] = new_hotend_and_buildplate_specific_material_id
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_name"] = buildplate_name
|
||||
new_hotend_and_buildplate_material_metadata["compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_compatible"] = buildplate_compatibility
|
||||
new_hotend_and_buildplate_material_metadata["buildplate_recommended"] = buildplate_recommended
|
||||
|
||||
result_metadata.append(new_hotend_and_buildplate_material_metadata)
|
||||
|
||||
# there is only one ID for a machine. Once we have reached here, it means we have already found
|
||||
# a workable ID for that machine, so there is no need to continue
|
||||
break
|
||||
|
@ -2127,6 +2127,7 @@
|
||||
"type": "float",
|
||||
"default_value": 60,
|
||||
"value": "default_material_bed_temperature",
|
||||
"resolve": "max(extruderValues('material_bed_temperature'))",
|
||||
"minimum_value": "-273.15",
|
||||
"minimum_value_warning": "0",
|
||||
"maximum_value_warning": "130",
|
||||
|
@ -4036,7 +4036,7 @@ msgstr "&Ansicht"
|
||||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:184
|
||||
msgctxt "@title:menu"
|
||||
msgid "&Settings"
|
||||
msgstr "&Einstellungen"
|
||||
msgstr "&Konfiguration"
|
||||
|
||||
#: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186
|
||||
msgctxt "@title:menu menubar:toplevel"
|
||||
|
@ -231,8 +231,8 @@ UM.MainWindow
|
||||
onObjectRemoved: settingsMenu.removeItem(object)
|
||||
}
|
||||
|
||||
// TODO Temporary hidden, add back again when feature ready
|
||||
// BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: Cura.MachineManager.hasVariantBuildplates }
|
||||
// TODO Only show in dev mode. Remove check when feature ready
|
||||
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates : false }
|
||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
|
||||
|
||||
MenuSeparator { }
|
||||
|
@ -48,15 +48,14 @@ Item {
|
||||
}
|
||||
|
||||
function sliceOrStopSlicing() {
|
||||
if ([1, 5].indexOf(base.backendState) != -1)
|
||||
{
|
||||
prepareButton.preparingToSlice = true;
|
||||
CuraApplication.backend.forceSlice();
|
||||
}
|
||||
else
|
||||
{
|
||||
prepareButton.preparingToSlice = false;
|
||||
CuraApplication.backend.stopSlicing();
|
||||
try {
|
||||
if ([1, 5].indexOf(base.backendState) != -1) {
|
||||
CuraApplication.backend.forceSlice();
|
||||
} else {
|
||||
CuraApplication.backend.stopSlicing();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Could not start or stop slicing', e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,10 +167,10 @@ Item {
|
||||
// Prepare button, only shows if auto_slice is off
|
||||
Button {
|
||||
id: prepareButton
|
||||
property bool preparingToSlice: false
|
||||
|
||||
tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process")
|
||||
// 1 = not started, 2 = Processing
|
||||
enabled: !preparingToSlice && base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity
|
||||
enabled: base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity
|
||||
visible: base.backendState != "undefined" && !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity
|
||||
property bool autoSlice
|
||||
height: UM.Theme.getSize("save_button_save_to_button").height
|
||||
@ -181,23 +180,7 @@ Item {
|
||||
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
|
||||
// 1 = not started, 4 = error, 5 = disabled
|
||||
text: {
|
||||
if (preparingToSlice)
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Preparing");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([1, 4, 5].indexOf(base.backendState) != -1)
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Prepare");
|
||||
}
|
||||
else
|
||||
{
|
||||
return catalog.i18nc("@label:Printjob", "Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
|
||||
onClicked:
|
||||
{
|
||||
sliceOrStopSlicing();
|
||||
@ -254,17 +237,6 @@ Item {
|
||||
}
|
||||
label: Item { }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: UM.Backend
|
||||
onStateChanged:
|
||||
{
|
||||
if ([2, 3].indexOf(UM.Backend.state) != -1)
|
||||
{
|
||||
prepareButton.preparingToSlice = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
|
@ -561,6 +561,28 @@ Item
|
||||
visible: machineExtruderCount.properties.value > 1
|
||||
}
|
||||
|
||||
Instantiator
|
||||
{
|
||||
id: customMenuItems
|
||||
model: Cura.SidebarCustomMenuItemsModel { }
|
||||
MenuItem
|
||||
{
|
||||
text: model.name
|
||||
iconName: model.icon_name
|
||||
onTriggered:
|
||||
{
|
||||
customMenuItems.model.callMenuItemMethod(name, model.actions, {"key": contextMenu.key})
|
||||
}
|
||||
}
|
||||
onObjectAdded: contextMenu.insertItem(index, object)
|
||||
onObjectRemoved: contextMenu.removeItem(object)
|
||||
}
|
||||
|
||||
MenuSeparator
|
||||
{
|
||||
visible: customMenuItems.count > 0
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
//: Settings context menu action
|
||||
|
@ -476,8 +476,8 @@ Column
|
||||
{
|
||||
id: buildplateRow
|
||||
height: UM.Theme.getSize("sidebar_setup").height
|
||||
// TODO Temporary hidden, add back again when feature ready
|
||||
visible: false //Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings
|
||||
// TODO Only show in dev mode. Remove check when feature ready
|
||||
visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings : false
|
||||
|
||||
anchors
|
||||
{
|
||||
@ -533,6 +533,7 @@ Column
|
||||
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||
}
|
||||
|
||||
// TODO This was added to replace the buildplate selector. Remove this component when the feature is ready
|
||||
Label
|
||||
{
|
||||
id: materialCompatibilityLabel
|
||||
@ -542,7 +543,7 @@ Column
|
||||
text: catalog.i18nc("@label", "Use glue with this material combination")
|
||||
font: UM.Theme.getFont("very_small")
|
||||
color: UM.Theme.getColor("text")
|
||||
visible: buildplateCompatibilityError || buildplateCompatibilityWarning
|
||||
visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning
|
||||
wrapMode: Text.WordWrap
|
||||
opacity: 0.5
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Copyright (c) 2018 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
@ -21,8 +21,8 @@ Item
|
||||
{
|
||||
id: buttons;
|
||||
|
||||
anchors.bottom: parent.bottom;
|
||||
anchors.left: parent.left;
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
spacing: UM.Theme.getSize("button_lining").width
|
||||
|
||||
Repeater
|
||||
@ -34,20 +34,22 @@ Item
|
||||
height: childrenRect.height
|
||||
Button
|
||||
{
|
||||
text: model.name
|
||||
text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "")
|
||||
iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon
|
||||
checkable: true
|
||||
checked: model.active
|
||||
enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled
|
||||
style: UM.Theme.styles.tool_button
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
base.activeY = y
|
||||
onCheckedChanged:
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
base.activeY = y;
|
||||
}
|
||||
}
|
||||
|
||||
//Workaround since using ToolButton"s onClicked would break the binding of the checked property, instead
|
||||
//Workaround since using ToolButton's onClicked would break the binding of the checked property, instead
|
||||
//just catch the click so we do not trigger that behaviour.
|
||||
MouseArea
|
||||
{
|
||||
@ -57,7 +59,7 @@ Item
|
||||
forceActiveFocus() //First grab focus, so all the text fields are updated
|
||||
if(parent.checked)
|
||||
{
|
||||
UM.Controller.setActiveTool(null)
|
||||
UM.Controller.setActiveTool(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -96,11 +98,13 @@ Item
|
||||
|
||||
width:
|
||||
{
|
||||
if (panel.item && panel.width > 0){
|
||||
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width)
|
||||
if (panel.item && panel.width > 0)
|
||||
{
|
||||
return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width);
|
||||
}
|
||||
else {
|
||||
return 0
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 0;
|
||||
@ -124,7 +128,7 @@ Item
|
||||
x: UM.Theme.getSize("default_margin").width;
|
||||
y: UM.Theme.getSize("default_margin").height;
|
||||
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "";
|
||||
source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""
|
||||
enabled: UM.Controller.toolsEnabled;
|
||||
}
|
||||
}
|
||||
@ -148,6 +152,6 @@ Item
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
visible: toolHint.text != "";
|
||||
visible: toolHint.text != ""
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_abs
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
cool_min_speed = 12
|
||||
layer_height_0 = 0.17
|
||||
machine_nozzle_cool_down_speed = 0.8
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 90
|
||||
material_bed_temperature_layer_0 = 100
|
||||
material_print_temperature = =default_material_print_temperature + 5
|
||||
material_initial_print_temperature = =material_print_temperature - 15
|
||||
material_final_print_temperature = =material_print_temperature - 20
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = False
|
||||
speed_print = 50
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
||||
|
@ -0,0 +1,53 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_cpe_plus
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
acceleration_enabled = True
|
||||
acceleration_print = 4000
|
||||
cool_fan_speed_max = 50
|
||||
cool_min_speed = 5
|
||||
infill_line_width = =round(line_width * 0.35 / 0.35, 2)
|
||||
infill_overlap = 0
|
||||
infill_wipe_dist = 0
|
||||
jerk_enabled = True
|
||||
jerk_print = 25
|
||||
layer_height_0 = 0.17
|
||||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 105
|
||||
material_bed_temperature_layer_0 = 115
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_print_temperature = =default_material_print_temperature + 2
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
multiple_mesh_overlap = 0
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
prime_tower_wipe_enabled = True
|
||||
retraction_combing = off
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 0.2
|
||||
retraction_hop_enabled = False
|
||||
retraction_hop_only_when_collides = True
|
||||
skin_overlap = 20
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 40)
|
||||
speed_print = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 35)
|
||||
|
||||
speed_wall = =math.ceil(speed_print * 35 / 40)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 35)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_z_distance = =layer_height
|
||||
wall_0_inset = 0
|
@ -0,0 +1,32 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_cpe
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
cool_min_speed = 12
|
||||
layer_height_0 = 0.17
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 80
|
||||
material_bed_temperature_layer_0 = 90
|
||||
material_print_temperature = =default_material_print_temperature - 5
|
||||
material_initial_print_temperature = =material_print_temperature - 5
|
||||
material_final_print_temperature = =material_print_temperature - 10
|
||||
prime_blob_enable = False
|
||||
speed_print = 50
|
||||
speed_layer_0 = =math.ceil(speed_print * 20 / 50)
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 50)
|
||||
|
||||
infill_pattern = zigzag
|
||||
speed_infill = =math.ceil(speed_print * 40 / 50)
|
@ -0,0 +1,71 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Extra Fine
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = high
|
||||
weight = 1
|
||||
material = generic_pc
|
||||
variant = AA 0.4
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
acceleration_enabled = True
|
||||
acceleration_print = 4000
|
||||
adhesion_type = brim
|
||||
brim_width = 10
|
||||
cool_fan_full_at_height = =layer_height_0 + layer_height
|
||||
cool_fan_speed_max = 50
|
||||
cool_min_layer_time_fan_speed_max = 5
|
||||
cool_min_speed = 8
|
||||
infill_line_width = =round(line_width * 0.4 / 0.35, 2)
|
||||
infill_overlap = 0
|
||||
infill_overlap_mm = 0.05
|
||||
infill_pattern = triangles
|
||||
infill_wipe_dist = 0.1
|
||||
jerk_enabled = True
|
||||
jerk_print = 25
|
||||
layer_height_0 = 0.17
|
||||
machine_min_cool_heat_time_window = 15
|
||||
machine_nozzle_cool_down_speed = 0.85
|
||||
machine_nozzle_heat_up_speed = 1.5
|
||||
material_bed_temperature = 115
|
||||
material_bed_temperature_layer_0 = 125
|
||||
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_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0
|
||||
ooze_shield_angle = 40
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
prime_tower_wipe_enabled = True
|
||||
raft_airgap = 0.25
|
||||
raft_interface_thickness = =max(layer_height * 1.5, 0.225)
|
||||
retraction_count_max = 80
|
||||
retraction_extrusion_window = 1
|
||||
retraction_hop = 2
|
||||
retraction_hop_only_when_collides = True
|
||||
retraction_min_travel = 0.8
|
||||
retraction_prime_speed = 15
|
||||
skin_overlap = 30
|
||||
speed_layer_0 = =math.ceil(speed_print * 25 / 50)
|
||||
speed_print = 50
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 25 / 40)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_interface_density = 87.5
|
||||
support_interface_pattern = lines
|
||||
switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 35
|
||||
wall_0_inset = 0
|
||||
wall_line_width_x = =round(line_width * 0.4 / 0.35, 2)
|
||||
wall_thickness = 1.2
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_abs
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 90
|
||||
material_bed_temperature_layer_0 = 100
|
||||
material_print_temperature = =default_material_print_temperature + 20
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
speed_print = 50
|
||||
speed_topbottom = =math.ceil(speed_print * 30 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
retract_at_layer_change = False
|
@ -0,0 +1,43 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Fast - Experimental
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_cpe_plus
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 14
|
||||
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||
infill_before_walls = True
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.9375
|
||||
machine_nozzle_cool_down_speed = 0.9
|
||||
machine_nozzle_heat_up_speed = 1.4
|
||||
material_bed_temperature = 105
|
||||
material_bed_temperature_layer_0 = 115
|
||||
material_print_temperature = =default_material_print_temperature - 10
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
retraction_combing = off
|
||||
retraction_hop = 0.1
|
||||
retraction_hop_enabled = False
|
||||
skin_overlap = 0
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||
speed_print = 50
|
||||
speed_slowdown_layers = 15
|
||||
speed_topbottom = =math.ceil(speed_print * 35 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 35 / 40)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
||||
support_z_distance = =layer_height
|
||||
top_bottom_thickness = 1.2
|
@ -0,0 +1,29 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_cpe
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 15
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 80
|
||||
material_bed_temperature_layer_0 = 90
|
||||
material_print_temperature = =default_material_print_temperature + 15
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
prime_tower_enable = True
|
||||
speed_print = 40
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 40)
|
||||
speed_wall = =math.ceil(speed_print * 30 / 40)
|
||||
|
||||
jerk_travel = 50
|
@ -0,0 +1,36 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Fast - Experimental
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = 0
|
||||
material = generic_pc
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 10
|
||||
cool_fan_full_at_height = =layer_height_0 + 14 * layer_height
|
||||
infill_before_walls = True
|
||||
layer_height_0 = 0.3
|
||||
line_width = =machine_nozzle_size * 0.875
|
||||
material_bed_temperature = 115
|
||||
material_bed_temperature_layer_0 = 125
|
||||
material_print_temperature = =default_material_print_temperature - 5
|
||||
material_print_temperature_layer_0 = =material_print_temperature
|
||||
material_standby_temperature = 100
|
||||
prime_blob_enable = False
|
||||
raft_airgap = 0.5
|
||||
raft_margin = 15
|
||||
skin_overlap = 0
|
||||
speed_layer_0 = =math.ceil(speed_print * 15 / 50)
|
||||
speed_print = 50
|
||||
speed_slowdown_layers = 15
|
||||
speed_topbottom = =math.ceil(speed_print * 25 / 50)
|
||||
speed_wall = =math.ceil(speed_print * 40 / 50)
|
||||
speed_wall_0 = =math.ceil(speed_wall * 30 / 40)
|
||||
support_line_width = =round(line_width * 0.6 / 0.7, 2)
|
@ -0,0 +1,53 @@
|
||||
[general]
|
||||
version = 4
|
||||
name = Fast
|
||||
definition = ultimaker_s5
|
||||
|
||||
[metadata]
|
||||
setting_version = 5
|
||||
type = quality
|
||||
quality_type = draft
|
||||
weight = -2
|
||||
material = generic_pp
|
||||
variant = AA 0.8
|
||||
buildplate = Aluminum
|
||||
|
||||
[values]
|
||||
brim_width = 25
|
||||
cool_min_layer_time_fan_speed_max = 6
|
||||
cool_min_speed = 17
|
||||
top_skin_expand_distance = =line_width * 2
|
||||
infill_before_walls = True
|
||||
infill_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||
infill_pattern = tetrahedral
|
||||
jerk_prime_tower = =math.ceil(jerk_print * 25 / 25)
|
||||
jerk_support = =math.ceil(jerk_print * 25 / 25)
|
||||
jerk_wall_0 = =math.ceil(jerk_wall * 15 / 25)
|
||||
material_bed_temperature_layer_0 = =material_bed_temperature + 5
|
||||
material_print_temperature = =default_material_print_temperature - 2
|
||||
material_print_temperature_layer_0 = =default_material_print_temperature + 2
|
||||
material_standby_temperature = 100
|
||||
multiple_mesh_overlap = 0.2
|
||||
prime_tower_enable = True
|
||||
prime_tower_flow = 100
|
||||
prime_tower_min_volume = 10
|
||||
retract_at_layer_change = False
|
||||
retraction_count_max = 12
|
||||
retraction_extra_prime_amount = 0.5
|
||||
retraction_hop = 0.5
|
||||
retraction_min_travel = 1.5
|
||||
retraction_prime_speed = 15
|
||||
skin_line_width = =round(line_width * 0.78 / 0.8, 2)
|
||||
|
||||
speed_wall_x = =math.ceil(speed_wall * 30 / 30)
|
||||
support_bottom_distance = =support_z_distance
|
||||
support_line_width = =round(line_width * 0.7 / 0.8, 2)
|
||||
support_offset = =line_width
|
||||
switch_extruder_prime_speed = 15
|
||||
switch_extruder_retraction_amount = 20
|
||||
switch_extruder_retraction_speeds = 45
|
||||
top_bottom_thickness = 1.6
|
||||
travel_compensate_overlapping_walls_0_enabled = False
|
||||
wall_0_wipe_dist = =line_width * 2
|
||||
wall_line_width_x = =round(line_width * 0.8 / 0.8, 2)
|
||||
wall_thickness = 1.6
|
Loading…
x
Reference in New Issue
Block a user