mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-15 10:47:49 -06:00
Merge branch 'master' into feature_sync_button
This commit is contained in:
commit
b212781a19
9 changed files with 396 additions and 25 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
resources/materials
|
||||||
|
CuraEngine
|
45
Dockerfile
Normal file
45
Dockerfile
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
FROM ultimaker/cura-build-environment:1
|
||||||
|
|
||||||
|
# Environment vars for easy configuration
|
||||||
|
ENV CURA_APP_DIR=/srv/cura
|
||||||
|
|
||||||
|
# Ensure our sources dir exists
|
||||||
|
RUN mkdir $CURA_APP_DIR
|
||||||
|
|
||||||
|
# Setup CuraEngine
|
||||||
|
ENV CURA_ENGINE_BRANCH=master
|
||||||
|
WORKDIR $CURA_APP_DIR
|
||||||
|
RUN git clone -b $CURA_ENGINE_BRANCH --depth 1 https://github.com/Ultimaker/CuraEngine
|
||||||
|
WORKDIR $CURA_APP_DIR/CuraEngine
|
||||||
|
RUN mkdir build
|
||||||
|
WORKDIR $CURA_APP_DIR/CuraEngine/build
|
||||||
|
RUN cmake3 ..
|
||||||
|
RUN make
|
||||||
|
RUN make install
|
||||||
|
|
||||||
|
# TODO: setup libCharon
|
||||||
|
|
||||||
|
# Setup Uranium
|
||||||
|
ENV URANIUM_BRANCH=master
|
||||||
|
WORKDIR $CURA_APP_DIR
|
||||||
|
RUN git clone -b $URANIUM_BRANCH --depth 1 https://github.com/Ultimaker/Uranium
|
||||||
|
|
||||||
|
# Setup materials
|
||||||
|
ENV MATERIALS_BRANCH=master
|
||||||
|
WORKDIR $CURA_APP_DIR
|
||||||
|
RUN git clone -b $MATERIALS_BRANCH --depth 1 https://github.com/Ultimaker/fdm_materials materials
|
||||||
|
|
||||||
|
# Setup Cura
|
||||||
|
WORKDIR $CURA_APP_DIR/Cura
|
||||||
|
ADD . .
|
||||||
|
RUN mv $CURA_APP_DIR/materials resources/materials
|
||||||
|
|
||||||
|
# Make sure Cura can find CuraEngine
|
||||||
|
RUN ln -s /usr/local/bin/CuraEngine $CURA_APP_DIR/Cura
|
||||||
|
|
||||||
|
# Run Cura
|
||||||
|
WORKDIR $CURA_APP_DIR/Cura
|
||||||
|
ENV PYTHONPATH=${PYTHONPATH}:$CURA_APP_DIR/Uranium
|
||||||
|
RUN chmod +x ./CuraEngine
|
||||||
|
RUN chmod +x ./run_in_docker.sh
|
||||||
|
CMD "./run_in_docker.sh"
|
|
@ -108,7 +108,7 @@ class MaterialManager(QObject):
|
||||||
Logger.log("e", "Missing root material node for [%s]. Probably caused by update using incomplete data."
|
Logger.log("e", "Missing root material node for [%s]. Probably caused by update using incomplete data."
|
||||||
" Check all related signals for further debugging.",
|
" Check all related signals for further debugging.",
|
||||||
material_group.name)
|
material_group.name)
|
||||||
# Do nothing here, we wait for a next signal to trigger an update.
|
self._update_timer.start()
|
||||||
return
|
return
|
||||||
guid = material_group.root_material_node.metadata["GUID"]
|
guid = material_group.root_material_node.metadata["GUID"]
|
||||||
self._guid_material_groups_map[guid].append(material_group)
|
self._guid_material_groups_map[guid].append(material_group)
|
||||||
|
@ -218,6 +218,7 @@ class MaterialManager(QObject):
|
||||||
self.materialsUpdated.emit()
|
self.materialsUpdated.emit()
|
||||||
|
|
||||||
def _updateMaps(self):
|
def _updateMaps(self):
|
||||||
|
Logger.log("i", "Updating material lookup data ...")
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
|
||||||
def _onContainerMetadataChanged(self, container):
|
def _onContainerMetadataChanged(self, container):
|
||||||
|
@ -400,6 +401,16 @@ class MaterialManager(QObject):
|
||||||
material_diameter, root_material_id)
|
material_diameter, root_material_id)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
def removeMaterialByRootId(self, root_material_id: str):
|
||||||
|
material_group = self.getMaterialGroup(root_material_id)
|
||||||
|
if not material_group:
|
||||||
|
Logger.log("i", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
||||||
|
for node in nodes_to_remove:
|
||||||
|
self._container_registry.removeContainer(node.metadata["id"])
|
||||||
|
|
||||||
#
|
#
|
||||||
# Methods for GUI
|
# Methods for GUI
|
||||||
#
|
#
|
||||||
|
@ -423,14 +434,7 @@ class MaterialManager(QObject):
|
||||||
@pyqtSlot("QVariant")
|
@pyqtSlot("QVariant")
|
||||||
def removeMaterial(self, material_node: "MaterialNode"):
|
def removeMaterial(self, material_node: "MaterialNode"):
|
||||||
root_material_id = material_node.metadata["base_file"]
|
root_material_id = material_node.metadata["base_file"]
|
||||||
material_group = self.getMaterialGroup(root_material_id)
|
self.removeMaterialByRootId(root_material_id)
|
||||||
if not material_group:
|
|
||||||
Logger.log("d", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
|
|
||||||
return
|
|
||||||
|
|
||||||
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
|
|
||||||
for node in nodes_to_remove:
|
|
||||||
self._container_registry.removeContainer(node.metadata["id"])
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
|
# Creates a duplicate of a material, which has the same GUID and base_file metadata.
|
||||||
|
|
|
@ -389,8 +389,6 @@ class ContainerManager(QObject):
|
||||||
return ContainerManager.getInstance()
|
return ContainerManager.getInstance()
|
||||||
|
|
||||||
def _performMerge(self, merge_into, merge, clear_settings = True):
|
def _performMerge(self, merge_into, merge, clear_settings = True):
|
||||||
assert isinstance(merge, type(merge_into))
|
|
||||||
|
|
||||||
if merge == merge_into:
|
if merge == merge_into:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -352,6 +352,14 @@ class MachineManager(QObject):
|
||||||
|
|
||||||
self.__emitChangedSignals()
|
self.__emitChangedSignals()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getMachine(definition_id: str) -> Optional["GlobalStack"]:
|
||||||
|
machines = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
|
||||||
|
for machine in machines:
|
||||||
|
if machine.definition.getId() == definition_id:
|
||||||
|
return machine
|
||||||
|
return None
|
||||||
|
|
||||||
@pyqtSlot(str, str)
|
@pyqtSlot(str, str)
|
||||||
def addMachine(self, name: str, definition_id: str) -> None:
|
def addMachine(self, name: str, definition_id: str) -> None:
|
||||||
new_stack = CuraStackBuilder.createMachine(name, definition_id)
|
new_stack = CuraStackBuilder.createMachine(name, definition_id)
|
||||||
|
|
|
@ -97,6 +97,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# In Cura 2.5 and 2.6, the empty profiles used to have those long names
|
# In Cura 2.5 and 2.6, the empty profiles used to have those long names
|
||||||
self._old_empty_profile_id_dict = {"empty_%s" % k: "empty" for k in ["material", "variant"]}
|
self._old_empty_profile_id_dict = {"empty_%s" % k: "empty" for k in ["material", "variant"]}
|
||||||
|
|
||||||
|
self._old_new_materials = {}
|
||||||
|
self._materials_to_select = {}
|
||||||
|
|
||||||
|
def _clearState(self):
|
||||||
|
self._id_mapping = {}
|
||||||
|
self._old_new_materials = {}
|
||||||
|
self._materials_to_select = {}
|
||||||
|
|
||||||
## Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results.
|
## Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results.
|
||||||
# This has nothing to do with speed, but with getting consistent new naming for instances & objects.
|
# This has nothing to do with speed, but with getting consistent new naming for instances & objects.
|
||||||
def getNewId(self, old_id):
|
def getNewId(self, old_id):
|
||||||
|
@ -449,10 +457,15 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# To avoid this, we postpone all signals so they don't get emitted immediately. But, please also be aware that,
|
# To avoid this, we postpone all signals so they don't get emitted immediately. But, please also be aware that,
|
||||||
# because of this, do not expect to have the latest data in the lookup tables in project loading.
|
# because of this, do not expect to have the latest data in the lookup tables in project loading.
|
||||||
#
|
#
|
||||||
with postponeSignals(*signals, compress = CompressTechnique.CompressSingle):
|
with postponeSignals(*signals, compress = CompressTechnique.NoCompression):
|
||||||
return self._read(file_name)
|
return self._read(file_name)
|
||||||
|
|
||||||
def _read(self, file_name):
|
def _read(self, file_name):
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
|
material_manager = application.getMaterialManager()
|
||||||
|
|
||||||
|
self._clearState()
|
||||||
|
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
archive = zipfile.ZipFile(file_name, "r")
|
||||||
|
|
||||||
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
cura_file_names = [name for name in archive.namelist() if name.startswith("Cura/")]
|
||||||
|
@ -545,31 +558,41 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
if self._material_container_suffix is None:
|
if self._material_container_suffix is None:
|
||||||
self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
|
self._material_container_suffix = ContainerRegistry.getMimeTypeForContainer(xml_material_profile).suffixes[0]
|
||||||
if xml_material_profile:
|
if xml_material_profile:
|
||||||
|
to_deserialize_material = False
|
||||||
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
|
material_container_files = [name for name in cura_file_names if name.endswith(self._material_container_suffix)]
|
||||||
for material_container_file in material_container_files:
|
for material_container_file in material_container_files:
|
||||||
container_id = self._stripFileToId(material_container_file)
|
container_id = self._stripFileToId(material_container_file)
|
||||||
|
need_new_name = False
|
||||||
materials = self._container_registry.findInstanceContainers(id = container_id)
|
materials = self._container_registry.findInstanceContainers(id = container_id)
|
||||||
|
|
||||||
if not materials:
|
if not materials:
|
||||||
material_container = xml_material_profile(container_id)
|
# No material found, deserialize this material later and add it
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
to_deserialize_material = True
|
||||||
file_name = material_container_file)
|
|
||||||
containers_to_add.append(material_container)
|
|
||||||
else:
|
else:
|
||||||
material_container = materials[0]
|
material_container = materials[0]
|
||||||
|
old_material_root_id = material_container.getMetaDataEntry("base_file")
|
||||||
if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only.
|
if not self._container_registry.isReadOnly(container_id): # Only create new materials if they are not read only.
|
||||||
|
to_deserialize_material = True
|
||||||
|
|
||||||
if self._resolve_strategies["material"] == "override":
|
if self._resolve_strategies["material"] == "override":
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
# Remove the old materials and then deserialize the one from the project
|
||||||
file_name = material_container_file)
|
root_material_id = material_container.getMetaDataEntry("base_file")
|
||||||
|
material_manager.removeMaterialByRootId(root_material_id)
|
||||||
elif self._resolve_strategies["material"] == "new":
|
elif self._resolve_strategies["material"] == "new":
|
||||||
# Note that we *must* deserialize it with a new ID, as multiple containers will be
|
# Note that we *must* deserialize it with a new ID, as multiple containers will be
|
||||||
# auto created & added.
|
# auto created & added.
|
||||||
material_container = xml_material_profile(self.getNewId(container_id))
|
container_id = self.getNewId(container_id)
|
||||||
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
self._old_new_materials[old_material_root_id] = container_id
|
||||||
file_name = material_container_file)
|
need_new_name = True
|
||||||
containers_to_add.append(material_container)
|
|
||||||
|
|
||||||
material_containers.append(material_container)
|
if to_deserialize_material:
|
||||||
|
material_container = xml_material_profile(container_id)
|
||||||
|
material_container.deserialize(archive.open(material_container_file).read().decode("utf-8"),
|
||||||
|
file_name = container_id + "." + self._material_container_suffix)
|
||||||
|
if need_new_name:
|
||||||
|
new_name = ContainerRegistry.getInstance().uniqueName(material_container.getName())
|
||||||
|
material_container.setName(new_name)
|
||||||
|
containers_to_add.append(material_container)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
Logger.log("d", "Workspace loading is checking instance containers...")
|
Logger.log("d", "Workspace loading is checking instance containers...")
|
||||||
|
@ -1081,11 +1104,18 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
old_to_new_material_dict[old_id] = each_material
|
old_to_new_material_dict[old_id] = each_material
|
||||||
break
|
break
|
||||||
|
|
||||||
|
global_stack.quality = empty_quality_container
|
||||||
|
|
||||||
# replace old material in global and extruder stacks with new
|
# replace old material in global and extruder stacks with new
|
||||||
self._replaceStackMaterialWithNew(global_stack, old_to_new_material_dict)
|
self._replaceStackMaterialWithNew(global_stack, old_to_new_material_dict)
|
||||||
if extruder_stacks:
|
if extruder_stacks:
|
||||||
for each_extruder_stack in extruder_stacks:
|
for extruder_stack in extruder_stacks:
|
||||||
self._replaceStackMaterialWithNew(each_extruder_stack, old_to_new_material_dict)
|
if extruder_stack.material.getId() in ("empty", "empty_material"):
|
||||||
|
continue
|
||||||
|
old_root_material_id = extruder_stack.material.getMetaDataEntry("base_file")
|
||||||
|
if old_root_material_id in self._old_new_materials:
|
||||||
|
new_root_material_id = self._old_new_materials[old_root_material_id]
|
||||||
|
self._materials_to_select[extruder_stack.getMetaDataEntry("position")] = new_root_material_id
|
||||||
|
|
||||||
if extruder_stacks:
|
if extruder_stacks:
|
||||||
for stack in extruder_stacks:
|
for stack in extruder_stacks:
|
||||||
|
@ -1123,6 +1153,22 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
def _updateActiveMachine(self, global_stack):
|
def _updateActiveMachine(self, global_stack):
|
||||||
# Actually change the active machine.
|
# Actually change the active machine.
|
||||||
machine_manager = Application.getInstance().getMachineManager()
|
machine_manager = Application.getInstance().getMachineManager()
|
||||||
|
material_manager = Application.getInstance().getMaterialManager()
|
||||||
|
|
||||||
|
# Switch materials if new materials are created due to conflicts
|
||||||
|
# We do it here because MaterialManager hasn't been updated in _read() yet.
|
||||||
|
for position, root_material_id in self._materials_to_select.items():
|
||||||
|
extruder_stack = global_stack.extruders[position]
|
||||||
|
material_diameter = extruder_stack.materialDiameter
|
||||||
|
material_node = material_manager.getMaterialNode(global_stack.getMetaDataEntry("definition"),
|
||||||
|
extruder_stack.variant.getName(),
|
||||||
|
material_diameter, root_material_id)
|
||||||
|
if material_node is None:
|
||||||
|
Application.getInstance().callLater(self._updateActiveMachine, global_stack)
|
||||||
|
return
|
||||||
|
extruder_stack.material = material_node.getContainer()
|
||||||
|
Logger.log("d", "Changed extruder [%s] to material [%s]", position, root_material_id)
|
||||||
|
|
||||||
machine_manager.setActiveMachine(global_stack.getId())
|
machine_manager.setActiveMachine(global_stack.getId())
|
||||||
|
|
||||||
# Notify everything/one that is to notify about changes.
|
# Notify everything/one that is to notify about changes.
|
||||||
|
|
|
@ -33,6 +33,9 @@ from UM.i18n import i18nCatalog
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
class CuraEngineBackend(QObject, Backend):
|
class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
|
backendError = Signal()
|
||||||
|
|
||||||
## Starts the back-end plug-in.
|
## Starts the back-end plug-in.
|
||||||
#
|
#
|
||||||
# This registers all the signal listeners and prepares for communication
|
# This registers all the signal listeners and prepares for communication
|
||||||
|
@ -289,6 +292,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
|
if job.isCancelled() or job.getError() or job.getResult() == StartSliceJob.StartJobResult.Error:
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
return
|
return
|
||||||
|
|
||||||
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
|
if job.getResult() == StartSliceJob.StartJobResult.MaterialIncompatible:
|
||||||
|
@ -297,6 +301,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
|
"Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
return
|
return
|
||||||
|
@ -325,6 +330,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
return
|
return
|
||||||
|
@ -347,6 +353,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
return
|
return
|
||||||
|
|
||||||
if job.getResult() == StartSliceJob.StartJobResult.BuildPlateError:
|
if job.getResult() == StartSliceJob.StartJobResult.BuildPlateError:
|
||||||
|
@ -355,6 +362,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
|
|
||||||
|
@ -364,6 +372,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
title = catalog.i18nc("@info:title", "Unable to slice"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
self.backendStateChange.emit(BackendState.Error)
|
||||||
|
self.backendError.emit(job)
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
self._invokeSlice()
|
self._invokeSlice()
|
||||||
|
|
|
@ -6229,6 +6229,259 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
|
},
|
||||||
|
"bridge_settings_enabled":
|
||||||
|
{
|
||||||
|
"label": "Enable Bridge Settings",
|
||||||
|
"description": "Detect bridges and modify print speed, flow and fan settings while bridges are printed.",
|
||||||
|
"type": "bool",
|
||||||
|
"default_value": false,
|
||||||
|
"value": "not support_enable and not support_tree_enable",
|
||||||
|
"settable_per_mesh": true,
|
||||||
|
"settable_per_extruder": false,
|
||||||
|
"settable_per_meshgroup": false
|
||||||
|
},
|
||||||
|
"bridge_wall_min_length":
|
||||||
|
{
|
||||||
|
"label": "Minimum Bridge Wall Length",
|
||||||
|
"description": "Unsupported walls shorter than this will be printed using the normal wall settings. Longer unsupported walls will be printed using the bridge wall settings.",
|
||||||
|
"unit": "mm",
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"default_value": 5,
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": true
|
||||||
|
},
|
||||||
|
"bridge_skin_support_threshold":
|
||||||
|
{
|
||||||
|
"label": "Bridge Skin Support Threshold",
|
||||||
|
"description": "If a skin region is supported for less than this percentage of its area, print it using the bridge settings. Otherwise it is printed using the normal skin settings.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 50,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_wall_max_overhang":
|
||||||
|
{
|
||||||
|
"label": "Bridge Wall Max Overhang",
|
||||||
|
"description": "The maximum allowed width of the region of air below a wall line before the wall is printed using bridge settings. Expressed as a percentage of the wall line width. When the air gap is wider than this, the wall line is printed using the bridge settings. Otherwise, the wall line is printed using the normal settings. The lower the value, the more likely it is that overhung wall lines will be printed using bridge settings.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 100,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_wall_coast":
|
||||||
|
{
|
||||||
|
"label": "Bridge Wall Coasting",
|
||||||
|
"description": "This controls the distance the extruder should coast immediately before a bridge wall begins. Coasting before the bridge starts can reduce the pressure in the nozzle and may produce a flatter bridge.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 100,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "500",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": false
|
||||||
|
},
|
||||||
|
"bridge_wall_speed":
|
||||||
|
{
|
||||||
|
"label": "Bridge Wall Speed",
|
||||||
|
"description": "The speed at which the bridge walls are printed.",
|
||||||
|
"unit": "mm/s",
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "cool_min_speed",
|
||||||
|
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||||
|
"maximum_value_warning": "300",
|
||||||
|
"default_value": 15,
|
||||||
|
"value": "max(cool_min_speed, speed_wall_0 / 2)",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_wall_material_flow":
|
||||||
|
{
|
||||||
|
"label": "Bridge Wall Flow",
|
||||||
|
"description": "When printing bridge walls, the amount of material extruded is multiplied by this value.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 50,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"minimum_value_warning": "50",
|
||||||
|
"maximum_value_warning": "150",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_speed":
|
||||||
|
{
|
||||||
|
"label": "Bridge Skin Speed",
|
||||||
|
"description": "The speed at which bridge skin regions are printed.",
|
||||||
|
"unit": "mm/s",
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "cool_min_speed",
|
||||||
|
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||||
|
"maximum_value_warning": "300",
|
||||||
|
"default_value": 15,
|
||||||
|
"value": "max(cool_min_speed, speed_topbottom / 2)",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_material_flow":
|
||||||
|
{
|
||||||
|
"label": "Bridge Skin Flow",
|
||||||
|
"description": "When printing bridge skin regions, the amount of material extruded is multiplied by this value.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 60,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"minimum_value_warning": "50",
|
||||||
|
"maximum_value_warning": "150",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_density":
|
||||||
|
{
|
||||||
|
"label": "Bridge Skin Density",
|
||||||
|
"description": "The density of the bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 100,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"minimum_value_warning": "20",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_fan_speed":
|
||||||
|
{
|
||||||
|
"label": "Bridge Fan Speed",
|
||||||
|
"description": "Percentage fan speed to use when printing bridge walls and skin.",
|
||||||
|
"unit": "%",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"default_value": 100,
|
||||||
|
"type": "float",
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_enable_more_layers":
|
||||||
|
{
|
||||||
|
"label": "Bridge Has Multiple Layers",
|
||||||
|
"description": "If enabled, the second and third layers above the air are printed using the following settings. Otherwise, those layers are printed using the normal settings.",
|
||||||
|
"type": "bool",
|
||||||
|
"default_value": true,
|
||||||
|
"enabled": "bridge_settings_enabled",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_speed_2":
|
||||||
|
{
|
||||||
|
"label": "Bridge Second Skin Speed",
|
||||||
|
"description": "Print speed to use when printing the second bridge skin layer.",
|
||||||
|
"unit": "mm/s",
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "cool_min_speed",
|
||||||
|
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||||
|
"maximum_value_warning": "300",
|
||||||
|
"default_value": 25,
|
||||||
|
"value": "bridge_skin_speed",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_material_flow_2":
|
||||||
|
{
|
||||||
|
"label": "Bridge Second Skin Flow",
|
||||||
|
"description": "When printing the second bridge skin layer, the amount of material extruded is multiplied by this value.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 100,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"maximum_value": "500",
|
||||||
|
"minimum_value_warning": "50",
|
||||||
|
"maximum_value_warning": "150",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_density_2":
|
||||||
|
{
|
||||||
|
"label": "Bridge Second Skin Density",
|
||||||
|
"description": "The density of the second bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 75,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"minimum_value_warning": "20",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_fan_speed_2":
|
||||||
|
{
|
||||||
|
"label": "Bridge Second Skin Fan Speed",
|
||||||
|
"description": "Percentage fan speed to use when printing the second bridge skin layer.",
|
||||||
|
"unit": "%",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"default_value": 0,
|
||||||
|
"type": "float",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_speed_3":
|
||||||
|
{
|
||||||
|
"label": "Bridge Third Skin Speed",
|
||||||
|
"description": "Print speed to use when printing the third bridge skin layer.",
|
||||||
|
"unit": "mm/s",
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "cool_min_speed",
|
||||||
|
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
|
||||||
|
"maximum_value_warning": "300",
|
||||||
|
"default_value": 15,
|
||||||
|
"value": "bridge_skin_speed",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_material_flow_3":
|
||||||
|
{
|
||||||
|
"label": "Bridge Third Skin Flow",
|
||||||
|
"description": "When printing the third bridge skin layer, the amount of material extruded is multiplied by this value.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 110,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"maximum_value": "500",
|
||||||
|
"minimum_value_warning": "50",
|
||||||
|
"maximum_value_warning": "150",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_skin_density_3":
|
||||||
|
{
|
||||||
|
"label": "Bridge Third Skin Density",
|
||||||
|
"description": "The density of the third bridge skin layer. Values less than 100 will increase the gaps between the skin lines.",
|
||||||
|
"unit": "%",
|
||||||
|
"default_value": 80,
|
||||||
|
"type": "float",
|
||||||
|
"minimum_value": "5",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"minimum_value_warning": "20",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
|
},
|
||||||
|
"bridge_fan_speed_3":
|
||||||
|
{
|
||||||
|
"label": "Bridge Third Skin Fan Speed",
|
||||||
|
"description": "Percentage fan speed to use when printing the third bridge skin layer.",
|
||||||
|
"unit": "%",
|
||||||
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "100",
|
||||||
|
"default_value": 0,
|
||||||
|
"type": "float",
|
||||||
|
"enabled": "bridge_settings_enabled and bridge_enable_more_layers",
|
||||||
|
"settable_per_mesh": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
4
run_in_docker.sh
Normal file
4
run_in_docker.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
Xvfb :1 -screen 0 1280x800x16 &
|
||||||
|
export DISPLAY=:1.0
|
||||||
|
python3 cura_app.py --headless
|
Loading…
Add table
Add a link
Reference in a new issue