Merge branch 'master' into master

This commit is contained in:
Eryone 2021-03-30 09:19:19 +08:00 committed by GitHub
commit 0e7a5cab8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
505 changed files with 54830 additions and 1106 deletions

View file

@ -1,49 +0,0 @@
---
name: Bug report
about: Create a report to help us fix issues.
title: ''
labels: 'Type: Bug'
assignees: ''
---
<!--
Processing an issue will go much faster when this is filled out, and issues which do not use this template WILL BE REMOVED and no fix will be considered!
Before filing, PLEASE check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
Also, please note the application version in the title of the issue. For example: "[3.2.1] Cannot connect to 3rd-party printer". Please do NOT write things like "Request:" or "[BUG]" in the title; this is what labels are for.
Thank you for using Cura!
-->
**Application version**
(The version of the application this issue occurs with.)
**Platform**
(Information about the operating system the issue occurs on. Include at least the operating system and maybe GPU.)
**Printer**
(Which printer was selected in Cura?)
**Reproduction steps**
1. (Something you did.)
2. (Something you did next.)
**Screenshot(s)**
(Image showing the problem, perhaps before/after images.)
**Actual results**
(What happens after the above steps have been followed.)
**Expected results**
(What should happen after the above steps have been followed.)
**Project file**
(For slicing bugs, provide a project which clearly shows the bug, by going to File->Save Project. For big files you may need to use WeTransfer or similar file sharing sites. G-code files are not project files!)
**Log file**
(See https://github.com/Ultimaker/Cura#logging-issues to find the log file to upload, or copy a relevant snippet from it.)
**Additional information**
(Extra information relevant to the issue.)

68
.github/ISSUE_TEMPLATE/bugreport.yaml vendored Normal file
View file

@ -0,0 +1,68 @@
name: Bug Report
description: Create a report to help us fix issues.
labels: "Type: Bug"
issue_body: true
body:
- type: markdown
attributes:
value: |
**Thank you for using Cura and wanting to report a bug.**
Before filing, please check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
Also, please note the application version in the title of the issue "For example (3.2.1) Cannot connect to 3rd-party printer". Please do not write things like **Request** or **BUG** in the title, this is what labels are for.
- type: input
attributes:
label: Application Version
description: The version of Cura this issue occurs with.
placeholder: 4.8.0
validations:
required: true
- type: input
attributes:
label: Platform
description: Information about the operating system the issue occurs on. Include at least the operating system and maybe GPU.
placeholder: Windows 10
validations:
required: true
- type: input
attributes:
label: Printer
description: Which printer was selected in Cura?
placeholder: Ultimaker S5
validations:
required: true
- type: textarea
attributes:
label: Reproduction steps
description: Tell us what you did!
placeholder: |
1. Something you did
2. Something you did next
validations:
required: true
- type: textarea
attributes:
label: Actual results
description: What happens after the above steps have been followed.
validations:
required: true
- type: textarea
attributes:
label: Expected results
description: What should happen after the above steps have been followed.
validations:
required: true
- type: markdown
attributes:
value: |
## Additional information & file uploads
Please be sure to add the following files:
* For slicing issues, upload a **project file** that clearly shows the bug.
To save a project file go to `File -> Save project`. Please make sure to .zip your project file. For big files you may need to use WeTransfer or similar file sharing sites.
G-code files are not project files!
* **Screenshots** of showing the problem, perhaps before/after images.
* A **log file**, see [here](https://github.com/Ultimaker/Cura#logging-issues) how to find the log file.
You can add these files and additional information that is relevant to the issue in the comments below.

View file

@ -1,23 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Type: New Feature'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
(A clear and concise description of what the problem is. Ex. I'm always frustrated when [...])
**Describe the solution you'd like**
(A clear and concise description of what you want to happen. If possible, describe why you think this is a good solution.)
**Describe alternatives you've considered**
(A clear and concise description of any alternative solutions or features you've considered. Again, if possible, think about why these alternatives are not working out.)
**Affected users and/or printers**
(Who do you think will benefit from this? Is everyone going to benefit from these changes? Or specific kinds of users?)
**Additional context**
(Add any other context or screenshots about the feature request here.)

View file

@ -0,0 +1,46 @@
name: Feature Request
description: Suggest an idea for this project.
labels: "Type: New Feature"
issue_body: true
body:
- type: markdown
attributes:
value: |
**Thank you for using Cura and wanting to suggest a new feature.**
Before filing, please check if the feature request already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
Please do not write things like **Request** or **BUG** in the title, this is what labels are for.
- type: textarea
attributes:
label: Is your feature request related to a problem?
description: Please describe a clear and concise description of what the problem is.
placeholder: I'm always frustrated when...
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen. If possible, describe why you think this is a good solution.
placeholder: I believe this will solve...
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered. Again, if possible, think about why these alternatives are not working out.
placeholder: The alternatives I've considered are...
validations:
required: true
- type: textarea
attributes:
label: Affected users and/or printers
description: Who do you think will benefit from this? Is everyone going to benefit from these changes? Or specific kinds of users?
placeholder: It will affect...
validations:
required: true
- type: markdown
attributes:
value: |
## Additional information & file uploads
You can add pictures or files to visualize your feature request in the comments below.

View file

@ -28,7 +28,7 @@ set(CURA_CLOUD_ACCOUNT_API_ROOT "" CACHE STRING "Alternative Cura cloud account
set(CURA_MARKETPLACE_ROOT "" CACHE STRING "Alternative Marketplace location")
set(CURA_DIGITAL_FACTORY_URL "" CACHE STRING "Alternative Digital Factory location")
configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY)
configure_file(${CMAKE_SOURCE_DIR}/com.ultimaker.cura.desktop.in ${CMAKE_BINARY_DIR}/com.ultimaker.cura.desktop @ONLY)
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
@ -82,11 +82,11 @@ if(NOT APPLE AND NOT WIN32)
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/cura)
endif()
install(FILES ${CMAKE_BINARY_DIR}/cura.desktop
install(FILES ${CMAKE_BINARY_DIR}/com.ultimaker.cura.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
install(FILES ${CMAKE_SOURCE_DIR}/resources/images/cura-icon.png
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/)
install(FILES cura.appdata.xml
install(FILES com.ultimaker.cura.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo)
install(FILES cura.sharedmimeinfo
DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages/

View file

@ -28,7 +28,7 @@ Dependencies
Build scripts
-------------
Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.
Please check out [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions.
Running from Source
-------------

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2016 Richard Hughes <richard@hughsie.com> -->
<component type="desktop">
<id>cura.desktop</id>
<id>com.ultimaker.cura.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>LGPL-3.0 and CC-BY-SA-4.0</project_license>
<name>Cura</name>
@ -24,7 +24,9 @@
</ul>
</description>
<screenshots>
<screenshot type="default" width="1280" height="720">http://software.ultimaker.com/Cura.png</screenshot>
<screenshot type="default">
<image>https://raw.githubusercontent.com/Ultimaker/Cura/master/screenshot.png</image>
</screenshot>
</screenshots>
<url type="homepage">https://ultimaker.com/en/products/cura-software?utm_source=cura&amp;utm_medium=software&amp;utm_campaign=resources</url>
<translation type="gettext">Cura</translation>

View file

@ -81,7 +81,8 @@ class Account(QObject):
CLIENT_ID="um----------------------------ultimaker_cura",
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download "
"packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write "
"cura.printjob.read cura.printjob.write cura.mesh.read cura.mesh.write",
"library.project.read library.project.write cura.printjob.read cura.printjob.write "
"cura.mesh.read cura.mesh.write",
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root)

View file

@ -281,7 +281,7 @@ class BuildVolume(SceneNode):
continue
# If the entire node is below the build plate, still mark it as outside.
node_bounding_box = node.getBoundingBox()
if node_bounding_box and node_bounding_box.top < 0:
if node_bounding_box and node_bounding_box.top < 0 and not node.getParent().callDecoration("isGroup"):
node.setOutsideBuildArea(True)
continue
# Mark the node as outside build volume if the set extruder is disabled
@ -344,7 +344,12 @@ class BuildVolume(SceneNode):
# Mark the node as outside build volume if the set extruder is disabled
extruder_position = node.callDecoration("getActiveExtruderPosition")
if not self._global_container_stack.extruderList[int(extruder_position)].isEnabled:
try:
if not self._global_container_stack.extruderList[int(extruder_position)].isEnabled:
node.setOutsideBuildArea(True)
return
except IndexError:
# If the extruder doesn't exist, also mark it as unprintable.
node.setOutsideBuildArea(True)
return
@ -1063,7 +1068,14 @@ class BuildVolume(SceneNode):
adhesion_type = adhesion_override
if adhesion_type is None:
adhesion_type = container_stack.getProperty("adhesion_type", "value")
skirt_brim_line_width = self._global_container_stack.getProperty("skirt_brim_line_width", "value")
# Skirt_brim_line_width is a bit of an odd one out. The primary bit of the skirt/brim is printed
# with the adhesion extruder, but it also prints one extra line by all other extruders. As such, the
# setting does *not* have a limit_to_extruder setting (which means that we can't ask the global extruder what
# the value is.
adhesion_extruder = self._global_container_stack.getProperty("adhesion_extruder_nr", "value")
skirt_brim_line_width = self._global_container_stack.extruderList[int(adhesion_extruder)].getProperty("skirt_brim_line_width", "value")
initial_layer_line_width_factor = self._global_container_stack.getProperty("initial_layer_line_width_factor", "value")
# Use brim width if brim is enabled OR the prime tower has a brim.
if adhesion_type == "brim":

View file

@ -1,8 +1,9 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
import sys
import tempfile
import time
from typing import cast, TYPE_CHECKING, Optional, Callable, List, Any, Dict
@ -30,6 +31,7 @@ from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.Platform import Platform
from UM.PluginError import PluginNotFoundError
from UM.Preferences import Preferences
from UM.Qt.Bindings.FileProviderModel import FileProviderModel
from UM.Qt.QtApplication import QtApplication # The class we're inheriting from.
from UM.Resources import Resources
from UM.Scene.Camera import Camera
@ -601,6 +603,15 @@ class CuraApplication(QtApplication):
@pyqtSlot()
def closeApplication(self) -> None:
Logger.log("i", "Close application")
# Workaround: Before closing the window, remove the global stack.
# This is necessary because as the main window gets closed, hundreds of QML elements get updated which often
# request the global stack. However as the Qt-side of the Machine Manager is being dismantled, the conversion of
# the Global Stack to a QObject fails.
# If instead we first take down the global stack, PyQt will just convert `None` to `null` which succeeds, and
# the QML code then gets `null` as the global stack and can deal with that as it deems fit.
self.getMachineManager().setActiveMachine(None)
main_window = self.getMainWindow()
if main_window is not None:
main_window.close()
@ -822,6 +833,9 @@ class CuraApplication(QtApplication):
self._add_printer_pages_model_without_cancel.initialize(cancellable = False)
self._whats_new_pages_model.initialize()
# Initialize the FileProviderModel
self._file_provider_model.initialize(self._onFileProviderEnabledChanged)
# Detect in which mode to run and execute that mode
if self._is_headless:
self.runWithoutGUI()
@ -889,14 +903,14 @@ class CuraApplication(QtApplication):
diagonal = self.getBuildVolume().getDiagonalSize()
if diagonal < 1: #No printer added yet. Set a default camera distance for normal-sized printers.
diagonal = 375
camera.setPosition(Vector(-80, 250, 700) * diagonal / 375)
camera.setPosition(Vector(-80, 180, 700) * diagonal / 375)
camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d")
# Initialize camera tool
camera_tool = controller.getTool("CameraTool")
if camera_tool:
camera_tool.setOrigin(Vector(0, 100, 0))
camera_tool.setOrigin(Vector(0, 30, 0))
camera_tool.setZoomRange(0.1, 2000)
# Initialize camera animations
@ -1051,6 +1065,13 @@ class CuraApplication(QtApplication):
self._simple_mode_settings_manager = SimpleModeSettingsManager()
return self._simple_mode_settings_manager
@pyqtSlot(result = QObject)
def getFileProviderModel(self) -> FileProviderModel:
return self._file_provider_model
def _onFileProviderEnabledChanged(self):
self._file_provider_model.update()
def event(self, event):
"""Handle Qt events"""
@ -1256,10 +1277,11 @@ class CuraApplication(QtApplication):
if other_bb is not None:
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
if print_information:
print_information.setPreSliced(is_block_slicing_node)
self.getWorkspaceFileHandler().setEnabled(not is_block_slicing_node)
if not scene_bounding_box:
scene_bounding_box = AxisAlignedBox.Null
@ -1466,7 +1488,8 @@ class CuraApplication(QtApplication):
for file_name, nodes in objects_in_filename.items():
for node in nodes:
job = ReadMeshJob(file_name)
file_path = os.path.normpath(os.path.dirname(file_name))
job = ReadMeshJob(file_name, add_to_recent_files = file_path != tempfile.gettempdir()) # Don't add temp files to the recent files list
job._node = node # type: ignore
job.finished.connect(self._reloadMeshFinished)
if has_merged_nodes:
@ -1720,15 +1743,17 @@ class CuraApplication(QtApplication):
def log(self, msg):
Logger.log("d", msg)
openProjectFile = pyqtSignal(QUrl, arguments = ["project_file"]) # Emitted when a project file is about to open.
openProjectFile = pyqtSignal(QUrl, bool, arguments = ["project_file", "add_to_recent_files"]) # Emitted when a project file is about to open.
@pyqtSlot(QUrl, str, bool)
@pyqtSlot(QUrl, str)
@pyqtSlot(QUrl)
def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None):
def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None, add_to_recent_files: bool = True):
"""Open a local file
:param project_mode: How to handle project files. Either None(default): Follow user preference, "open_as_model"
or "open_as_project". This parameter is only considered if the file is a project file.
:param add_to_recent_files: Whether or not to add the file as an option to the Recent Files list.
"""
Logger.log("i", "Attempting to read file %s", file.toString())
if not file.isValid():
@ -1749,12 +1774,12 @@ class CuraApplication(QtApplication):
if is_project_file and project_mode == "open_as_project":
# open as project immediately without presenting a dialog
workspace_handler = self.getWorkspaceFileHandler()
workspace_handler.readLocalFile(file)
workspace_handler.readLocalFile(file, add_to_recent_files_hint = add_to_recent_files)
return
if is_project_file and project_mode == "always_ask":
# present a dialog asking to open as project or import models
self.callLater(self.openProjectFile.emit, file)
self.callLater(self.openProjectFile.emit, file, add_to_recent_files)
return
# Either the file is a model file or we want to load only models from project. Continue to load models.
@ -1790,7 +1815,7 @@ class CuraApplication(QtApplication):
if extension in self._non_sliceable_extensions:
self.deleteAll(only_selectable = False)
job = ReadMeshJob(f)
job = ReadMeshJob(f, add_to_recent_files = add_to_recent_files)
job.finished.connect(self._readMeshFinished)
job.start()
@ -1905,6 +1930,11 @@ class CuraApplication(QtApplication):
arrange(nodes_to_arrange, self.getBuildVolume(), fixed_nodes)
except:
Logger.logException("e", "Failed to arrange the models")
# Ensure that we don't have any weird floaty objects (CURA-7855)
for node in nodes_to_arrange:
node.translate(Vector(0, -node.getBoundingBox().bottom, 0), SceneNode.TransformSpace.World)
self.fileCompleted.emit(file_name)
def addNonSliceableExtension(self, extension):
@ -1921,7 +1951,7 @@ class CuraApplication(QtApplication):
try:
result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted
except Exception:
except:
Logger.logException("e", "Could not check file %s", file_url)
return False

View file

@ -88,8 +88,10 @@ class MaterialNode(ContainerNode):
variant = self.variant.variant_name)
else:
qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition)
for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type):
qualities.extend((quality for quality in qualities_any_material if quality.get("material") == material_metadata["base_file"]))
all_material_base_files = {material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)}
qualities.extend((quality for quality in qualities_any_material if quality.get("material") in all_material_base_files))
if not qualities: # No quality profiles found. Go by GUID then.
my_guid = self.guid

View file

@ -58,7 +58,7 @@ class AuthorizationHelpers:
:return: An AuthenticationResponse object.
"""
Logger.log("d", "Refreshing the access token.")
Logger.log("d", "Refreshing the access token for [%s]", self._settings.OAUTH_SERVER_URL)
data = {
"client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
"redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
@ -69,7 +69,9 @@ class AuthorizationHelpers:
try:
return self.parseTokenResponse(requests.post(self._token_url, data = data)) # type: ignore
except requests.exceptions.ConnectionError:
return AuthenticationResponse(success=False, err_message="Unable to connect to remote server")
return AuthenticationResponse(success = False, err_message = "Unable to connect to remote server")
except OSError as e:
return AuthenticationResponse(success = False, err_message = "Operating system is unable to set up a secure connection: {err}".format(err = str(e)))
@staticmethod
def parseTokenResponse(token_response: requests.models.Response) -> "AuthenticationResponse":
@ -108,7 +110,9 @@ class AuthorizationHelpers:
"""
try:
token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = {
check_token_url = "{}/check-token".format(self._settings.OAUTH_SERVER_URL)
Logger.log("d", "Checking the access token for [%s]", check_token_url)
token_request = requests.get(check_token_url, headers = {
"Authorization": "Bearer {}".format(access_token)
})
except requests.exceptions.ConnectionError:

View file

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import json
@ -241,13 +241,13 @@ class AuthorizationService:
self._unable_to_get_data_message = Message(i18n_catalog.i18nc("@info", "Unable to reach the Ultimaker account server."), title = i18n_catalog.i18nc("@info:title", "Warning"))
self._unable_to_get_data_message.show()
except ValueError:
except (ValueError, TypeError):
Logger.logException("w", "Could not load auth data from preferences")
def _storeAuthData(self, auth_data: Optional[AuthenticationResponse] = None) -> None:
"""Store authentication data in preferences."""
Logger.log("d", "Attempting to store the auth data")
Logger.log("d", "Attempting to store the auth data for [%s]", self._settings.OAUTH_SERVER_URL)
if self._preferences is None:
Logger.log("e", "Unable to save authentication data, since no preference has been set!")
return

View file

@ -221,6 +221,7 @@ class ContainerManager(QObject):
except OSError:
return {"status": "error", "message": "Unable to write to this location.", "path": file_url}
Logger.info("Successfully exported container to {path}".format(path = file_url))
return {"status": "success", "message": "Successfully exported container", "path": file_url}
@pyqtSlot(QUrl, result = "QVariantMap")

View file

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
@ -381,9 +381,10 @@ class CuraContainerRegistry(ContainerRegistry):
if profile_count > 1:
continue
# Only one profile found, this should not ever be the case, so that profile needs to be removed!
Logger.log("d", "Found an invalid quality_changes profile with the name %s. Going to remove that now", profile_name)
invalid_quality_changes = ContainerRegistry.getInstance().findContainersMetadata(name=profile_name)
self.removeContainer(invalid_quality_changes[0]["id"])
if invalid_quality_changes:
Logger.log("d", "Found an invalid quality_changes profile with the name %s. Going to remove that now", profile_name)
self.removeContainer(invalid_quality_changes[0]["id"])
@override(ContainerRegistry)
def _isMetadataValid(self, metadata: Optional[Dict[str, Any]]) -> bool:
@ -400,7 +401,9 @@ class CuraContainerRegistry(ContainerRegistry):
try:
if int(metadata["setting_version"]) != cura.CuraApplication.CuraApplication.SettingVersion:
return False
except ValueError: #Not parsable as int.
except ValueError: # Not parsable as int.
return False
except TypeError: # Expecting string input here, not e.g. list or anything.
return False
return True

View file

@ -1,4 +1,4 @@
# Copyright (c) 2016 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSlot, pyqtProperty, QObject, pyqtSignal, QRegExp
@ -23,7 +23,7 @@ class MachineNameValidator(QObject):
#Compute the validation regex for printer names. This is limited by the maximum file name length.
try:
filename_max_length = os.statvfs(Resources.getDataStoragePath()).f_namemax
except AttributeError: #Doesn't support statvfs. Probably because it's not a Unix system.
except (AttributeError, EnvironmentError): # Doesn't support statvfs. Probably because it's not a Unix system. Or perhaps there is no permission or it doesn't exist.
filename_max_length = 255 #Assume it's Windows on NTFS.
machine_name_max_length = filename_max_length - len("_current_settings.") - len(ContainerRegistry.getMimeTypeForContainer(InstanceContainer).preferredSuffix)
# Characters that urllib.parse.quote_plus escapes count for 12! So now

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import numpy
@ -42,8 +42,8 @@ class Snapshot:
"""
scene = Application.getInstance().getController().getScene()
active_camera = scene.getActiveCamera()
render_width, render_height = active_camera.getWindowSize()
active_camera = scene.getActiveCamera() or scene.findCamera("3d")
render_width, render_height = (width, height) if active_camera is None else active_camera.getWindowSize()
render_width = int(render_width)
render_height = int(render_height)
preview_pass = PreviewPass(render_width, render_height)

View file

@ -4,7 +4,6 @@
import json
import math
import os
import unicodedata
from typing import Dict, List, Optional, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot, QTimer
@ -301,10 +300,11 @@ class PrintInformation(QObject):
if self._base_name == "":
self._job_name = self.UNTITLED_JOB_NAME
self._is_user_specified_job_name = False
self._application.getController().getScene().clearMetaData()
self.jobNameChanged.emit()
return
base_name = self._stripAccents(self._base_name)
base_name = self._base_name
self._defineAbbreviatedMachineName()
# Only update the job name when it's not user-specified.
@ -400,11 +400,6 @@ class PrintInformation(QObject):
self._abbr_machine = self._application.getMachineManager().getAbbreviatedMachineName(active_machine_type_name)
def _stripAccents(self, to_strip: str) -> str:
"""Utility method that strips accents from characters (eg: â -> a)"""
return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
@pyqtSlot(result = "QVariantMap")
def getFeaturePrintTimes(self) -> Dict[str, Duration]:
result = {}

View file

@ -239,9 +239,6 @@ class WelcomePagesModel(ListModel):
{"id": "user_agreement",
"page_url": self._getBuiltinWelcomePagePath("UserAgreementContent.qml"),
},
{"id": "whats_new",
"page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"),
},
{"id": "data_collections",
"page_url": self._getBuiltinWelcomePagePath("DataCollectionsContent.qml"),
},
@ -259,13 +256,21 @@ class WelcomePagesModel(ListModel):
},
{"id": "add_cloud_printers",
"page_url": self._getBuiltinWelcomePagePath("AddCloudPrintersView.qml"),
"is_final_page": True, # If we end up in this page, the next button will close the dialog
"next_page_button_text": self._catalog.i18nc("@action:button", "Finish"),
"next_page_button_text": self._catalog.i18nc("@action:button", "Next"),
"next_page_id": "whats_new",
},
{"id": "machine_actions",
"page_url": self._getBuiltinWelcomePagePath("FirstStartMachineActionsContent.qml"),
"should_show_function": self.shouldShowMachineActions,
},
{"id": "whats_new",
"page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"),
"next_page_button_text": self._catalog.i18nc("@action:button", "Skip"),
},
{"id": "changelog",
"page_url": self._getBuiltinWelcomePagePath("ChangelogContent.qml"),
"next_page_button_text": self._catalog.i18nc("@action:button", "Finish"),
},
]
pages_to_show = all_pages_list

View file

@ -1,8 +1,12 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from .WelcomePagesModel import WelcomePagesModel
import os
from typing import Optional, Dict, List, Tuple
from PyQt5.QtCore import pyqtProperty, pyqtSlot
from UM.Logger import Logger
from UM.Resources import Resources
#
# This Qt ListModel is more or less the same the WelcomePagesModel, except that this model is only for showing the
@ -10,13 +14,84 @@ from .WelcomePagesModel import WelcomePagesModel
#
class WhatsNewPagesModel(WelcomePagesModel):
image_formats = [".png", ".jpg", ".jpeg", ".gif", ".svg"]
text_formats = [".txt", ".htm", ".html"]
image_key = "image"
text_key = "text"
@staticmethod
def _collectOrdinalFiles(resource_type: int, include: List[str]) -> Tuple[Dict[int, str], int]:
result = {} #type: Dict[int, str]
highest = -1
try:
folder_path = Resources.getPath(resource_type, "whats_new")
for _, _, files in os.walk(folder_path):
for filename in files:
basename = os.path.basename(filename)
base, ext = os.path.splitext(basename)
if ext not in include or not base.isdigit():
continue
page_no = int(base)
highest = max(highest, page_no)
result[page_no] = os.path.join(folder_path, filename)
except FileNotFoundError:
Logger.logException("w", "Could not find 'whats_new' folder for resource-type {0}".format(resource_type))
return result, highest
@staticmethod
def _loadText(filename: str) -> str:
result = ""
try:
with open(filename, "r", encoding="utf-8") as file:
result = file.read()
except OSError:
Logger.logException("w", "Could not open {0}".format(filename))
return result
def initialize(self) -> None:
self._pages = []
self._pages.append({"id": "whats_new",
"page_url": self._getBuiltinWelcomePagePath("WhatsNewContent.qml"),
"next_page_button_text": self._catalog.i18nc("@action:button", "Skip"),
"next_page_id": "changelog"
})
self._pages.append({"id": "changelog",
"page_url": self._getBuiltinWelcomePagePath("ChangelogContent.qml"),
"next_page_button_text": self._catalog.i18nc("@action:button", "Close"),
})
self.setItems(self._pages)
images, max_image = WhatsNewPagesModel._collectOrdinalFiles(Resources.Images, WhatsNewPagesModel.image_formats)
texts, max_text = WhatsNewPagesModel._collectOrdinalFiles(Resources.Texts, WhatsNewPagesModel.text_formats)
highest = max(max_image, max_text)
self._subpages = [] #type: List[Dict[str, Optional[str]]]
for n in range(0, highest + 1):
self._subpages.append({
WhatsNewPagesModel.image_key: None if n not in images else images[n],
WhatsNewPagesModel.text_key: None if n not in texts else self._loadText(texts[n])
})
if len(self._subpages) == 0:
self._subpages.append({WhatsNewPagesModel.text_key: "~ There Is Nothing New Under The Sun ~"})
def _getSubpageItem(self, page: int, item: str) -> Optional[str]:
if 0 <= page < self.subpageCount and item in self._subpages[page]:
return self._subpages[page][item]
else:
return None
@pyqtProperty(int, constant = True)
def subpageCount(self) -> int:
return len(self._subpages)
@pyqtSlot(int, result = str)
def getSubpageImageSource(self, page: int) -> str:
result = self._getSubpageItem(page, WhatsNewPagesModel.image_key)
return "file:///" + (result if result else Resources.getPath(Resources.Images, "cura-icon.png"))
@pyqtSlot(int, result = str)
def getSubpageText(self, page: int) -> str:
result = self._getSubpageItem(page, WhatsNewPagesModel.text_key)
return result if result else "* * *"
__all__ = ["WhatsNewPagesModel"]

View file

@ -226,6 +226,12 @@ if Platform.isLinux() and getattr(sys, "frozen", False):
import trimesh.exchange.load
os.environ["LD_LIBRARY_PATH"] = old_env
# WORKAROUND: Cura#5488
# When using the KDE qqc2-desktop-style, the UI layout is completely broken, and
# even worse, it crashes when switching to the "Preview" pane.
if Platform.isLinux():
os.environ["QT_QUICK_CONTROLS_STYLE"] = "default"
if ApplicationMetadata.CuraDebugMode:
ssl_conf = QSslConfiguration.defaultConfiguration()
ssl_conf.setPeerVerifyMode(QSslSocket.VerifyNone)

View file

@ -27,7 +27,7 @@ Note: The profiler front-end itself is quite "heavy" (ok, not optimised). It run
What the Profiler Sees
----------------------
The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and thier names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`.
The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and their names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`.
Note that not all slots are captured. Only those slots which belong to classes which use the `pyqtSlot` decorator from the `UM.FlameProfiler` module.

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

113
docs/scene/operations.md Normal file
View file

@ -0,0 +1,113 @@
# Operations and the OperationStack
Cura supports an operation stack. The `OperationStack` class maintains a history of the operations performed in Cura, which allows for undo and redo actions. Every operation registers itself in the stack. The OperationStuck supports the following functions:
* `push(operation)`: Pushes an operation in the stack and applies the operation. This function is called when an operation pushes itself in the stack.
* `undo()`: Reverses the actions performed by the last operation and reduces the current index of the stack.
* `redo()`: Applies the actions performed by the next operation in the stack and increments the current index of the stack.
* `getOperations()`: Returns a list of all the operations that are currently inside the OperationStack
* `canUndo()`: Indicates whether the index of the operation stack has reached the bottom of the stack, which means that there are no more operations to be undone.
* `canRedo()`: Indicates whether the index of the operation stack has reached the top of the stack, which means that there are no more operations to be redone.
**Note 1:** When consecutive operations are performed very quickly after each other, they are merged together at the top of the stack. This action ensures that these minor operation will be undone with one Undo keystroke (e.g. when moving the object around and you press and release the left mouse button really fast, it is considered as one move operation).
**Note 2:** When an operation is pushed in the middle of the stack, all operations above it are removed from the stack. This ensures that there won't be any "history branches" created.
### Operations
Every action that happens in the scene and affects one or multiple models is associated with a subclass of the `Operation` class and is it added to the `OperationStack`. The subclassed operations that can be found in Cura (excluding the ones from downloadable plugins) are the following:
* [GroupedOperation](#groupedoperation)
* [AddSceneNodeOperation](#addscenenodeoperation)
* [RemoveSceneNodeOperation](#removescenenodeoperation)
* [SetParentOperation](#setparentoperation)
* [SetTransformOperation](#settransformoperation)
* [SetObjectExtruderOperation](#setobjectextruderoperation)
* [GravityOperation](#gravityoperation)
* [PlatformPhysicsOperation](#platformphysicsoperation)
* [TranslateOperation](#translateoperation)
* [ScaleOperation](#scaleoperation)
* [RotateOperation](#rotateoperation)
* [MirrorOperation](#mirroroperation)
* [LayFlatOperation](#layflatoperation)
* [SetBuildPlateNumberOperation]()
### GroupedOperation
The `GroupedOperation` is an operation that groups several other operations together. The intent of this operation is to hide an underlying chain of operations from the user if they correspond to only one interaction with the user, such as an operation applied to multiple scene nodes or a re-arrangement of multiple items in the scene.
Once a `GroupedOperation` is pushed into the stack, it applies all of its children operations in one go. Similarly, when it is undone, it reverses all its children operations at once.
### AddSceneNodeOperation
The `AddSceneNodeOperation` is added to the stack whenever a mesh is loaded inside the `Scene`, either by a `FileReader` or by inserting a [Support Blocker](tools.md#supporteraser-tool) in an object.
### RemoveSceneNodeOperation
The `RemoveSceneNodeOperation` is added to the stack whenever a mesh is removed from the Scene by the user or when the user requests to clear the build plate (_Ctrl+D_).
### SetParentOperation
The `SetParentOperation` changes the parent of a node. It is primarily used when grouping (the group node is set as the nodes' parent) and ungrouping (the group's children's parent is set to the group's parent before the group node is deleted), or when a SupportEraser node is added to the scene (to set the selected object as the Eraser's parent).
### SetTransformOperation
The `SetTransformOperation` translates, rotates, and scales a node all at once. This operation accepts a transformation matrix, an orientation matrix, and a scale matrix, and it is used by the _"Reset All Model Positions"_ and _"Reset All Model Transformations"_ options in the right-click (context) menu.
### SetObjectExtruderOperation
This operation is used to set the extruder with which a certain object should be printed with. It adds a [SettingOverrideDecorator](scene.md#settingoverridedecorator) to the object (if it doesn't have any) and then sets the extruder number via the decoration function `node.callDecoration("setActiveExtruder", extruder_id)`.
### GravityOperation
The `GravityOperation` moves a scene node down to 0 on the y-axis. It is currently used by the _"Lay flat"_ and _"Select face to align to the build plate"_ actions of the `RotationTool` to ensure that the object will end up touching the build plate after the corresponding rotation operations have be done.
### PlatformPhysicsOperation
The `PlatformPhysicsOperation` is generated by the `PlatformPhysics` class and it is associated with the preferences _"Ensure models are kept apart"_ and _"Automatically drop models to the build plate"_. If any of these preferences is set to true, the `PlatformPhysics` class periodically checks to make sure that the two conditions are met and if not, it calculates the move vector for each of the nodes that will satisfy the conditions.
Once the move vectors have been computed, they are applied to the nodes through consecutive `PlatformPhysicsOperations`, whose job is to use the `translate` function on the nodes.
**Note:** When there are multiple nodes, multiple `PlatformPhysicsOperations` may be generated (all models may be moved to ensure they are kept apart). These operations eventually get merged together by the `OperationStack` due to the fact that the individual operations are applied very fast one after the other.
### TranslateOperation
The `TranslateOperation` applies a linear transformation on a node, moving the node in the scene. This operation is primarily linked to the [TranslateTool](tools.md#translatetool) but it is also used in other places around Cura, such as arranging objects on the build plate (Ctrl+R) and centering an object to the build plate (via the right-click context menu's _"Center Selected Model"_ option).
When an object is moved using the move tool handles, multiple translate operations are generated to make sure that the object is rendered properly while it is moved. These translate operations are merged together once the user releases the tool handle.
**Note:** Some functionalities may move (translate) nodes without generating a TranslateOperation (such as when a model with is imported from a 3mf into a certain position). This ensures that the moving of the object cannot be accidentally undone by the user.
### ScaleOperation
The `ScaleOperation` scales the selected scene node uniformly or non-uniformly. This operation is primarily generated by the [ScaleTool](tools.md#scaletool).
When an object is scaled using the scale tool handles, multiple scale operations are generated to make sure that the object is rendered properly while it is being resized. These scale operations are merged together once the user releases the tool handle.
**Note:** When the _"Scale extremely small models"_ or the _"Scale large models"_ preferences are enabled the model is scaled when it is inserted into the build plate but it **DOES NOT** generate a `ScaleOperation`. This ensures that Cura doesn't register the scaling as an action that can be undone and the user doesn't accidentally end up with a very big or very small model.
### RotateOperation
The `RotateOperation` rotates the selected scene node(s) according to a given rotation quaternion and, optionally, around a given point. This operation is primarily generated by the [RotationTool](tools.md#rotatetool). It is also used by the arrange algorithm, which may rotate some models to fit them in the build plate.
When an object is rotated using the rotate tool handles, multiple rotate operations are generated to make sure that the object is rendered properly while it is being rotated. These operations are merged together once the user releases the tool handle.
### MirrorOperation
The `MirrorOperation` mirrors the selected object. It is primarily associated with the [MirrorTool](tools.md#mirrortool) and allows for mirroring the object in a certain direction, using the `MirrorToolHandles`.
The `MirrorOperation` accepts a transformation matrix that should only define values on the diagonal of the matrix, and only the values 1 or -1. It allows for mirroring around the center of the object or around the axis origin. The latter isn't used that often.
### LayFlatOperation
The `LayFlatOperation` computes some orientation to hopefully lay the object flat on the build plate. It is generated by the `layFlat()` function of the [RotateTool](tools.md#rotatetool). Contrary to the other operations, the `LayFlatOperation` is computed in a separate thread through the `LayFlatJob` since it can be quite computationally expensive.
### SetBuildPlateNumberOperation
The `SetBuildPlateNumberOperation` is linked to a legacy feature which allowed the user to have multiple build plates open in Cura at the same time. With this operation it was possible to transfer a node to another build plate through the node's [BuildPlateDecorator](scene.md#buildplatedecorator) by calling the decoration `node.callDecoration("setBuildPlateNumber", new_build_plate_nr)`.
**Note:** Changing the active build plate is a disabled feature in Cura and it is intended to be completely removed (internal ticket: CURA-4975), along with the `SetBuildPlateNumberOperation`.

View file

@ -8,19 +8,209 @@ Cura's scene graph is a mere tree data structure. This tree contains all scene n
The main idea behind the scene tree is that each scene node has a transformation applied to it. The scene nodes can be nested beneath other scene nodes. The transformation of the parents is then also applied to the children. This way you can have scene nodes grouped together and transform the group as a whole. Since the transformations are all linear, this ensures that the elements of this group stay in the same relative position and orientation. It will look as if the whole group is a single object. This idea is very common for games where objects are often composed of multiple 3D models but need to move together as a whole. For Cura it is used to group objects together and to transform the collision area correctly.
Class Diagram
----
The following class diagram depicts the classes that interact with the Scene
![alt text](images/components_interacting_with_scene.jpg)
The scene lives in the Controller of the Application, and it is primarily interacting with SceneNode objects, which are the components of the Scene Graph.
A Typical Scene
----
Cura's scene has a few nodes that are always present, and a few nodes that are repeated for every object that the user loads onto their build plate. To give an idea of how a scene normally looks, this is an overview of a typical scene tree for Cura.
Cura's scene has a few nodes that are always present, and a few nodes that are repeated for every object that the user loads onto their build plate. The root of the scene graph is a SceneNode that lives inside the Scene and contains all the other children SceneNodes of the scene. Typically, inside the root you can find the SceneNodes that are always loaded (the Cameras, the [BuildVolume](build_volume.md), and the Platform), the objects that are loaded on the platform, and finally a ConvexHullNode for each object and each group of objects in the Scene.
* Root
* Camera
* [Build volume](build_volume.md)
* Platform
* Object 1
* Group 1
* Object 2
* Object 3
* Object 1 convex hull node
* Object 2 convex hull node
* Object 3 convex hull node
* Group 1 convex hull node
Let's take the following example Scene:
![scene_example.png](images/scene_example.jpg)
The scene graph in this case is the following:
![scene_example_scene_graph.png](images/scene_example_scene_graph.jpg)
**Note 1:** The Platform is actually a child of the BuildVolume.
**Note 2:** The ConvexHullNodes are not actually named after the object they decorate. Their names are used in the image to convey how the ConvexHullNodes are related to the objects in the scene.
**Note 3:** The CuraSceneNode that holds the layer data (inside the BuildVolume) is created and destroyed according to the availability of sliced layer data provided by the CuraEngine. See the [LayerDataDecorator](#layerdatadecorator) for more information.
Accessing SceneNodes in the Scene
----
SceneNodes can be accessed using a `BreadthFirstIterator` or a `DepthFirstIterator`. Each iterator traverses the scene graph and returns a Python iterator, which yields all the SceneNodes and their children.
``` python
for node in BreadthFirstIterator(scene.getRoot()):
# do stuff with the node
```
Example result when iterating the above scene graph:
```python
[i for i in BreadthFirstIterator(CuraApplication.getInstance().getController().getScene().getRoot()]
```
* 00 = {SceneNode} <SceneNode object: 'Root'>
* 01 = {BuildVolume} <BuildVolume object '0x2e35dbce108'>
* 02 = {Camera} <Camera object: '3d'>
* 03 = {CuraSceneNode} <CuraSceneNode object: 'Torus.stl'>
* 04 = {CuraSceneNode} <CuraSceneNode object: 'Group #1'>
* 05 = {Camera} <Camera object: 'snapshot'>
* 06 = {CuraSceneNode} <CuraSceneNode object: 'Star.stl'>
* 07 = {ConvexHullNode} <ConvexHullNode object: '0x2e3000def08'>
* 08 = {ConvexHullNode} <ConvexHullNode object: '0x2e36861bd88'>
* 09 = {ConvexHullNode} <ConvexHullNode object: '0x2e3000bd4c8'>
* 10 = {ConvexHullNode} <ConvexHullNode object: '0x2e35fbb62c8'>
* 11 = {ConvexHullNode} <ConvexHullNode object: '0x2e3000a0648'>
* 12 = {ConvexHullNode} <ConvexHullNode object: '0x2e30019d0c8'>
* 13 = {ConvexHullNode} <ConvexHullNode object: '0x2e3001a2dc8'>
* 14 = {Platform} <Platform object '0x2e35a001948'>
* 15 = {CuraSceneNode} <CuraSceneNode object: 'Group #2'>
* 16 = {CuraSceneNode} <CuraSceneNode object: 'Sphere.stl'>
* 17 = {CuraSceneNode} <CuraSceneNode object: 'Cylinder.stl'>
* 18 = {CuraSceneNode} <CuraSceneNode object: 'Cube.stl'>
SceneNodeDecorators
----
SceneNodeDecorators are decorators that can be added to the nodes of the scene to provide them with additional functions.
Cura provides the following classes derived from the SceneNodeDecorator class:
1. [GroupDecorator](#groupdecorator)
2. [ConvexHullDecorator](#convexhulldecorator)
3. [SettingOverrideDecorator](#settingoverridedecorator)
4. [SliceableObjectDecorator](#sliceableobjectdecorator)
5. [LayerDataDecorator](#layerdatadecorator)
6. [ZOffsetDecorator](#zoffsetdecorator)
7. [BlockSlicingDecorator](#blockslicingdecorator)
8. [GCodeListDecorator](#gcodelistdecorator)
9. [BuildPlateDecorator](#buildplatedecorator)
GroupDecorator
----
Whenever objects on the build plate are grouped together, a new node is added in the scene as the parent of the grouped objects. Group nodes can be identified when traversing the SceneGraph by running the following:
```python
node.callDecoration("isGroup") == True
```
Group nodes decorated by GroupDecorators are added in the scene either by reading project files which contain grouped objects, or when the user selects multiple objects and groups them together (Ctrl + G).
Group nodes that are left with only one child are removed from the scene, making their only child a child of the group's parent. In addition, group nodes without any remaining children are removed from the scene.
ConvexHullDecorator
----
As seen in the scene graph of the scene example, each CuraSceneNode that represents an object on the build plate is linked to a ConvexHullNode which is rendered as the object's shadow on the build plate. The ConvexHullDecorator is the link between these two nodes.
In essence, the CuraSceneNode has a ConvexHullDecorator which points to the ConvexHullNode of the object. The data of the object's convex hull can be accessed via
```python
convex_hull_polygon = object_node.callDecoration("getConvexHull")
```
The ConvexHullDecorator also provides convex hulls that include the head, the fans, and the adhesion of the object. These are primarily used and rendered when One-at-a-time mode is activated.
For more information on the functions added to the node by this decorator, visit the [ConvexHullDecorator.py](https://github.com/Ultimaker/Cura/blob/master/cura/Scene/ConvexHullDecorator.py).
SettingOverrideDecorator
----
SettingOverrideDecorators are primarily used for modifier meshes such as support meshes, cutting meshes, infill meshes, and anti-overhang meshes. When a user converts an object to a modifier mesh, the object's node is decorated by a SettingOverrideDecorator. This decorator adds a PerObjectContainerStack to the CuraSceneNode, which allows the user to modify the settings of the specific model.
For more information on the functions added to the node by this decorator, visit the [SettingOverrideDecorator.py](https://github.com/Ultimaker/Cura/blob/master/cura/Settings/SettingOverrideDecorator.py).
SliceableObjectDecorator
----
This is a convenience decorator that allows us to easily identify the nodes which can be sliced. All **individual** objects (meshes) added to the build plate receive this decorator, apart from the nodes loaded from GCode files (.gcode, .g, .gz, .ufp).
The SceneNodes that do not receive this decorator are:
- Cameras
- BuildVolume
- Platform
- ConvexHullNodes
- CuraSceneNodes that serve as group nodes (these have a GroupDecorator instead)
- The CuraSceneNode that serves as the layer data node
- ToolHandles
- NozzleNode
- Nodes that contain GCode data. See the [BlockSlicingDecorator](#blockslicingdecorator) for more information on that.
This decorator provides the following function to the node:
```python
node.callDecoration("isSliceable")
```
LayerDataDecorator
----
Once the Slicing has completed and the CuraEngine has returned the slicing data, Cura creates a CuraSceneNode inside the BuildVolume which is decorated by a LayerDataDecorator. This decorator holds the layer data of the scene.
![Layer Data Scene Node](images/layer_data_scene_node.jpg)
The layer data can be accessed through the function given to the aforementioned CuraSceneNode by the LayerDataDecorator:
```python
node.callDecoration("getLayerData")
```
This CuraSceneNode is created once Cura has completed processing the Layer data (after the user clicks on the Preview tab after slicing). The CuraSceneNode then is destroyed once any action that changes the Scene occurs (e.g. if the user moves/rotates/scales an object or changes a setting value), indicating that the layer data is no longer available. When that happens, the "Slice" button becomes available again.
ZOffsetDecorator
----
The ZOffsetDecorator is added to an object in the scene when that object is moved below the build plate. It is primarily used when the "Automatically drop models to the build plate" preference is enabled, in order to make sure that the GravityOperation, which drops the mode on the build plate, is not applied when the object is moved under the build plate.
The amount the object is moved under the build plate can be retrieved by calling the "getZOffset" decoration on the node:
```python
z_offset = node.callDecoration("getZOffset")
```
The ZOffsetDecorator is removed from the node when the node is move above the build plate.
BlockSlicingDecorator
----
The BlockSlicingDecorator is the opposite of the SliceableObjectDecorator. It is added on objects loaded on the scene which should not be sliced. This decorator is primarily added on objects loaded from ".gcode", ".ufp", ".g", and ".gz" files. Such an object already contains all the slice information and therefore should not allow Cura to slice it.
If an object with a BlockSlicingDecorator appears in the scene, the backend (CuraEngine) and the print setup (changing print settings) become disabled, considering that G-code files cannot be modified.
The BlockSlicingDecorator adds the following decoration function to the node:
```python
node.callDecoration("isBlockSlicing")
```
GCodeListDecorator
----
The GCodeListDecorator is also added only when a file containing GCode is loaded in the scene. It's purpose is to hold a list of all the GCode data of the loaded object.
The GCode list data is stored in the scene's gcode_dict attribute which then is used in other places in the Cura code, e.g. to provide the GCode to the GCodeWriter or to the PostProcessingPlugin.
The GCode data becomes available by calling the "getGCodeList" decoration of the node:
```python
gcode_list = node.callDecoration("getGCodeList")
```
The CuraSceneNode with the GCodeListDecorator is destroyed when another object or project file is loaded in the Scene.
BuildPlateDecorator
----
The BuildPlateDecorator is added to all the CuraSceneNodes. This decorator is linked to a legacy feature which allowed the user to have multiple build plates open in Cura at the same time. With this decorator it was possible to determine which nodes are present on each build plate, and therefore, which objects should be visible in the currently active build plate. It indicates the number of the build plate this scene node belongs to, which currently is always the build plate -1.
This decorator provides a function to the node that returns the number of the build plate it belongs to:
```python
node.callDecoration("getBuildPlateNumber")
```
**Note:** Changing the active build plate is a disabled feature in Cura and it is intended to be completely removed (internal ticket: CURA-4975).

86
docs/scene/tools.md Normal file
View file

@ -0,0 +1,86 @@
# Tools
Tools are plugin objects which are used to manipulate or interact with the scene and the objects (node) in the scene.
![Class diagram of tools in the scene](images/tools_tool-handles_class_diagram.jpg)
Tools live inside the Controller of the Application and may be associated with ToolHandles. Some of them interact with the scene as a whole (such as the Camera), while others interact with the objects (nodes) in the Scene (selection tool, rotate tool, scale tool etc.). The tools that are available in Cura (excluding the ones provided by downloadable plugins) are the following:
* [CameraTool](#cameratool)
* [SelectionTool](#selectiontool)
* [TranslateTool](#translatetool)
* [ScaleTool](#scaletool)
* [RotateTool](#rotatetool)
* [MirrorTool](#mirrortool)
* [PerObjectSettingsTool](#perobjectsettingstool)
* [SupportEraserTool](#supporteraser)
*****
### CameraTool
The CameraTool is the tool that allows the user to manipulate the Camera. It provides the functions of moving, zooming, and rotating the Camera. This tool does not contain a handle.
### SelectionTool
This tool allows the user to select objects and groups of objects in the scene. The selected objects gain a blue outline and become available in the code through the Selection class.
![Selection Tool](images/selection_tool.jpg)
This tool does not contain a handle.
### TranslateTool
This tool allows the user to move the object around the build plate. The TranslateTool is activated once the user presses the Move icon in the tool sidebar or hits the shortcut (T) while an object is selected.
![Translate Tool](images/translate_tool.jpg)
The TranslateTool contains the TranslateToolHandle, which draws the arrow handles on the selected object(s). The TranslateTool generates TranslateOperations whenever the object is moved around the build plate.
### ScaleTool
This tool allows the user to scale the selected object(s). The ScaleTool is activated once the user presses the Scale icon in the tool sidebar or hits the shortcut (S) while an object is selected.
![Scale Tool](images/scale_tool.jpg)
The ScaleTool contains the ScaleToolHandle, which draws the box handles on the selected object(s). The ScaleTool generates ScaleOperations whenever the object is scaled.
### RotateTool
This tool allows the user to rotate the selected object(s). The RotateTool is activated once the user presses the Rotate icon in the tool sidebar or hits the shortcut (R) while an object is selected.
![Rotate Tool](images/rotate_tool.jpg)
The RotateTool contains the RotateToolHandle, which draws the donuts (tori) and arrow handles on the selected object(s). The RotateTool generates RotateOperations whenever the object is rotated or if a face is selected to be laid flat on the build plate. It also contains the `layFlat()` action, which generates the [LayFlatOperation](operations.md#layflatoperation).
### MirrorTool
This tool allows the user to mirror the selected object(s) in the required direction. The MirrorTool is activated once the user presses the Mirror icon in the tool sidebar or hits the shortcut (M) while an object is selected.
![Mirror Tool](images/mirror_tool.jpg)
The MirrorTool contains the MirrorToolHandle, which draws pyramid handles on the selected object(s). The MirrorTool generates MirrorOperations whenever the object is mirrored against an axis.
### PerObjectSettingsTool
This tool allows the user to change the mesh type of the object into one of the following:
* Normal Model
* Print as support
* Modify settings for overlaps
- Infill mesh only
- Cutting mesh
* Don't support overlaps
![Per object settings tool](images/per_objectsettings_tool.jpg)
Contrary to other tools, this tool doesn't have any handles and it does not generate any operations. This means that once an object's type is changed it cannot be undone/redone using the OperationStack. This tool adds a [SettingOverrideDecorator](scene.md#settingoverridedecorator) on the object's node instead, which allows the user to change certain settings only for this mesh.
### SupportEraser tool
This tool allows the user to add support blockers on the selected model. The SupportEraserTool is activated once the user pressed the Support Blocker icon in the tool sidebar or hits the shortcut (E) while an object is selected. With this tool active, the user can add support blockers (cubes) on the object by clicking on various places on the selected mesh.
![Support Eraser Tool](images/support_blocker_tool.jpg)
The SupportEraser uses a GroupOperation to add a new CuraSceneNode (the eraser) in the scene and set the selected model as the parent of the eraser. This means that the addition of Erasers in the scene can be undone/redone. The SupportEraser does not have any tool handles.

View file

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
@ -51,6 +51,10 @@ class ThreeMFReader(MeshReader):
self._root = None
self._base_name = ""
self._unit = None
self._empty_project = False
def emptyFileHintSet(self) -> bool:
return self._empty_project
def _createMatrixFromTransformationString(self, transformation: str) -> Matrix:
if transformation == "":
@ -159,9 +163,9 @@ class ThreeMFReader(MeshReader):
um_node.callDecoration("getStack").getTop().setDefinition(definition_id)
setting_container = um_node.callDecoration("getStack").getTop()
known_setting_keys = um_node.callDecoration("getStack").getAllKeys()
for key in settings:
setting_value = settings[key]
setting_value = settings[key].value
# Extruder_nr is a special case.
if key == "extruder_nr":
@ -171,7 +175,10 @@ class ThreeMFReader(MeshReader):
else:
Logger.log("w", "Unable to find extruder in position %s", setting_value)
continue
setting_container.setProperty(key, "value", setting_value)
if key in known_setting_keys:
setting_container.setProperty(key, "value", setting_value)
else:
um_node.metadata[key] = settings[key]
if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None:
if len(um_node.getAllChildren()) == 1:
@ -193,6 +200,7 @@ class ThreeMFReader(MeshReader):
return um_node
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
self._empty_project = False
result = []
# The base object of 3mf is a zipped archive.
try:
@ -201,6 +209,10 @@ class ThreeMFReader(MeshReader):
parser = Savitar.ThreeMFParser()
scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
self._unit = scene_3mf.getUnit()
for key, value in scene_3mf.getMetadata().items():
CuraApplication.getInstance().getController().getScene().setMetaDataEntry(key, value)
for node in scene_3mf.getSceneNodes():
um_node = self._convertSavitarNodeToUMNode(node, file_name)
if um_node is None:
@ -257,6 +269,9 @@ class ThreeMFReader(MeshReader):
result.append(um_node)
if len(result) == 0:
self._empty_project = True
except Exception:
Logger.logException("e", "An exception occurred in 3mf reader.")
return []

View file

@ -14,6 +14,7 @@ from cura.CuraApplication import CuraApplication
import Savitar
import numpy
import datetime
MYPY = False
try:
@ -108,7 +109,11 @@ class ThreeMFWriter(MeshWriter):
# Get values for all changed settings & save them.
for key in changed_setting_keys:
savitar_node.setSetting(key, str(stack.getProperty(key, "value")))
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
# Store the metadata.
for key, value in um_node.metadata.items():
savitar_node.setSetting(key, value)
for child_node in um_node.getChildren():
# only save the nodes on the active build plate
@ -145,6 +150,22 @@ class ThreeMFWriter(MeshWriter):
model_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/3D/3dmodel.model", Id = "rel0", Type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")
savitar_scene = Savitar.Scene()
metadata_to_store = CuraApplication.getInstance().getController().getScene().getMetaData()
for key, value in metadata_to_store.items():
savitar_scene.setMetaDataEntry(key, value)
current_time_string = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if "Application" not in metadata_to_store:
# This might sound a bit strange, but this field should store the original application that created
# the 3mf. So if it was already set, leave it to whatever it was.
savitar_scene.setMetaDataEntry("Application", CuraApplication.getInstance().getApplicationDisplayName())
if "CreationDate" not in metadata_to_store:
savitar_scene.setMetaDataEntry("CreationDate", current_time_string)
savitar_scene.setMetaDataEntry("ModificationDate", current_time_string)
transformation_matrix = Matrix()
transformation_matrix._data[1, 1] = 0
transformation_matrix._data[1, 2] = -1

View file

@ -43,6 +43,10 @@ class DriveApiService:
return
backup_list_response = HttpRequestManager.readJSON(reply)
if backup_list_response is None:
Logger.error("List of back-ups can't be parsed.")
changed([])
return
if "data" not in backup_list_response:
Logger.log("w", "Could not get backups from remote, actual response body was: %s",
str(backup_list_response))

View file

@ -69,7 +69,7 @@ Item
BackupListFooter
{
id: backupListFooter
showInfoButton: backupList.model.length > 4
}
}
}

View file

@ -1,4 +1,4 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import argparse #To run the engine in debug mode if the front-end is in debug mode.
@ -9,6 +9,8 @@ import sys
from time import time
from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING
from PyQt5.QtGui import QImage
from UM.Backend.Backend import Backend, BackendState
from UM.Scene.SceneNode import SceneNode
from UM.Signal import Signal
@ -24,6 +26,8 @@ from UM.Tool import Tool #For typing.
from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread
from .ProcessSlicedLayersJob import ProcessSlicedLayersJob
from .StartSliceJob import StartSliceJob, StartJobResult
@ -153,6 +157,8 @@ class CuraEngineBackend(QObject, Backend):
self.determineAutoSlicing()
application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
self._snapshot = None #type: Optional[QImage]
application.initializationFinished.connect(self.initialize)
def initialize(self) -> None:
@ -241,9 +247,27 @@ class CuraEngineBackend(QObject, Backend):
self.markSliceAll()
self.slice()
@call_on_qt_thread # must be called from the main thread because of OpenGL
def _createSnapshot(self) -> None:
self._snapshot = None
if not CuraApplication.getInstance().isVisible:
Logger.log("w", "Can't create snapshot when renderer not initialized.")
return
Logger.log("i", "Creating thumbnail image (just before slice)...")
try:
self._snapshot = Snapshot.snapshot(width = 300, height = 300)
except:
Logger.logException("w", "Failed to create snapshot image")
self._snapshot = None # Failing to create thumbnail should not fail creation of UFP
def getLatestSnapshot(self) -> Optional[QImage]:
return self._snapshot
def slice(self) -> None:
"""Perform a slice of the scene."""
self._createSnapshot()
Logger.log("i", "Starting to slice...")
self._slice_start_time = time()
if not self._build_plates_to_be_sliced:
@ -331,7 +355,6 @@ class CuraEngineBackend(QObject, Backend):
def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
"""Event handler to call when the job to initiate the slicing process is
completed.
When the start slice job is successfully completed, it will be happily

View file

@ -43,7 +43,7 @@ UM.Dialog
TextField {
id: peak_height
objectName: "Peak_Height"
validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/}
validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onPeakHeightChanged(text) }
}
@ -66,7 +66,7 @@ UM.Dialog
TextField {
id: base_height
objectName: "Base_Height"
validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/}
validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/}
width: 180 * screenScaleFactor
onTextChanged: { manager.onBaseHeightChanged(text) }
}

View file

@ -52,11 +52,8 @@ class ImageReader(MeshReader):
def _generateSceneNode(self, file_name, xz_size, height_from_base, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm):
scene_node = SceneNode()
mesh = MeshBuilder()
img = QImage(file_name)
if img.isNull():
Logger.log("e", "Image is corrupt.")
return None
@ -70,11 +67,10 @@ class ImageReader(MeshReader):
height_from_base = max(height_from_base, 0)
base_height = max(base_height, 0)
peak_height = base_height + height_from_base
xz_size = max(xz_size, 1)
scale_vector = Vector(xz_size, peak_height, xz_size)
scale_vector = Vector(xz_size, height_from_base, xz_size)
if width > height:
scale_vector = scale_vector.set(z=scale_vector.z * aspect)
@ -132,7 +128,7 @@ class ImageReader(MeshReader):
if use_transparency_model:
divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel.
min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height)
min_luminance = (transmittance_1mm / 100.0) ** height_from_base
for (y, x) in numpy.ndindex(height_data.shape):
mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x]
height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this

View file

@ -137,6 +137,7 @@ Item
labelWidth: base.labelWidth
controlWidth: base.controlWidth
unitText: ""
decimals: 0
forceUpdateOnChangeFunction: forceUpdateFunction
}
}

View file

@ -330,6 +330,25 @@ Item
}
}
/*
- Fix for this issue: https://github.com/Ultimaker/Cura/issues/9167
- Allows user to toggle if GCODE coordinates are affected by the extruder offset.
- Machine wide setting. CuraEngine/src/gcodeExport.cpp is not set up to evaluate per extruder currently.
- If it is moved to per-extruder (unlikely), then this should be moved to the extruder tab.
*/
Cura.SimpleCheckBox // "GCode Affected By Extruder Offsets"
{
id: applyExtruderOffsetsCheckbox
containerStackId: machineStackId
settingKey: "machine_use_extruder_offset_to_offset_coords"
settingStoreIndex: propertyStoreIndex
labelText: catalog.i18nc("@label", "Apply Extruder offsets to GCode")
labelFont: base.labelFont
labelWidth: base.labelWidth
forceUpdateOnChangeFunction: forceUpdateFunction
}
/* The "Shared Heater" feature is temporarily disabled because its
implementation is incomplete. Printers with multiple filaments going
into one nozzle will keep the inactive filaments retracted at the

View file

@ -237,7 +237,7 @@ Item
id: settingLoader
width: UM.Theme.getSize("setting").width
height: UM.Theme.getSize("section").height
enabled: provider.properties.enabled === "True"
property var definition: model
property var settingDefinitionsModel: addedSettingsModel
property var propertyProvider: provider

View file

@ -142,7 +142,9 @@ class PostProcessingPlugin(QObject, Extension):
# The PostProcessingPlugin path is for built-in scripts.
# The Resources path is where the user should store custom scripts.
# The Preferences path is legacy, where the user may previously have stored scripts.
for root in [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Resources), Resources.getStoragePath(Resources.Preferences)]:
resource_folders = [PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), Resources.getStoragePath(Resources.Preferences)]
resource_folders.extend(Resources.getAllPathsForType(Resources.Resources))
for root in resource_folders:
if root is None:
continue
path = os.path.join(root, "scripts")

View file

@ -774,15 +774,15 @@ class ChangeAtZProcessor:
# looking for wait for bed temp
if "bedTemp" in values:
codes.append("BedTemp: " + str(values["bedTemp"]))
codes.append("BedTemp: " + str(round(values["bedTemp"])))
# set our extruder one temp (if specified)
if "extruderOne" in values:
codes.append("Extruder 1 Temp: " + str(values["extruderOne"]))
codes.append("Extruder 1 Temp: " + str(round(values["extruderOne"])))
# set our extruder two temp (if specified)
if "extruderTwo" in values:
codes.append("Extruder 2 Temp: " + str(values["extruderTwo"]))
codes.append("Extruder 2 Temp: " + str(round(values["extruderTwo"])))
# set global flow rate
if "flowrate" in values:

View file

@ -0,0 +1,109 @@
import base64
from UM.Logger import Logger
from cura.Snapshot import Snapshot
from PyQt5.QtCore import QByteArray, QIODevice, QBuffer
from ..Script import Script
class CreateThumbnail(Script):
def __init__(self):
super().__init__()
def _createSnapshot(self, width, height):
Logger.log("d", "Creating thumbnail image...")
try:
return Snapshot.snapshot(width, height)
except Exception:
Logger.logException("w", "Failed to create snapshot image")
def _encodeSnapshot(self, snapshot):
Logger.log("d", "Encoding thumbnail image...")
try:
thumbnail_buffer = QBuffer()
thumbnail_buffer.open(QBuffer.ReadWrite)
thumbnail_image = snapshot
thumbnail_image.save(thumbnail_buffer, "PNG")
base64_bytes = base64.b64encode(thumbnail_buffer.data())
base64_message = base64_bytes.decode('ascii')
thumbnail_buffer.close()
return base64_message
except Exception:
Logger.logException("w", "Failed to encode snapshot image")
def _convertSnapshotToGcode(self, encoded_snapshot, width, height, chunk_size=78):
gcode = []
encoded_snapshot_length = len(encoded_snapshot)
gcode.append(";")
gcode.append("; thumbnail begin {} {} {}".format(
width, height, encoded_snapshot_length))
chunks = ["; {}".format(encoded_snapshot[i:i+chunk_size])
for i in range(0, len(encoded_snapshot), chunk_size)]
gcode.extend(chunks)
gcode.append("; thumbnail end")
gcode.append(";")
gcode.append("")
return gcode
def getSettingDataString(self):
return """{
"name": "Create Thumbnail",
"key": "CreateThumbnail",
"metadata": {},
"version": 2,
"settings":
{
"width":
{
"label": "Width",
"description": "Width of the generated thumbnail",
"unit": "px",
"type": "int",
"default_value": 32,
"minimum_value": "0",
"minimum_value_warning": "12",
"maximum_value_warning": "800"
},
"height":
{
"label": "Height",
"description": "Height of the generated thumbnail",
"unit": "px",
"type": "int",
"default_value": 32,
"minimum_value": "0",
"minimum_value_warning": "12",
"maximum_value_warning": "600"
}
}
}"""
def execute(self, data):
width = self.getSettingValueByKey("width")
height = self.getSettingValueByKey("height")
snapshot = self._createSnapshot(width, height)
if snapshot:
encoded_snapshot = self._encodeSnapshot(snapshot)
snapshot_gcode = self._convertSnapshotToGcode(
encoded_snapshot, width, height)
for layer in data:
layer_index = data.index(layer)
lines = data[layer_index].split("\n")
for line in lines:
if line.startswith(";Generated with Cura"):
line_index = lines.index(line)
insert_index = line_index + 1
lines[insert_index:insert_index] = snapshot_gcode
break
final_lines = "\n".join(lines)
data[layer_index] = final_lines
return data

View file

@ -182,8 +182,7 @@ class PauseAtHeight(Script):
"Repetier": "Repetier"
},
"default_value": "RepRap (Marlin/Sprinter)",
"enabled": false,
"default_value": ""
"enabled": false
},
"custom_gcode_before_pause":
{
@ -339,11 +338,6 @@ class PauseAtHeight(Script):
if current_layer < pause_layer - nbr_negative_layers:
continue
# Get X and Y from the next layer (better position for
# the nozzle)
next_layer = data[index + 1]
x, y = self.getNextXY(next_layer)
prev_layer = data[index - 1]
prev_lines = prev_layer.split("\n")
current_e = 0.
@ -354,6 +348,13 @@ class PauseAtHeight(Script):
current_e = self.getValue(prevLine, "E", -1)
if current_e >= 0:
break
# and also find last X,Y
for prevLine in reversed(prev_lines):
if prevLine.startswith(("G0", "G1", "G2", "G3")):
if self.getValue(prevLine, "X") is not None and self.getValue(prevLine, "Y") is not None:
x = self.getValue(prevLine, "X")
y = self.getValue(prevLine, "Y")
break
# Maybe redo the last layer.
if redo_layer:
@ -455,7 +456,7 @@ class PauseAtHeight(Script):
prepend_gcode += self.putValue(G = 1, E = -retraction_amount, F = 6000) + "\n"
#Move the head back
prepend_gcode += self.putValue(G = 1, Z = current_z + 1, F = 300) + "\n"
prepend_gcode += self.putValue(G = 1, Z = current_z, F = 300) + "\n"
prepend_gcode += self.putValue(G = 1, X = x, Y = y, F = 9000) + "\n"
if retraction_amount != 0:
prepend_gcode += self.putValue(G = 1, E = retraction_amount, F = 6000) + "\n"

View file

@ -15,9 +15,10 @@ from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
from cura.Settings.ExtruderManager import ExtruderManager
from cura.LayerPolygon import LayerPolygon
import os.path
import numpy
## RenderPass used to display g-code paths.
from .NozzleNode import NozzleNode
@ -35,7 +36,7 @@ class SimulationPass(RenderPass):
self._disabled_shader = None
self._old_current_layer = 0
self._old_current_path = 0
self._switching_layers = True # It tracks when the user is moving the layers' slider
self._switching_layers = True # Tracking whether the user is moving across layers (True) or across paths (False). If false, lower layers render as shadowy.
self._gl = OpenGL.getInstance().getBindingsObject()
self._scene = Application.getInstance().getController().getScene()
self._extruder_manager = ExtruderManager.getInstance()
@ -43,6 +44,8 @@ class SimulationPass(RenderPass):
self._layer_view = None
self._compatibility_mode = None
self._scene.sceneChanged.connect(self._onSceneChanged)
def setSimulationView(self, layerview):
self._layer_view = layerview
self._compatibility_mode = layerview.getCompatibilityMode()
@ -60,29 +63,38 @@ class SimulationPass(RenderPass):
self._current_shader = self._layer_shader
# Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex)))
if not self._compatibility_mode:
self._layer_shader.setUniformValue("u_starts_color", Color(*Application.getInstance().getTheme().getColor("layerview_starts").getRgb()))
if self._layer_view:
self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
self._layer_shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
self._layer_shader.setUniformValue("u_max_line_width", self._layer_view.getMaxLineWidth())
self._layer_shader.setUniformValue("u_min_line_width", self._layer_view.getMinLineWidth())
self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
self._layer_shader.setUniformValue("u_show_starts", self._layer_view.getShowStarts())
else:
#defaults
self._layer_shader.setUniformValue("u_max_feedrate", 1)
self._layer_shader.setUniformValue("u_min_feedrate", 0)
self._layer_shader.setUniformValue("u_max_thickness", 1)
self._layer_shader.setUniformValue("u_min_thickness", 0)
self._layer_shader.setUniformValue("u_max_line_width", 1)
self._layer_shader.setUniformValue("u_min_line_width", 0)
self._layer_shader.setUniformValue("u_layer_view_type", 1)
self._layer_shader.setUniformValue("u_extruder_opacity", [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
self._layer_shader.setUniformValue("u_show_travel_moves", 0)
self._layer_shader.setUniformValue("u_show_helpers", 1)
self._layer_shader.setUniformValue("u_show_skin", 1)
self._layer_shader.setUniformValue("u_show_infill", 1)
self._layer_shader.setUniformValue("u_show_starts", 1)
if not self._tool_handle_shader:
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
@ -161,6 +173,13 @@ class SimulationPass(RenderPass):
self._current_shader = self._layer_shader
self._switching_layers = True
# The first line does not have a previous line: add a MoveCombingType in front for start detection
# this way the first start of the layer can also be drawn
prev_line_types = numpy.concatenate([numpy.asarray([LayerPolygon.MoveCombingType], dtype = numpy.float32), layer_data._attributes["line_types"]["value"]])
# Remove the last element
prev_line_types = prev_line_types[0:layer_data._attributes["line_types"]["value"].size]
layer_data._attributes["prev_line_types"] = {'opengl_type': 'float', 'value': prev_line_types, 'opengl_name': 'a_prev_line_type'}
layers_batch = RenderBatch(self._current_shader, type = RenderBatch.RenderType.Solid, mode = RenderBatch.RenderMode.Lines, range = (start, end), backface_cull = True)
layers_batch.addItem(node.getWorldTransformation(), layer_data)
layers_batch.render(self._scene.getActiveCamera())
@ -202,3 +221,9 @@ class SimulationPass(RenderPass):
tool_handle_batch.render(self._scene.getActiveCamera())
self.release()
def _onSceneChanged(self, changed_object: SceneNode):
if changed_object.callDecoration("getLayerData"): # Any layer data has changed.
self._switching_layers = True
self._old_current_layer = 0
self._old_current_path = 0

View file

@ -91,6 +91,8 @@ class SimulationView(CuraView):
self._min_feedrate = sys.float_info.max
self._max_thickness = sys.float_info.min
self._min_thickness = sys.float_info.max
self._max_line_width = sys.float_info.min
self._min_line_width = sys.float_info.max
self._global_container_stack = None # type: Optional[ContainerStack]
self._proxy = None
@ -104,13 +106,14 @@ class SimulationView(CuraView):
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
Application.getInstance().getPreferences().addPreference("view/force_layer_view_compatibility_mode", False)
Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 0)
Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 1) # Default to "Line Type".
Application.getInstance().getPreferences().addPreference("layerview/extruder_opacities", "")
Application.getInstance().getPreferences().addPreference("layerview/show_travel_moves", False)
Application.getInstance().getPreferences().addPreference("layerview/show_helpers", True)
Application.getInstance().getPreferences().addPreference("layerview/show_skin", True)
Application.getInstance().getPreferences().addPreference("layerview/show_infill", True)
Application.getInstance().getPreferences().addPreference("layerview/show_starts", True)
self._updateWithPreferences()
@ -146,6 +149,7 @@ class SimulationView(CuraView):
self._show_helpers = True
self._show_skin = True
self._show_infill = True
self._show_starts = True
self.resetLayerData()
def getActivity(self) -> bool:
@ -218,6 +222,8 @@ class SimulationView(CuraView):
self._min_feedrate = sys.float_info.max
self._max_thickness = sys.float_info.min
self._min_thickness = sys.float_info.max
self._max_line_width = sys.float_info.min
self._min_line_width = sys.float_info.max
def beginRendering(self) -> None:
scene = self.getController().getScene()
@ -355,6 +361,13 @@ class SimulationView(CuraView):
def getShowInfill(self) -> bool:
return self._show_infill
def setShowStarts(self, show: bool) -> None:
self._show_starts = show
self.currentLayerNumChanged.emit()
def getShowStarts(self) -> bool:
return self._show_starts
def getCompatibilityMode(self) -> bool:
return self._compatibility_mode
@ -377,6 +390,14 @@ class SimulationView(CuraView):
def getMaxThickness(self) -> float:
return self._max_thickness
def getMaxLineWidth(self) -> float:
return self._max_line_width
def getMinLineWidth(self) -> float:
if abs(self._min_line_width - sys.float_info.max) < 10: # Some lenience due to floating point rounding.
return 0.0 # If it's still max-float, there are no measurements. Use 0 then.
return self._min_line_width
def calculateMaxLayers(self) -> None:
scene = self.getController().getScene()
@ -401,6 +422,8 @@ class SimulationView(CuraView):
for p in layer_data.getLayer(layer_id).polygons:
self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
self._max_line_width = max(float(p.lineWidths.max()), self._max_line_width)
self._min_line_width = min(float(p.lineWidths.min()), self._min_line_width)
self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
try:
self._min_thickness = min(float(p.lineThicknesses[numpy.nonzero(p.lineThicknesses)].min()), self._min_thickness)
@ -638,6 +661,7 @@ class SimulationView(CuraView):
self.setShowHelpers(bool(Application.getInstance().getPreferences().getValue("layerview/show_helpers")))
self.setShowSkin(bool(Application.getInstance().getPreferences().getValue("layerview/show_skin")))
self.setShowInfill(bool(Application.getInstance().getPreferences().getValue("layerview/show_infill")))
self.setShowStarts(bool(Application.getInstance().getPreferences().getValue("layerview/show_starts")))
self._startUpdateTopLayers()
self.preferencesChanged.emit()
@ -653,6 +677,7 @@ class SimulationView(CuraView):
"layerview/show_helpers",
"layerview/show_skin",
"layerview/show_infill",
"layerview/show_starts",
}:
return

View file

@ -82,12 +82,14 @@ Cura.ExpandableComponent
property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers")
property bool show_skin: UM.Preferences.getValue("layerview/show_skin")
property bool show_infill: UM.Preferences.getValue("layerview/show_infill")
property bool show_starts: UM.Preferences.getValue("layerview/show_starts")
// If we are in compatibility mode, we only show the "line type"
property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1
property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2
property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3
property bool show_line_width_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 4
property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers")
property int top_layer_count: UM.Preferences.getValue("view/top_layer_count")
@ -116,9 +118,13 @@ Cura.ExpandableComponent
type_id: 2
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Layer thickness"),
text: catalog.i18nc("@label:listbox", "Layer Thickness"),
type_id: 3 // these ids match the switching in the shader
})
layerViewTypes.append({
text: catalog.i18nc("@label:listbox", "Line Width"),
type_id: 4
})
}
ComboBox
@ -144,9 +150,10 @@ Cura.ExpandableComponent
{
// Update the visibility of the legends.
viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1);
viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3);
viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3 || type_id == 4);
viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2);
viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3);
viewSettings.show_line_width_gradient = viewSettings.show_gradient && (type_id == 4);
}
}
@ -250,6 +257,15 @@ Cura.ExpandableComponent
preference: "layerview/show_infill",
colorId: "layerview_infill"
});
if (! UM.SimulationView.compatibilityMode)
{
typesLegendModel.append({
label: catalog.i18nc("@label", "Starts"),
initialValue: viewSettings.show_starts,
preference: "layerview/show_starts",
colorId: "layerview_starts"
});
}
}
}
@ -380,6 +396,11 @@ Cura.ExpandableComponent
{
return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
}
//Line width selected
if(UM.Preferences.getValue("layerview/layer_view_type") == 4)
{
return parseFloat(UM.SimulationView.getMinLineWidth()).toFixed(2);
}
}
return catalog.i18nc("@label","min")
}
@ -405,6 +426,11 @@ Cura.ExpandableComponent
{
return "mm"
}
//Line width selected
if(UM.Preferences.getValue("layerview/layer_view_type") == 4)
{
return "mm"
}
}
return ""
}
@ -429,6 +455,11 @@ Cura.ExpandableComponent
{
return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
}
//Line width selected
if(UM.Preferences.getValue("layerview/layer_view_type") == 4)
{
return parseFloat(UM.SimulationView.getMaxLineWidth()).toFixed(2);
}
}
return catalog.i18nc("@label","max")
}
@ -443,7 +474,7 @@ Cura.ExpandableComponent
Rectangle
{
id: feedrateGradient
visible: viewSettings.show_feedrate_gradient
visible: viewSettings.show_feedrate_gradient || viewSettings.show_line_width_gradient
anchors.left: parent.left
anchors.right: parent.right
height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5)

View file

@ -117,6 +117,14 @@ class SimulationViewProxy(QObject):
def getMaxThickness(self):
return self._simulation_view.getMaxThickness()
@pyqtSlot(result=float)
def getMaxLineWidth(self):
return self._simulation_view.getMaxLineWidth()
@pyqtSlot(result=float)
def getMinLineWidth(self):
return self._simulation_view.getMinLineWidth()
# Opacity 0..1
@pyqtSlot(int, float)
def setExtruderOpacity(self, extruder_nr, opacity):

View file

@ -10,6 +10,8 @@ vertex41core =
uniform lowp float u_min_feedrate;
uniform lowp float u_max_thickness;
uniform lowp float u_min_thickness;
uniform lowp float u_max_line_width;
uniform lowp float u_min_line_width;
uniform lowp int u_layer_view_type;
uniform lowp mat4 u_extruder_opacity; // currently only for max 16 extruders, others always visible
@ -21,6 +23,7 @@ vertex41core =
in highp vec4 a_normal;
in highp vec2 a_line_dim; // line width and thickness
in highp float a_extruder;
in highp float a_prev_line_type;
in highp float a_line_type;
in highp float a_feedrate;
in highp float a_thickness;
@ -32,6 +35,7 @@ vertex41core =
out lowp vec2 v_line_dim;
out highp int v_extruder;
out highp mat4 v_extruder_opacity;
out float v_prev_line_type;
out float v_line_type;
out lowp vec4 f_color;
@ -64,6 +68,19 @@ vertex41core =
return vec4(red, green, blue, 1.0);
}
vec4 lineWidthGradientColor(float abs_value, float min_value, float max_value)
{
float value = (abs_value - min_value) / (max_value - min_value);
float red = value;
float green = 1 - abs(1 - 4 * value);
if(value > 0.375)
{
green = 0.5;
}
float blue = max(1 - 4 * value, 0);
return vec4(red, green, blue, 1.0);
}
void main()
{
vec4 v1_vertex = a_vertex;
@ -86,12 +103,16 @@ vertex41core =
case 3: // "Layer thickness"
v_color = layerThicknessGradientColor(a_line_dim.y, u_min_thickness, u_max_thickness);
break;
case 4: // "Line width"
v_color = lineWidthGradientColor(a_line_dim.x, u_min_line_width, u_max_line_width);
break;
}
v_vertex = world_space_vert.xyz;
v_normal = (u_normalMatrix * normalize(a_normal)).xyz;
v_line_dim = a_line_dim;
v_extruder = int(a_extruder);
v_prev_line_type = a_prev_line_type;
v_line_type = a_line_type;
v_extruder_opacity = u_extruder_opacity;
@ -108,13 +129,16 @@ geometry41core =
uniform highp mat4 u_viewMatrix;
uniform highp mat4 u_projectionMatrix;
uniform lowp vec4 u_starts_color;
uniform int u_show_travel_moves;
uniform int u_show_helpers;
uniform int u_show_skin;
uniform int u_show_infill;
uniform int u_show_starts;
layout(lines) in;
layout(triangle_strip, max_vertices = 26) out;
layout(triangle_strip, max_vertices = 40) out;
in vec4 v_color[];
in vec3 v_vertex[];
@ -122,6 +146,7 @@ geometry41core =
in vec2 v_line_dim[];
in int v_extruder[];
in mat4 v_extruder_opacity[];
in float v_prev_line_type[];
in float v_line_type[];
out vec4 f_color;
@ -177,17 +202,17 @@ geometry41core =
}
size_y = v_line_dim[1].y / 2 + 0.01;
g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position;
g_vertex_normal_horz_head = normalize(vec3(-g_vertex_delta.x, -g_vertex_delta.y, -g_vertex_delta.z));
g_vertex_offset_horz_head = vec4(g_vertex_normal_horz_head * size_x, 0.0);
g_vertex_delta = gl_in[1].gl_Position - gl_in[0].gl_Position; //Actual movement exhibited by the line.
g_vertex_normal_horz_head = normalize(vec3(-g_vertex_delta.x, -g_vertex_delta.y, -g_vertex_delta.z)); //Lengthwise normal vector pointing backwards.
g_vertex_offset_horz_head = vec4(g_vertex_normal_horz_head * size_x, 0.0); //Lengthwise offset vector pointing backwards.
g_vertex_normal_horz = normalize(vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x));
g_vertex_normal_horz = normalize(vec3(g_vertex_delta.z, g_vertex_delta.y, -g_vertex_delta.x)); //Normal vector pointing right.
g_vertex_offset_horz = vec4(g_vertex_normal_horz * size_x, 0.0); //Offset vector pointing right.
g_vertex_offset_horz = vec4(g_vertex_normal_horz * size_x, 0.0); //size * g_vertex_normal_horz;
g_vertex_normal_vert = vec3(0.0, 1.0, 0.0);
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0);
g_vertex_normal_vert = vec3(0.0, 1.0, 0.0); //Upwards normal vector.
g_vertex_offset_vert = vec4(g_vertex_normal_vert * size_y, 0.0); //Upwards offset vector. Goes up by half the layer thickness.
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) {
if ((v_line_type[0] == 8) || (v_line_type[0] == 9)) { //Travel or retraction moves.
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head + g_vertex_offset_vert);
vec4 va_up = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz + g_vertex_offset_vert);
vec4 va_down = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz + g_vertex_offset_vert);
@ -213,16 +238,16 @@ geometry41core =
EndPrimitive();
} else {
vec4 va_m_horz = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz);
vec4 vb_m_horz = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz);
vec4 va_p_vert = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert);
vec4 vb_p_vert = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert);
vec4 va_p_horz = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz);
vec4 vb_p_horz = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz);
vec4 va_m_vert = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert);
vec4 vb_m_vert = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert);
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head);
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head);
vec4 va_m_horz = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_horz); //Line start, left vertex.
vec4 vb_m_horz = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz); //Line end, left vertex.
vec4 va_p_vert = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_vert); //Line start, top vertex.
vec4 vb_p_vert = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_vert); //Line end, top vertex.
vec4 va_p_horz = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz); //Line start, right vertex.
vec4 vb_p_horz = viewProjectionMatrix * (gl_in[1].gl_Position + g_vertex_offset_horz); //Line end, right vertex.
vec4 va_m_vert = viewProjectionMatrix * (gl_in[0].gl_Position - g_vertex_offset_vert); //Line start, bottom vertex.
vec4 vb_m_vert = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_vert); //Line end, bottom vertex.
vec4 va_head = viewProjectionMatrix * (gl_in[0].gl_Position + g_vertex_offset_horz_head); //Line start, tip.
vec4 vb_head = viewProjectionMatrix * (gl_in[1].gl_Position - g_vertex_offset_horz_head); //Line end, tip.
// All normal lines are rendered as 3d tubes.
myEmitVertex(v_vertex[0], v_color[0], -g_vertex_normal_horz, va_m_horz);
@ -268,6 +293,29 @@ geometry41core =
EndPrimitive();
}
if ((u_show_starts == 1) && (v_prev_line_type[0] != 1) && (v_line_type[0] == 1)) {
float w = v_line_dim[0].x / 2;
float h = v_line_dim[0].y / 2;
myEmitVertex(v_vertex[0] + vec3( w, h, w), u_starts_color, normalize(vec3( 1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, w, 0.0))); // Front-top-left
myEmitVertex(v_vertex[0] + vec3(-w, h, w), u_starts_color, normalize(vec3(-1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, w, 0.0))); // Front-top-right
myEmitVertex(v_vertex[0] + vec3( w, -h, w), u_starts_color, normalize(vec3( 1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, w, 0.0))); // Front-bottom-left
myEmitVertex(v_vertex[0] + vec3(-w, -h, w), u_starts_color, normalize(vec3(-1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, w, 0.0))); // Front-bottom-right
myEmitVertex(v_vertex[0] + vec3(-w, -h, -w), u_starts_color, normalize(vec3(-1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, -w, 0.0))); // Back-bottom-right
myEmitVertex(v_vertex[0] + vec3(-w, h, w), u_starts_color, normalize(vec3(-1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, w, 0.0))); // Front-top-right
myEmitVertex(v_vertex[0] + vec3(-w, h, -w), u_starts_color, normalize(vec3(-1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, -w, 0.0))); // Back-top-right
myEmitVertex(v_vertex[0] + vec3( w, h, w), u_starts_color, normalize(vec3( 1.0, 1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, w, 0.0))); // Front-top-left
myEmitVertex(v_vertex[0] + vec3( w, h, -w), u_starts_color, normalize(vec3( 1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, -w, 0.0))); // Back-top-left
myEmitVertex(v_vertex[0] + vec3( w, -h, w), u_starts_color, normalize(vec3( 1.0, -1.0, 1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, w, 0.0))); // Front-bottom-left
myEmitVertex(v_vertex[0] + vec3( w, -h, -w), u_starts_color, normalize(vec3( 1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, -h, -w, 0.0))); // Back-bottom-left
myEmitVertex(v_vertex[0] + vec3(-w, -h, -w), u_starts_color, normalize(vec3(-1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, -w, 0.0))); // Back-bottom-right
myEmitVertex(v_vertex[0] + vec3( w, h, -w), u_starts_color, normalize(vec3( 1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, -w, 0.0))); // Back-top-left
myEmitVertex(v_vertex[0] + vec3(-w, h, -w), u_starts_color, normalize(vec3(-1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, -w, 0.0))); // Back-top-right
EndPrimitive();
}
}
fragment41core =
@ -312,10 +360,13 @@ u_diffuseColor = [1.0, 0.79, 0.14, 1.0]
u_minimumAlbedo = [0.1, 0.1, 0.1, 1.0]
u_shininess = 20.0
u_starts_color = [1.0, 1.0, 1.0, 1.0]
u_show_travel_moves = 0
u_show_helpers = 1
u_show_skin = 1
u_show_infill = 1
u_show_starts = 1
u_min_feedrate = 0
u_max_feedrate = 1
@ -337,6 +388,7 @@ a_normal = normal
a_line_dim = line_dim
a_extruder = extruder
a_material_color = material_color
a_prev_line_type = prev_line_type
a_line_type = line_type
a_feedrate = feedrate
a_thickness = thickness

View file

@ -1,4 +1,4 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os.path
@ -6,8 +6,8 @@ from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.Selection import Selection
from UM.Resources import Resources
from PyQt5.QtGui import QOpenGLContext, QImage
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QOpenGLContext, QDesktopServices, QImage
from PyQt5.QtCore import QSize, QUrl
import numpy as np
import time
@ -56,7 +56,8 @@ class SolidView(View):
self._extruders_model = None
self._theme = None
self._support_angle = 90
self._support_angle = self._retrieveSupportAngle()
self._lowest_printable_height = self._retrieveLowestPrintHeight()
self._global_stack = None
@ -67,20 +68,27 @@ class SolidView(View):
self._xray_checking_update_time = 30.0 # seconds
self._xray_warning_cooldown = 60 * 10 # reshow Model error message every 10 minutes
self._xray_warning_message = Message(
catalog.i18nc("@info:status", "Your model is not manifold. The highlighted areas indicate either missing or extraneous surfaces."),
catalog.i18nc("@info:status", "The highlighted areas indicate either missing or extraneous surfaces. Fix your model and open it again into Cura."),
lifetime = 60 * 5, # leave message for 5 minutes
title = catalog.i18nc("@info:title", "Model errors"),
title = catalog.i18nc("@info:title", "Model Errors"),
option_text = catalog.i18nc("@info:option_text", "Do not show this message again"),
option_state = False
)
self._xray_warning_message.optionToggled.connect(self._onDontAskMeAgain)
application.getPreferences().addPreference(self._show_xray_warning_preference, True)
self._xray_warning_message.addAction("manifold", catalog.i18nc("@action:button", "Learn more"), "[no_icon]", "[no_description]",
button_style = Message.ActionButtonStyle.LINK,
button_align = Message.ActionButtonAlignment.ALIGN_LEFT)
self._xray_warning_message.actionTriggered.connect(self._onNonManifoldLearnMoreClicked)
application.engineCreatedSignal.connect(self._onGlobalContainerChanged)
def _onDontAskMeAgain(self, checked: bool) -> None:
Application.getInstance().getPreferences().setValue(self._show_xray_warning_preference, not checked)
def _onNonManifoldLearnMoreClicked(self, action, message) -> None:
QDesktopServices.openUrl(QUrl("https://support.ultimaker.com/hc/en-us/articles/360014055959"))
def _onGlobalContainerChanged(self) -> None:
if self._global_stack:
try:
@ -95,12 +103,20 @@ class SolidView(View):
self._global_stack.propertyChanged.connect(self._onPropertyChanged)
for extruder_stack in ExtruderManager.getInstance().getActiveExtruderStacks():
extruder_stack.propertyChanged.connect(self._onPropertyChanged)
self._onPropertyChanged("support_angle", "value") # Force an re-evaluation
# Force re-evaluation:
self._support_angle = self._retrieveSupportAngle()
self._lowest_printable_height = self._retrieveLowestPrintHeight()
def _onPropertyChanged(self, key: str, property_name: str) -> None:
if key != "support_angle" or property_name != "value":
if property_name != "value":
return
# As the rendering is called a *lot* we really, dont want to re-evaluate the property every time. So we store em!
if key == "support_angle":
self._support_angle = self._retrieveSupportAngle()
elif key == "layer_height_0" or key == "slicing_tolerance":
self._lowest_printable_height = self._retrieveLowestPrintHeight()
def _retrieveSupportAngle(self) -> float:
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
support_extruder_nr = int(global_container_stack.getExtruderPositionValueWithDefault("support_extruder_nr"))
@ -111,7 +127,18 @@ class SolidView(View):
else:
angle = support_angle_stack.getProperty("support_angle", "value")
if angle is not None:
self._support_angle = angle
return angle
return 90.0
def _retrieveLowestPrintHeight(self) -> float:
min_height = 0.0
for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks():
init_layer_height = extruder.getProperty("layer_height_0", "value")
tolerance_setting = extruder.getProperty("slicing_tolerance", "value")
if tolerance_setting == "middle":
init_layer_height /= 2.0
min_height = max(min_height, init_layer_height)
return min_height
def _checkSetup(self):
if not self._extruders_model:
@ -194,6 +221,7 @@ class SolidView(View):
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
else:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
self._enabled_shader.setUniformValue("u_lowestPrintableHeight", self._lowest_printable_height)
disabled_batch = renderer.createRenderBatch(shader = self._disabled_shader)
normal_object_batch = renderer.createRenderBatch(shader = self._enabled_shader)
renderer.addRenderBatch(disabled_batch)

View file

@ -1,3 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import os
from typing import List, Dict, Any, cast
@ -96,7 +99,10 @@ class SyncOrchestrator(Extension):
else:
self._cloud_api.unsubscribe(item["package_id"])
# delete temp file
os.remove(item["package_path"])
try:
os.remove(item["package_path"])
except EnvironmentError as e: # File was already removed, no access rights, etc.
Logger.error("Can't delete temporary package file: {err}".format(err = str(e)))
if has_changes:
self._restart_presenter.present()

View file

@ -46,8 +46,6 @@ class Toolbox(QObject, Extension):
self._application = application # type: CuraApplication
self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
# Network:
self._download_request_data = None # type: Optional[HttpRequestData]
self._download_progress = 0 # type: float

View file

@ -1,4 +1,4 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import cast, List, Dict
@ -7,16 +7,16 @@ from Charon.VirtualFile import VirtualFile # To open UFP files.
from Charon.OpenMode import OpenMode # To indicate that we want to write to UFP files.
from io import StringIO # For converting g-code to bytes.
from PyQt5.QtCore import QBuffer
from UM.Logger import Logger
from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement.
from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.PluginRegistry import PluginRegistry # To get the g-code writer.
from PyQt5.QtCore import QBuffer
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread
from UM.i18n import i18nCatalog
@ -38,17 +38,6 @@ class UFPWriter(MeshWriter):
)
)
self._snapshot = None
def _createSnapshot(self, *args):
# must be called from the main thread because of OpenGL
Logger.log("d", "Creating thumbnail image...")
try:
self._snapshot = Snapshot.snapshot(width = 300, height = 300)
except Exception:
Logger.logException("w", "Failed to create snapshot image")
self._snapshot = None # Failing to create thumbnail should not fail creation of UFP
# This needs to be called on the main thread (Qt thread) because the serialization of material containers can
# trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the
# Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled
@ -72,24 +61,23 @@ class UFPWriter(MeshWriter):
gcode.write(gcode_textio.getvalue().encode("UTF-8"))
archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")
self._createSnapshot()
# Store the thumbnail.
if self._snapshot:
# Attempt to store the thumbnail, if any:
backend = CuraApplication.getInstance().getBackend()
snapshot = None if getattr(backend, "getLatestSnapshot", None) is None else backend.getLatestSnapshot()
if snapshot:
archive.addContentType(extension = "png", mime_type = "image/png")
thumbnail = archive.getStream("/Metadata/thumbnail.png")
thumbnail_buffer = QBuffer()
thumbnail_buffer.open(QBuffer.ReadWrite)
thumbnail_image = self._snapshot
thumbnail_image.save(thumbnail_buffer, "PNG")
snapshot.save(thumbnail_buffer, "PNG")
thumbnail.write(thumbnail_buffer.data())
archive.addRelation(virtual_path = "/Metadata/thumbnail.png",
relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail",
origin = "/3D/model.gcode")
else:
Logger.log("d", "Thumbnail not created, cannot save it")
Logger.log("w", "Thumbnail not created, cannot save it")
# Store the material.
application = CuraApplication.getInstance()

View file

@ -1,3 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional
from UM.FileHandler.FileHandler import FileHandler

View file

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional

View file

@ -1,4 +1,4 @@
# Copyright (c) 2020 Ultimaker B.V.
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional, Union, Dict, Any
@ -8,7 +8,7 @@ from .ClusterBuildPlate import ClusterBuildPlate
from .ClusterPrintJobConfigurationChange import ClusterPrintJobConfigurationChange
from .ClusterPrintJobImpediment import ClusterPrintJobImpediment
from .ClusterPrintCoreConfiguration import ClusterPrintCoreConfiguration
from .ClusterPrintJobConstraint import ClusterPrintJobConstraints
from .ClusterPrintJobConstraints import ClusterPrintJobConstraints
from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel
from ..ConfigurationChangeModel import ConfigurationChangeModel
from ..BaseModel import BaseModel
@ -18,13 +18,24 @@ from ...ClusterOutputController import ClusterOutputController
class ClusterPrintJobStatus(BaseModel):
"""Model for the status of a single print job in a cluster."""
def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str,
time_total: int, uuid: str,
def __init__(self,
created_at: str,
force: bool,
machine_variant: str,
name: str,
started: bool,
status: str,
time_total: int,
uuid: str,
configuration: List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]],
constraints: List[Union[Dict[str, Any], ClusterPrintJobConstraints]],
last_seen: Optional[float] = None, network_error_count: Optional[int] = None,
owner: Optional[str] = None, printer_uuid: Optional[str] = None, time_elapsed: Optional[int] = None,
assigned_to: Optional[str] = None, deleted_at: Optional[str] = None,
constraints: Optional[Union[Dict[str, Any], ClusterPrintJobConstraints]] = None,
last_seen: Optional[float] = None,
network_error_count: Optional[int] = None,
owner: Optional[str] = None,
printer_uuid: Optional[str] = None,
time_elapsed: Optional[int] = None,
assigned_to: Optional[str] = None,
deleted_at: Optional[str] = None,
printed_on_uuid: Optional[str] = None,
configuration_changes_required: List[
Union[Dict[str, Any], ClusterPrintJobConfigurationChange]] = None,
@ -63,10 +74,9 @@ class ClusterPrintJobStatus(BaseModel):
printer
:param preview_url: URL to the preview image (same as wou;d've been included in the ufp).
"""
self.assigned_to = assigned_to
self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration)
self.constraints = self.parseModels(ClusterPrintJobConstraints, constraints)
self.constraints = self.parseModel(ClusterPrintJobConstraints, constraints) if constraints else None
self.created_at = created_at
self.force = force
self.last_seen = last_seen
@ -83,7 +93,6 @@ class ClusterPrintJobStatus(BaseModel):
self.deleted_at = deleted_at
self.printed_on_uuid = printed_on_uuid
self.preview_url = preview_url
self.configuration_changes_required = self.parseModels(ClusterPrintJobConfigurationChange,
configuration_changes_required) \
if configuration_changes_required else []

View file

@ -129,16 +129,16 @@ class ZeroConfClient:
for record in zero_conf.cache.entries_with_name(info.server):
info.update_record(zero_conf, time(), record)
if info.addresses:
if hasattr(info, "addresses") and info.addresses:
break
# Request more data if info is not complete
if not info.addresses:
if not hasattr(info, "addresses") or not info.addresses:
new_info = zero_conf.get_service_info(service_type, name)
if new_info is not None:
info = new_info
if info and info.addresses:
if info and hasattr(info, "addresses") and info.addresses:
type_of_device = info.properties.get(b"type", None)
if type_of_device:
if type_of_device == b"printer":

View file

@ -211,6 +211,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
def _onGlobalContainerStackChanged(self):
container_stack = CuraApplication.getInstance().getGlobalContainerStack()
if container_stack is None:
return
num_extruders = container_stack.getProperty("machine_extruder_count", "value")
# Ensure that a printer is created.
controller = GenericOutputController(self)

View file

@ -151,7 +151,7 @@ class XmlMaterialProfile(InstanceContainer):
"version": self.CurrentFdmMaterialVersion})
## Begin Metadata Block
builder.start("metadata") # type: ignore
builder.start("metadata", {}) # type: ignore
metadata = copy.deepcopy(self.getMetaData())
# setting_version is derived from the "version" tag in the schema, so don't serialize it into a file
@ -165,21 +165,21 @@ class XmlMaterialProfile(InstanceContainer):
properties = metadata.pop("properties", {})
## Begin Name Block
builder.start("name") # type: ignore
builder.start("name", {}) # type: ignore
builder.start("brand") # type: ignore
builder.start("brand", {}) # type: ignore
builder.data(metadata.pop("brand", ""))
builder.end("brand")
builder.start("material") # type: ignore
builder.start("material", {}) # type: ignore
builder.data(metadata.pop("material", ""))
builder.end("material")
builder.start("color") # type: ignore
builder.start("color", {}) # type: ignore
builder.data(metadata.pop("color_name", ""))
builder.end("color")
builder.start("label") # type: ignore
builder.start("label", {}) # type: ignore
builder.data(self.getName())
builder.end("label")
@ -190,7 +190,7 @@ class XmlMaterialProfile(InstanceContainer):
key_to_use = key
if key in self._metadata_tags_that_have_cura_namespace:
key_to_use = "cura:" + key_to_use
builder.start(key_to_use) # type: ignore
builder.start(key_to_use, {}) # type: ignore
if value is not None: #Nones get handled well by the builder.
#Otherwise the builder always expects a string.
#Deserialize expects the stringified version.
@ -202,10 +202,10 @@ class XmlMaterialProfile(InstanceContainer):
## End Metadata Block
## Begin Properties Block
builder.start("properties") # type: ignore
builder.start("properties", {}) # type: ignore
for key, value in properties.items():
builder.start(key) # type: ignore
builder.start(key, {}) # type: ignore
builder.data(value)
builder.end(key)
@ -213,7 +213,7 @@ class XmlMaterialProfile(InstanceContainer):
## End Properties Block
## Begin Settings Block
builder.start("settings") # type: ignore
builder.start("settings", {}) # type: ignore
if self.getMetaDataEntry("definition") == "fdmprinter":
for instance in self.findInstances():
@ -258,7 +258,7 @@ class XmlMaterialProfile(InstanceContainer):
product = product_name
break
builder.start("machine") # type: ignore
builder.start("machine", {}) # type: ignore
builder.start("machine_identifier", {
"manufacturer": container.getMetaDataEntry("machine_manufacturer",
definition_metadata.get("manufacturer", "Unknown")),

View file

@ -65,6 +65,6 @@
"retraction_speed": { "default_value": 50},
"adhesion_type": { "value": "'skirt'" },
"machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X0 Y240 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X0 Y240 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }
}
}
}

View file

@ -59,7 +59,7 @@
},
"machine_end_gcode":
{
"default_value": "M117 Cooling down...\nM104 S0 ; turn off extruder\nM84 ; disable motors\nM107 ; Fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;move X to min endstops, so the head is out of the way\nG90 ;Absolute positionning\nG1 Y200 F3000 ;Present print\nM84 ;steppers off\nM300 P300 S4000\nM117 Finished.\n"
"default_value": "M117 Cooling down...\nM104 S0 ; turn off extruder\nM84 ; disable motors\nM107 ; Fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;move X to min endstops, so the head is out of the way\nG90 ;Absolute positioning\nG1 Y200 F3000 ;Present print\nM84 ;steppers off\nM300 P300 S4000\nM117 Finished.\n"
},
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },

View file

@ -0,0 +1,52 @@
{
"version": 2,
"name": "Arjun 300",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Venkat Kamesh",
"manufacturer": "Sri Vignan Technologies",
"weight": 3,
"file_formats": "text/x-gcode",
"platform": "arjun300_platform.stl",
"platform_offset": [-155, -6, 190],
"has_material": true,
"has_variants": true,
"preferred_variant_name": "0.4 mm Nozzle",
"machine_extruder_trains":
{
"0": "arjun_extruder_0",
"1": "arjun_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Arjun 300" },
"machine_width": { "default_value": 317 },
"machine_height": { "default_value": 290 },
"machine_depth": { "default_value": 310 },
"machine_center_is_zero": {"default_value": false},
"machine_heated_bed": { "default_value": true },
"machine_nozzle_size": {"default_value": 0.4},
"machine_show_variants": {"default_value": true},
"machine_acceleration": {"default_value": 2000},
"machine_max_feedrate_x": { "value": 300 },
"machine_max_feedrate_y": { "value": 300 },
"machine_max_feedrate_z": { "value": 15 },
"machine_max_feedrate_e": { "value": 150 },
"machine_use_extruder_offset_to_offset_coords": {"default_value": false},
"line_width": {"value": "machine_nozzle_size"},
"speed_travel": {"maximum_value": "300", "value": "200"},
"optimize_wall_printing_order": { "value": "True" },
"material_diameter": { "default_value": 1.75},
"retraction_amount": {"default_value": 6.5},
"retraction_speed": { "default_value": 30},
"adhesion_type": { "default_value": "skirt" },
"machine_gcode_flavor": { "default_value": "Marlin"},
"ironing_enabled":{"default_value": true},
"machine_start_gcode": { "default_value": "M605 S0\nG21\nG90\nM82\nM107\nT1\nG28\nG1 Z0.3 F150\nT1\nG92 E0\nG1 E45 F210\nG92 E0\nT0\nG92 E0\nG1 E45 F210\nG92 E0\nM117\n"},
"machine_end_gcode": { "default_value": "G91\nG1 Z+0.5 E-16 Y+10 F9000\nG90\nM107\nM104 S0 T1\nM104 S0 T0\nM140 S0\nM117\nG28 X0 Y0\nT0\nM84"},
"machine_extruder_count": { "default_value": 2 }
}
}

View file

@ -0,0 +1,49 @@
{
"version": 2,
"name": "Arjun 300 Duplication",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Venkat Kamesh",
"manufacturer": "Sri Vignan Technologies",
"weight": 3,
"file_formats": "text/x-gcode",
"has_material": true,
"has_variants": true,
"preferred_variant_name": "0.4 mm Nozzle",
"machine_extruder_trains":
{
"0": "arjun_dm_extruder"
}
},
"overrides": {
"machine_name": { "default_value": "Arjun 300 Duplication" },
"machine_width": { "default_value": 158.5 },
"machine_height": { "default_value": 290 },
"machine_depth": { "default_value": 310 },
"machine_center_is_zero": {"default_value": false},
"machine_heated_bed": { "default_value": true },
"machine_nozzle_size": {"default_value": 0.4},
"machine_show_variants": {"default_value": true},
"machine_acceleration": {"default_value": 2000},
"machine_max_feedrate_x": { "value": 300 },
"machine_max_feedrate_y": { "value": 300 },
"machine_max_feedrate_z": { "value": 15 },
"machine_max_feedrate_e": { "value": 150 },
"machine_use_extruder_offset_to_offset_coords": {"default_value": false},
"line_width": {"value": "machine_nozzle_size"},
"speed_travel": {"maximum_value": "300", "value": "200"},
"optimize_wall_printing_order": { "value": "True" },
"material_diameter": { "default_value": 1.75},
"retraction_amount": {"default_value": 6.5},
"retraction_speed": { "default_value": 30},
"adhesion_type": { "default_value": "skirt" },
"machine_gcode_flavor": { "default_value": "Marlin"},
"ironing_enabled":{"default_value": true},
"machine_start_gcode": {"default_value": "M605 S2 R0 X155\nG21\nG90\nM82\nM107\nM104 S{material_print_temperature}\nM105\nM109 S{material_print_temperature}\nG28 Z0\nG1 Z15 F150\nG28 Y0\nG1 Y20 F6000\nG28 X0\nG1 X80 F9000\nT0\nG92 E0\nG1 E35 F250\nG1 E45 F120\nG92 E0\nG1 X100 Z0 F5000\nG1 X145 F9000\nM117\n"},
"machine_end_gcode": {"default_value": "G91\nG1 Z+0.5 E-16 Y+10 F9000\nG90\nM107\nM107 P1\nM104 S0\nM140 S0\nM117\nM605 S0\nG28 X0 Y0\nM84"},
"machine_extruder_count": { "default_value": 1 }
}
}

View file

@ -0,0 +1,49 @@
{
"version": 2,
"name": "Arjun 300 Mirror",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Venkat Kamesh",
"manufacturer": "Sri Vignan Technologies",
"weight": 3,
"file_formats": "text/x-gcode",
"has_material": true,
"has_variants": true,
"preferred_variant_name": "0.4 mm Nozzle",
"machine_extruder_trains":
{
"0": "arjun_mm_extruder"
}
},
"overrides": {
"machine_name": { "default_value": "Arjun 300 Mirror" },
"machine_width": { "default_value": 158.5 },
"machine_height": { "default_value": 290 },
"machine_depth": { "default_value": 310 },
"machine_center_is_zero": {"default_value": false},
"machine_heated_bed": { "default_value": true },
"machine_nozzle_size": {"default_value": 0.4},
"machine_show_variants": {"default_value": true},
"machine_acceleration": {"default_value": 2000},
"machine_max_feedrate_x": { "value": 300 },
"machine_max_feedrate_y": { "value": 300 },
"machine_max_feedrate_z": { "value": 15 },
"machine_max_feedrate_e": { "value": 150 },
"machine_use_extruder_offset_to_offset_coords": {"default_value": false},
"line_width": {"value": "machine_nozzle_size"},
"speed_travel": {"maximum_value": "300", "value": "200"},
"optimize_wall_printing_order": { "value": "True" },
"material_diameter": { "default_value": 1.75},
"retraction_amount": {"default_value": 6.5},
"retraction_speed": { "default_value": 30},
"adhesion_type": { "default_value": "skirt" },
"machine_gcode_flavor": { "default_value": "Marlin"},
"ironing_enabled":{"default_value": true},
"machine_start_gcode": {"default_value": "M605 S2 R0 X155\nM605 S3 X155\nG21\nG90\nM82\nM107\nM104 S{material_print_temperature}\nM105\nM109 S{material_print_temperature}\nG28 Z0\nG1 Z15 F150\nG28 Y0\nG1 Y20 F6000\nG28 X0\nG1 X80 F9000\nT0\nG92 E0\nG1 E35 F250\nG1 E45 F120\nG92 E0\nG1 X100 Z0 F5000\nG1 X145 F9000\nM117\n"},
"machine_end_gcode": {"default_value": "G91\nG1 Z+0.5 E-16 Y+10 F9000\nG90\nM107\nM107 P1\nM104 S0\nM140 S0\nM117\nM605 S0\nG28 X0 Y0\nM84"},
"machine_extruder_count": { "default_value": 1 }
}
}

View file

@ -71,7 +71,7 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 15mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nM117 BIBO Printing..."
"default_value": "M104 T0 165\nM104 T1 165\nM109 T{initial_extruder_nr} S{material_print_temperature_layer_0, initial_extruder_nr}\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 2mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nT{initial_extruder_nr}\nM117 BIBO Printing..."
},
"machine_end_gcode": {
"default_value": ";End GCode\nM104 T0 S0 ;extruder heater off\nM104 T1 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91\nG1 Z1 F100 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-2 X-20 Y-20 F300 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"

View file

@ -25,7 +25,7 @@
"overrides": {
"machine_name": { "default_value": "BIQU Base Printer" },
"machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" },
"machine_end_gcode": { "default_value": " ;BIQU Default End Gcode\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract a bit more and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z by 10mm\nG90 ;Return to absolute positionning\n\nG1 X0 Y{machine_depth} ;TaDaaaa\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_end_gcode": { "default_value": " ;BIQU Default End Gcode\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract a bit more and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z by 10mm\nG90 ;Return to absolute positioning\n\nG1 X0 Y{machine_depth} ;TaDaaaa\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },

View file

@ -125,7 +125,7 @@
"overrides": {
"machine_name": { "default_value": "Creawsome Base Printer" },
"machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },

View file

@ -4,7 +4,7 @@
"inherits": "creality_base",
"overrides": {
"machine_name": { "default_value": "Creality Ender-5" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_width": { "default_value": 220 },
"machine_depth": { "default_value": 220 },
"machine_height": { "default_value": 300 },
@ -25,4 +25,4 @@
"quality_definition": "creality_base",
"visible": true
}
}
}

View file

@ -22,7 +22,7 @@
"default_value": "Cubicon Style Neo-A22"
},
"machine_start_gcode": {
"default_value": "M911 Style Neo-A22\nM201 X400 Y400\nM202 X400 Y400\nG28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0"
"default_value": "M911 Style Neo-A22\nM201 X400 Y400\nM202 X400 Y400\nG28 ; Home\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0"
},
"machine_width": {
"default_value": 220

View file

@ -0,0 +1,40 @@
{
"version": 2,
"name": "Cubicon Style Neo-A31",
"inherits": "cubicon_common",
"metadata": {
"author": "Cubicon R&D Center",
"manufacturer": "Cubicon",
"visible": true,
"file_formats": "text/x-gcode",
"supports_usb_connection": false,
"machine_extruder_trains": {
"0": "cubicon_style_neo_a31_extruder_0"
},
"platform_offset": [
0,
0,
0
]
},
"overrides": {
"machine_name": {
"default_value": "Cubicon Style Neo-A31"
},
"machine_start_gcode": {
"default_value": "M911 Style Neo-A31\nM201 X400 Y400\nM202 X400 Y400\nG28 ; Home\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0"
},
"machine_width": {
"default_value": 310
},
"machine_depth": {
"default_value": 310
},
"machine_height": {
"default_value": 310
},
"material_bed_temp_wait":{
"default_value": false
}
}
}

View file

@ -22,7 +22,7 @@
"default_value": "Cubicon Style Plus-A15"
},
"machine_start_gcode": {
"default_value": "M911 Style Plus-A15\nM201 X400 Y400\nM202 X400 Y400\nG28 ; Home\nG1 Z15.0 F6000 ;move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0"
"default_value": "M911 Style Plus-A15\nM201 X400 Y400\nM202 X400 Y400\nG28 ; Home\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0"
},
"machine_width": {
"default_value": 150

View file

@ -8,12 +8,13 @@
"manufacturer": "Sri Vignan Technologies",
"weight": 3,
"file_formats": "text/x-gcode",
"platform": "ultimaker3_platform.obj",
"platform": "diy220_platform.obj",
"platform_texture": "svtbacktext.png",
"platform_offset": [0, 0, 0],
"has_materials": true,
"has_variants": true,
"preferred_variant_name": "0.4 mm",
"preferred_variant_name": "0.4 mm Nozzle",
"machine_extruder_trains":
{
"0": "diy220_extruder_0"
@ -21,12 +22,14 @@
},
"overrides": {
"machine_name": { "default_value": "Diytech 220" },
"machine_start_gcode" : {
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG28 Z0 ;move Z to bottom endstops\\nG28 X0 Y0 ;move X/Y to endstops\\nG1 X15 Y0 F4000 ;move X/Y to front of printer\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E50 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\""
},
"machine_end_gcode" : {
"value": "\";Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\\n;Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\""
"machine_start_gcode": {
"default_value": "G21\nG90\nM82\nM107\nG28\nG1 Z15 F200\nT0\nG92 E0\nG1 E16 F250\nG92 E0\n"
},
"machine_end_gcode": {
"default_value": "G91\nG1 Z+0.5 E-16 Y+10 F9000\nG90\nM107\nM104 S0 T0\nM140 S0\nM117\nG28 X0 Y0\nM84"
},
"machine_width": {
"default_value": 220
},
@ -39,15 +42,7 @@
"machine_heated_bed": {
"default_value": true
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -42, 12 ],
[ -42, -32 ],
[ 62, 12 ],
[ 62, -32 ]
]
},
"machine_center_is_zero": {
"default_value": false
},
@ -60,13 +55,7 @@
"machine_gcode_flavor": {
"default_value": "Marlin"
},
"machine_disallowed_areas": {
"default_value": [
[[-115, 112.5], [ -82, 112.5], [ -84, 102.5], [-115, 102.5]],
[[ 115, 112.5], [ 115, 102.5], [ 110, 102.5], [ 108, 112.5]],
[[-115, -112.5], [-115, -104.5], [ -84, -104.5], [ -82, -112.5]],
[[ 115, -112.5], [ 108, -112.5], [ 110, -104.5], [ 115, -104.5]]
]},
"machine_nozzle_tip_outer_diameter": {
"default_value": 1
},

View file

@ -398,6 +398,29 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_extruders_share_nozzle":
{
"label": "Extruders Share Nozzle",
"description": "Whether the extruders share a single nozzle rather than each extruder having its own nozzle. When set to true, it is expected that the printer-start gcode script properly sets up all extruders in an initial retraction state that is known and mutually compatible (either zero or one filament not retracted); in that case the initial retraction status is described, per extruder, by the 'machine_extruders_shared_nozzle_initial_retraction' parameter.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_extruders_shared_nozzle_initial_retraction":
{
"label": "Shared Nozzle Initial Retraction",
"description": "How much the filament of each extruder is assumed to have been retracted from the shared nozzle tip at the completion of the printer-start gcode script; the value should be equal to or greater than the length of the common part of the nozzle's ducts.",
"unit": "mm",
"default_value": 0,
"minimum_value": "0",
"type": "float",
"enabled": "machine_extruders_share_nozzle",
"settable_per_mesh": false,
"settable_per_extruder": true,
"settable_per_meshgroup": false
},
"machine_disallowed_areas":
{
"label": "Disallowed Areas",
@ -485,7 +508,7 @@
"machine_use_extruder_offset_to_offset_coords":
{
"label": "Offset with Extruder",
"description": "Apply the extruder offset to the coordinate system.",
"description": "Apply the extruder offset to the coordinate system. Affects all extruders.",
"type": "bool",
"default_value": true,
"settable_per_mesh": false,
@ -652,7 +675,7 @@
{
"label": "Steps per Millimeter (X)",
"description": "How many steps of the stepper motor will result in one millimeter of movement in the X direction.",
"type": "int",
"type": "float",
"default_value": 50,
"minimum_value": "0.0000001",
"settable_per_mesh": false,
@ -662,7 +685,7 @@
{
"label": "Steps per Millimeter (Y)",
"description": "How many steps of the stepper motor will result in one millimeter of movement in the Y direction.",
"type": "int",
"type": "float",
"default_value": 50,
"minimum_value": "0.0000001",
"settable_per_mesh": false,
@ -672,7 +695,7 @@
{
"label": "Steps per Millimeter (Z)",
"description": "How many steps of the stepper motor will result in one millimeter of movement in the Z direction.",
"type": "int",
"type": "float",
"default_value": 50,
"minimum_value": "0.0000001",
"settable_per_mesh": false,
@ -682,7 +705,7 @@
{
"label": "Steps per Millimeter (E)",
"description": "How many steps of the stepper motors will result in one millimeter of extrusion.",
"type": "int",
"type": "float",
"default_value": 1600,
"minimum_value": "0.0000001",
"settable_per_mesh": false,
@ -977,7 +1000,7 @@
},
"shell":
{
"label": "Shell",
"label": "Walls",
"icon": "category_shell",
"description": "Shell",
"type": "category",
@ -1066,184 +1089,6 @@
"limit_to_extruder": "wall_0_extruder_nr",
"settable_per_mesh": true
},
"roofing_extruder_nr":
{
"label": "Top Surface Skin Extruder",
"description": "The extruder train used for printing the top most skin. This is used in multi-extrusion.",
"type": "optional_extruder",
"default_value": "-1",
"value": "top_bottom_extruder_nr",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
"enabled": "extruders_enabled_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
},
"roofing_layer_count":
{
"label": "Top Surface Skin Layers",
"description": "The number of top most skin layers. Usually only one top most layer is sufficient to generate higher quality top surfaces.",
"default_value": 0,
"minimum_value": "0",
"maximum_value_warning": "top_layers - 1",
"type": "int",
"value": "0",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "top_layers > 0"
},
"top_bottom_extruder_nr":
{
"label": "Top/Bottom Extruder",
"description": "The extruder train used for printing the top and bottom skin. This is used in multi-extrusion.",
"type": "optional_extruder",
"default_value": "-1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
"enabled": "extruders_enabled_count > 1"
},
"top_bottom_thickness":
{
"label": "Top/Bottom Thickness",
"description": "The thickness of the top/bottom layers in the print. This value divided by the layer height defines the number of top/bottom layers.",
"unit": "mm",
"default_value": 0.8,
"minimum_value": "0",
"minimum_value_warning": "0.6",
"maximum_value": "machine_height",
"type": "float",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_thickness":
{
"label": "Top Thickness",
"description": "The thickness of the top layers in the print. This value divided by the layer height defines the number of top layers.",
"unit": "mm",
"default_value": 0.8,
"minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value": "machine_height",
"type": "float",
"value": "top_bottom_thickness",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_layers":
{
"label": "Top Layers",
"description": "The number of top layers. When calculated by the top thickness, this value is rounded to a whole number.",
"default_value": 8,
"minimum_value": "0",
"maximum_value_warning": "100",
"type": "int",
"minimum_value_warning": "2",
"value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"bottom_thickness":
{
"label": "Bottom Thickness",
"description": "The thickness of the bottom layers in the print. This value divided by the layer height defines the number of bottom layers.",
"unit": "mm",
"default_value": 0.6,
"minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"type": "float",
"value": "top_bottom_thickness",
"maximum_value": "machine_height",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"bottom_layers":
{
"label": "Bottom Layers",
"description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.",
"minimum_value": "0",
"minimum_value_warning": "2",
"default_value": 6,
"type": "int",
"value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"initial_bottom_layers":
{
"label": "Initial Bottom Layers",
"description": "The number of initial bottom layers, from the build-plate upwards. When calculated by the bottom thickness, this value is rounded to a whole number.",
"minimum_value": "0",
"minimum_value_warning": "2",
"default_value": 6,
"type": "int",
"value": "bottom_layers",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
}
}
},
"top_bottom_pattern":
{
"label": "Top/Bottom Pattern",
"description": "The pattern of the top/bottom layers.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"top_bottom_pattern_0":
{
"label": "Bottom Pattern Initial Layer",
"description": "The pattern on the bottom of the print on the first layer.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"enabled": "top_layers > 0 or bottom_layers > 0",
"value": "top_bottom_pattern",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"connect_skin_polygons":
{
"label": "Connect Top/Bottom Polygons",
"description": "Connect top/bottom skin paths where they run next to each other. For the concentric pattern enabling this setting greatly reduces the travel time, but because the connections can happen midway over infill this feature can reduce the top surface quality.",
"type": "bool",
"default_value": false,
"enabled": "((top_layers > 0 or bottom_layers > 0) and top_bottom_pattern == 'concentric') or (roofing_layer_count > 0 and roofing_pattern == 'concentric')",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"skin_angles":
{
"label": "Top/Bottom Line Directions",
"description": "A list of integer line directions to use when the top/bottom layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).",
"type": "[int]",
"default_value": "[ ]",
"enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"wall_0_inset":
{
"label": "Outer Wall Inset",
@ -1495,6 +1340,194 @@
"enabled": "z_seam_type == 'back'",
"limit_to_extruder": "wall_0_extruder_nr",
"settable_per_mesh": true
}
}
},
"top_bottom":
{
"label": "Top/Bottom",
"icon": "category_topbottom",
"description": "Top/Bottom",
"type": "category",
"children":
{
"roofing_extruder_nr":
{
"label": "Top Surface Skin Extruder",
"description": "The extruder train used for printing the top most skin. This is used in multi-extrusion.",
"type": "optional_extruder",
"default_value": "-1",
"value": "top_bottom_extruder_nr",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
"enabled": "extruders_enabled_count > 1 and max(extruderValues('roofing_layer_count')) > 0 and max(extruderValues('top_layers')) > 0"
},
"roofing_layer_count":
{
"label": "Top Surface Skin Layers",
"description": "The number of top most skin layers. Usually only one top most layer is sufficient to generate higher quality top surfaces.",
"default_value": 0,
"minimum_value": "0",
"maximum_value_warning": "top_layers - 1",
"type": "int",
"value": "0",
"limit_to_extruder": "roofing_extruder_nr",
"settable_per_mesh": true,
"enabled": "top_layers > 0"
},
"top_bottom_extruder_nr":
{
"label": "Top/Bottom Extruder",
"description": "The extruder train used for printing the top and bottom skin. This is used in multi-extrusion.",
"type": "optional_extruder",
"default_value": "-1",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": true,
"settable_globally": true,
"enabled": "extruders_enabled_count > 1"
},
"top_bottom_thickness":
{
"label": "Top/Bottom Thickness",
"description": "The thickness of the top/bottom layers in the print. This value divided by the layer height defines the number of top/bottom layers.",
"unit": "mm",
"default_value": 0.8,
"minimum_value": "0",
"minimum_value_warning": "0.6",
"maximum_value": "machine_height",
"type": "float",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_thickness":
{
"label": "Top Thickness",
"description": "The thickness of the top layers in the print. This value divided by the layer height defines the number of top layers.",
"unit": "mm",
"default_value": 0.8,
"minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value": "machine_height",
"type": "float",
"value": "top_bottom_thickness",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_layers":
{
"label": "Top Layers",
"description": "The number of top layers. When calculated by the top thickness, this value is rounded to a whole number.",
"default_value": 8,
"minimum_value": "0",
"maximum_value_warning": "100",
"type": "int",
"minimum_value_warning": "2",
"value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"bottom_thickness":
{
"label": "Bottom Thickness",
"description": "The thickness of the bottom layers in the print. This value divided by the layer height defines the number of bottom layers.",
"unit": "mm",
"default_value": 0.6,
"minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"type": "float",
"value": "top_bottom_thickness",
"maximum_value": "machine_height",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"bottom_layers":
{
"label": "Bottom Layers",
"description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.",
"minimum_value": "0",
"minimum_value_warning": "2",
"default_value": 6,
"type": "int",
"value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"initial_bottom_layers":
{
"label": "Initial Bottom Layers",
"description": "The number of initial bottom layers, from the build-plate upwards. When calculated by the bottom thickness, this value is rounded to a whole number.",
"minimum_value": "0",
"minimum_value_warning": "2",
"default_value": 6,
"type": "int",
"value": "bottom_layers",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
}
}
},
"top_bottom_pattern":
{
"label": "Top/Bottom Pattern",
"description": "The pattern of the top/bottom layers.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"top_bottom_pattern_0":
{
"label": "Bottom Pattern Initial Layer",
"description": "The pattern on the bottom of the print on the first layer.",
"type": "enum",
"options":
{
"lines": "Lines",
"concentric": "Concentric",
"zigzag": "Zig Zag"
},
"default_value": "lines",
"enabled": "top_layers > 0 or bottom_layers > 0",
"value": "top_bottom_pattern",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"connect_skin_polygons":
{
"label": "Connect Top/Bottom Polygons",
"description": "Connect top/bottom skin paths where they run next to each other. For the concentric pattern enabling this setting greatly reduces the travel time, but because the connections can happen midway over infill this feature can reduce the top surface quality.",
"type": "bool",
"default_value": false,
"enabled": "((top_layers > 0 or bottom_layers > 0) and top_bottom_pattern == 'concentric') or (initial_bottom_layers > 0 and top_bottom_pattern_0 == 'concentric') or (roofing_layer_count > 0 and roofing_pattern == 'concentric')",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"skin_angles":
{
"label": "Top/Bottom Line Directions",
"description": "A list of integer line directions to use when the top/bottom layers use the lines or zig zag pattern. Elements from the list are used sequentially as the layers progress and when the end of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained in square brackets. Default is an empty list which means use the traditional default angles (45 and 135 degrees).",
"type": "[int]",
"default_value": "[ ]",
"enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"skin_no_small_gaps_heuristic":
{
@ -1666,6 +1699,123 @@
"settable_per_mesh": true
}
}
},
"skin_preshrink":
{
"label": "Skin Removal Width",
"description": "The largest width of skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing top/bottom skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_skin_preshrink":
{
"label": "Top Skin Removal Width",
"description": "The largest width of top skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing top skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "skin_preshrink",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"bottom_skin_preshrink":
{
"label": "Bottom Skin Removal Width",
"description": "The largest width of bottom skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing bottom skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "skin_preshrink",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"expand_skins_expand_distance":
{
"label": "Skin Expand Distance",
"description": "The distance the skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the walls on neighboring layers adhere better to the skin. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "-skin_preshrink",
"limit_to_extruder": "top_bottom_extruder_nr",
"enabled": "top_layers > 0 or bottom_layers > 0",
"settable_per_mesh": true,
"children":
{
"top_skin_expand_distance":
{
"label": "Top Skin Expand Distance",
"description": "The distance the top skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the walls on the layer above adhere better to the skin. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "expand_skins_expand_distance",
"minimum_value": "-top_skin_preshrink",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"bottom_skin_expand_distance":
{
"label": "Bottom Skin Expand Distance",
"description": "The distance the bottom skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the skin adhere better to the walls on the layer below. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "expand_skins_expand_distance",
"minimum_value": "-bottom_skin_preshrink",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"max_skin_angle_for_expansion":
{
"label": "Maximum Skin Angle for Expansion",
"description": "Top and/or bottom surfaces of your object with an angle larger than this setting, won't have their top/bottom skin expanded. This avoids expanding the narrow skin areas that are created when the model surface has a near vertical slope. An angle of 0° is horizontal and will cause no skin to be expanded, while an angle of 90° is vertical and will cause all skin to be expanded.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "2",
"maximum_value": "90",
"default_value": 90,
"enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"min_skin_width_for_expansion":
{
"label": "Minimum Skin Width for Expansion",
"description": "Skin areas narrower than this are not expanded. This avoids expanding the narrow skin areas that are created when the model surface has a slope close to the vertical.",
"unit": "mm",
"type": "float",
"default_value": 0,
"value": "top_layers * layer_height / math.tan(math.radians(max_skin_angle_for_expansion))",
"minimum_value": "0",
"enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
}
}
},
@ -1975,123 +2125,6 @@
"limit_to_extruder": "infill_extruder_nr",
"settable_per_mesh": true
},
"skin_preshrink":
{
"label": "Skin Removal Width",
"description": "The largest width of skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing top/bottom skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"top_skin_preshrink":
{
"label": "Top Skin Removal Width",
"description": "The largest width of top skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing top skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "skin_preshrink",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"bottom_skin_preshrink":
{
"label": "Bottom Skin Removal Width",
"description": "The largest width of bottom skin areas which are to be removed. Every skin area smaller than this value will disappear. This can help in limiting the amount of time and material spent on printing bottom skin at slanted surfaces in the model.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "skin_preshrink",
"maximum_value_warning": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "0",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"expand_skins_expand_distance":
{
"label": "Skin Expand Distance",
"description": "The distance the skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the walls on neighboring layers adhere better to the skin. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x",
"minimum_value": "-skin_preshrink",
"limit_to_extruder": "top_bottom_extruder_nr",
"enabled": "top_layers > 0 or bottom_layers > 0",
"settable_per_mesh": true,
"children":
{
"top_skin_expand_distance":
{
"label": "Top Skin Expand Distance",
"description": "The distance the top skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the walls on the layer above adhere better to the skin. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "expand_skins_expand_distance",
"minimum_value": "-top_skin_preshrink",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
},
"bottom_skin_expand_distance":
{
"label": "Bottom Skin Expand Distance",
"description": "The distance the bottom skins are expanded into the infill. Higher values makes the skin attach better to the infill pattern and makes the skin adhere better to the walls on the layer below. Lower values save amount of material used.",
"unit": "mm",
"type": "float",
"default_value": 1,
"value": "expand_skins_expand_distance",
"minimum_value": "-bottom_skin_preshrink",
"enabled": "top_layers > 0 or bottom_layers > 0",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"max_skin_angle_for_expansion":
{
"label": "Maximum Skin Angle for Expansion",
"description": "Top and/or bottom surfaces of your object with an angle larger than this setting, won't have their top/bottom skin expanded. This avoids expanding the narrow skin areas that are created when the model surface has a near vertical slope. An angle of 0° is horizontal and will cause no skin to be expanded, while an angle of 90° is vertical and will cause all skin to be expanded.",
"unit": "°",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "2",
"maximum_value": "90",
"default_value": 90,
"enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true,
"children":
{
"min_skin_width_for_expansion":
{
"label": "Minimum Skin Width for Expansion",
"description": "Skin areas narrower than this are not expanded. This avoids expanding the narrow skin areas that are created when the model surface has a slope close to the vertical.",
"unit": "mm",
"type": "float",
"default_value": 0,
"value": "top_layers * layer_height / math.tan(math.radians(max_skin_angle_for_expansion))",
"minimum_value": "0",
"enabled": "(top_layers > 0 or bottom_layers > 0) and (top_skin_expand_distance > 0 or bottom_skin_expand_distance > 0)",
"limit_to_extruder": "top_bottom_extruder_nr",
"settable_per_mesh": true
}
}
},
"skin_edge_support_thickness":
{
"label": "Skin Edge Support Thickness",
@ -2960,7 +2993,7 @@
"speed_layer_0":
{
"label": "Initial Layer Speed",
"description": "The speed for the initial layer. A lower value is advised to improve adhesion to the build plate.",
"description": "The speed for the initial layer. A lower value is advised to improve adhesion to the build plate. Does not affect the build plate adhesion structures themselves, like brim and raft.",
"unit": "mm/s",
"type": "float",
"default_value": 30,
@ -6518,6 +6551,17 @@
"default_value": 50,
"enabled": "conical_overhang_enabled"
},
"conical_overhang_hole_size":
{
"label": "Maximum Overhang Hole Area",
"description": "The maximum area of a hole in the base of the model before it's removed by Make Overhang Printable. Holes smaller than this will be retained. A value of 0 mm² will fill all holes in the models base.",
"unit": "mm²",
"type": "float",
"minimum_value": "0",
"minimum_value_warning": "0",
"default_value": 0,
"enabled": "conical_overhang_enabled"
},
"coasting_enable":
{
"label": "Enable Coasting",

View file

@ -0,0 +1,44 @@
{
"version": 2,
"name": "Dreamer NX",
"inherits": "fdmprinter",
"metadata": {
"visible": false,
"author": "Egon",
"manufacturer": "Flashforge",
"file_formats": "text/x-gcode",
"first_start_actions": ["MachineSettingsAction"],
"machine_extruder_trains":
{
"0": "flashforge_dreamernx_extruder_0"
},
"has_materials": true,
"has_variants": true,
"has_machine_quality": true,
"preferred_variant_name": "0.4mm Nozzle",
"preferred_quality_type": "normal",
"preferred_material": "generic_pla",
"variants_name": "Nozzle Size"
},
"overrides": {
"machine_heated_bed": {"default_value": true},
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },
"machine_max_feedrate_z": { "value": 10 },
"machine_max_feedrate_e": { "value": 50 },
"machine_max_acceleration_x": { "value": 1500 },
"machine_max_acceleration_y": { "value": 1500 },
"machine_max_acceleration_z": { "value": 500 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_acceleration": { "value": 500 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.4 },
"machine_max_jerk_e": { "value": 5 }
},
"machine_gcode_flavor": {"default_value": "RepRap (Marlin/Sprinter)"},
"machine_start_gcode": {"default_value": ";Start Gcode\nG90 ;absolute positioning\nM118 X25.00 Y25.00 Z20.00 T0\nM140 S{material_bed_temperature_layer_0} T0 ;Heat bed up to first layer temperature\nM104 S{material_print_temperature_layer_0} T0 ;Set nozzle temperature to first layer temperature\nM107 ;start with the fan off\nG90\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651\nM907 X100 Y100 Z40 A100 B20 ;Digital potentiometer value\nM108 T0\n;Purge line\nG1 X-110.00 Y-60.00 F4800\nG1 Z{layer_height_0} F420\nG1 X-110.00 Y60.00 E17,4 F1200\n;Purge line end"},
"machine_end_gcode": {"default_value": ";end gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM652\nM132 X Y Z A B\nG91\nM18"
}
}

View file

@ -0,0 +1,31 @@
{
"version": 2,
"name": "Dreamer NX",
"inherits": "flashforge_base",
"metadata": {
"visible": true,
"author": "Egon",
"manufacturer": "Flashforge",
"file_formats": "text/x-gcode",
"platform": "FlashForge_DreamerNX.obj",
"platform_offset": [0, 0, 0],
"quality_definition": "flashforge_base"
},
"overrides": {
"machine_name": { "default_value": "Dreamer NX" },
"machine_width": {"default_value": 230},
"machine_height": {"default_value": 140},
"machine_depth": {"default_value": 150},
"machine_center_is_zero": {"default_value": true},
"machine_head_with_fans_polygon":{"default_value": [[ -15, -25 ],[ -15, 35 ],[ 40, 35 ],[ 40, -25 ]]
},
"gantry_height": {"value": "30"},
"machine_gcode_flavor": {"default_value": "RepRap (Marlin/Sprinter)"},
"machine_start_gcode": {"default_value": ";Start Gcode\nG90 ;absolute positioning\nM118 X25.00 Y25.00 Z20.00 T0\nM140 S{material_bed_temperature_layer_0} T0 ;Heat bed up to first layer temperature\nM104 S{material_print_temperature_layer_0} T0 ;Set nozzle temperature to first layer temperature\nM107 ;start with the fan off\nG90\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651\nM907 X100 Y100 Z40 A100 B20 ;Digital potentiometer value\nM108 T0\n;Purge line\nG1 X-110.00 Y-60.00 F4800\nG1 Z{layer_height_0} F420\nG1 X-110.00 Y60.00 E17,4 F1200\n;Purge line end"},
"machine_end_gcode": {"default_value": ";end gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM652\nM132 X Y Z A B\nG91\nM18"
}
}
}

View file

@ -152,7 +152,7 @@
"machine_start_gcode": { "default_value": "M220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\n;Code for nozzle cleaning and flow normalization\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.4 Y20 Z0.28 F5000.0\nG1 X10.4 Y170.0 Z0.28 F1500.0 E15\nG1 X10.1 Y170.0 Z0.28 F5000.0\nG1 X10.1 Y40 Z0.28 F1500.0 E30\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract the filament\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG28 X0 Y0 ;Home X and Y\n\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract the filament\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG28 X0 Y0 ;Home X and Y\n\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z" },
"machine_heated_bed": { "default_value": true },
"machine_shape": { "default_value": "rectangular" },
@ -256,4 +256,4 @@
"adaptive_layer_height_variation": { "value": 0.04 },
"adaptive_layer_height_variation_step": { "value": 0.04 }
}
}
}

View file

@ -10,7 +10,7 @@
"overrides": {
"machine_extruder_count": { "value": 2 },
"machine_name": { "default_value": "FF300 Doppia" },
"machine_width": { "default_value": 320 },
"machine_width": { "default_value": 360 },
"machine_depth": { "default_value": 300 },
"machine_height": { "default_value": 320 },
"machine_max_feedrate_x": { "default_value": 100 },

View file

@ -25,6 +25,9 @@
"machine_max_jerk_z": { "default_value": 0.3 },
"machine_max_jerk_e": { "default_value": 5 },
"acceleration_travel": {"value":900}
}
}

View file

@ -54,7 +54,7 @@
"retraction_amount": { "default_value": 4 },
"retraction_speed": { "default_value": 70},
"retraction_min_travel": {"value":5 },
"retraction_count_max": {"value":10 },
"retraction_count_max": {"default_value":10 },
"retraction_extrusion_window": {"value":4 },
"retraction_hop": {"default_value":0.2},
"retraction_hop_enabled": {"value":true},

View file

@ -54,7 +54,7 @@
"retraction_amount": { "default_value": 4 },
"retraction_speed": { "default_value": 70},
"retraction_min_travel": {"value":5 },
"retraction_count_max": {"value":10 },
"retraction_count_max": {"default_value":10 },
"retraction_extrusion_window": {"value":4 },
"retraction_hop": {"default_value":0.2},
"retraction_hop_enabled": {"value":true},
@ -77,7 +77,9 @@
"support_xy_distance": {"value": 0.5},
"support_z_distance": {"value": 0.3 },
"adhesion_type": {"default_value":"skirt"},
"adhesion_type": {"default_value":"skirt"}
"switch_extruder_retraction_amount": { "value": 6 },
"switch_extruder_retraction_speeds": { "value": 60 }
}
}

View file

@ -46,10 +46,10 @@
"machine_heated_bed": { "default_value": true },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": {
"default_value": "G28 \nG1 Z15 F300\nM107\nG90\nM82\nM104 S215\nM140 S55\nG92 E0\nM109 S215\nM107\nG0 X10 Y20 F6000\nG1 Z0.8\nG1 F300 X180 E40\nG1 F1200 Z2\nG92 E0\nG28"
"default_value": "G28 ; Auto home\nG1 Z15 F300 ;Move up slightly\nM107 ;Off fans\nG90 ;Switch to absolute positioning\nM82 ;Extruder absolute mode\nG92 E0 ;Set position of extruder to 0\nG0 X10 Y20 F1500 ;Move to X10 Y20 at 1500mms\nG1 Z0.8 ;Move Z to 0.8\nG1 F300 X180 E40 ;Extrude a line of filament\nG1 F1200 Z2 ;Raise Z\nG92 E0 ;Set extruder position to zero\nG28 ;Auto home"
},
"machine_end_gcode": {
"default_value": "G91\nG1 E-1\nG0 X0 Y200\nM104 S0\nG90\nG92 E0\nM140 S0\nM84\nM104 S0\nM140 S0\nM84"
"default_value": "G91 ;Switch to relative positioning\nG1 E-1 ;Retract filament to lower pressure\nG0 X0 Y200 ;Move hotend to left and bed forward\nM104 S0 ;Cooldown hotend\nG90 ;Switch to absolute mode\nG92 E0 ;Set extruder to zero\nM140 S0 ;Cooldown bed\nM84 ; Disable steppers"
},
"machine_extruder_count": {
"default_value": 1

View file

@ -0,0 +1,59 @@
{
"version": 2,
"name": "Hellbot Hidra",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "hellbot_hidra.obj",
"platform_offset": [0, 0, 5],
"platform_texture": "hellbot_hidra.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_hidra_extruder_0",
"1": "hellbot_hidra_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Hidra" },
"machine_width": {
"default_value": 220
},
"machine_depth": {
"default_value": 220
},
"machine_height": {
"default_value": 250
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -75, 35 ],
[ -75, -18 ],
[ 18, 35 ],
[ 18, -18 ]
]
},
"machine_extruder_count": {
"default_value": 2
},
"machine_start_gcode": {
"default_value": "G21; Unidades en Milimetro\nG90; Posicionamiento Absoluto\nM82; E Absoluto\nM107; Apagar Venitilador de capas\nG28; Llevar ejes a origen\nG1 Z15.0 F9000; Levantar Eje Z 15mm"
},
"machine_end_gcode": {
"default_value": "M104 T0 S0; Apagar Extrusor E0\nM104 T1 S0; Apagar Extrusor E1\nM140 S0; Apagar Cama Caliente\nG92 E1; Posicionar Extrusor en 1mm\nG1 E-1 F300; Retraer Extrusor 1mm\nG28 X0 Y0; Llevar al origen ejes X e Y\nM84; Desactivar Motores "
}
}
}

View file

@ -0,0 +1,59 @@
{
"version": 2,
"name": "Hellbot Hidra Plus",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "hellbot_hidra_plus.obj",
"platform_offset": [0, 0, 5],
"platform_texture": "hellbot_hidra_plus.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_hidra_plus_extruder_0",
"1": "hellbot_hidra_plus_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Hidra Plus" },
"machine_width": {
"default_value": 305
},
"machine_depth": {
"default_value": 305
},
"machine_height": {
"default_value": 350
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -75, 35 ],
[ -75, -18 ],
[ 18, 35 ],
[ 18, -18 ]
]
},
"machine_extruder_count": {
"default_value": 2
},
"machine_start_gcode": {
"default_value": "G21; Unidades en Milimetro\nG90; Posicionamiento Absoluto\nM82; E Absoluto\nM107; Apagar Venitilador de capas\nG28; Llevar ejes a origen\nG1 Z15.0 F9000; Levantar Eje Z 15mm"
},
"machine_end_gcode": {
"default_value": "M104 T0 S0; Apagar Extrusor E0\nM104 T1 S0; Apagar Extrusor E1\nM140 S0; Apagar Cama Caliente\nG92 E1; Posicionar Extrusor en 1mm\nG1 E-1 F300; Retraer Extrusor 1mm\nG28 X0 Y0; Llevar al origen ejes X e Y\nM84; Desactivar Motores "
}
}
}

View file

@ -0,0 +1,42 @@
{
"version": 2,
"name": "Hellbot Magna 2 230",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "Hellbot_Magna_2_230.obj",
"platform_texture": "Magna2_230.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_magna_2_230_extruder_0"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Magna 2 230" },
"machine_width": {
"default_value": 230
},
"machine_height": {
"default_value": 250
},
"machine_depth": {
"default_value": 230
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_extruder_count": {
"default_value": 1
}
}
}

View file

@ -0,0 +1,49 @@
{
"version": 2,
"name": "Hellbot Magna 2 230 dual",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "Hellbot_Magna_2_230.obj",
"platform_texture": "Magna2_230.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_magna_2_230_dual_extruder_0",
"1": "hellbot_magna_2_230_dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Magna 2 230 dual" },
"machine_width": {
"default_value": 230
},
"machine_height": {
"default_value": 250
},
"machine_depth": {
"default_value": 230
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_extruder_count": {
"default_value": 2
},
"machine_start_gcode": {
"default_value": "G21\nG90\nM107\nG28 X0 Y0\nG28 Z0\nG1 Z15.0 F300\nT0\nG92 E0\nG1 F700 E-80\nT1\nG92 E0\nG1 F1000 X1 Y1 Z0.3\nG1 F600 X200 E60\nG1 F1000 Y3\nG1 F600 X1 E120\nT1\nG92 E0\nG28 X0 Y0\nG1 F700 E-80\nT0\nG92 E0"
},
"machine_end_gcode": {
"default_value": "M104 T0 S0\nM104 T1 S0\nM140 S0\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84"
}
}
}

View file

@ -0,0 +1,42 @@
{
"version": 2,
"name": "Hellbot Magna 2 300",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "Hellbot_Magna_2_300.obj",
"platform_texture": "Magna2_300.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_magna_2_300_extruder_0"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Magna 2 300" },
"machine_width": {
"default_value": 300
},
"machine_height": {
"default_value": 400
},
"machine_depth": {
"default_value": 300
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_extruder_count": {
"default_value": 1
}
}
}

View file

@ -0,0 +1,49 @@
{
"version": 2,
"name": "Hellbot Magna 2 300 dual",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "Hellbot_Magna_2_300.obj",
"platform_texture": "Magna2_300.png",
"has_materials": true,
"machine_extruder_trains":
{
"0": "hellbot_magna_2_300_dual_extruder_0",
"1": "hellbot_magna_2_300_dual_extruder_1"
}
},
"overrides": {
"machine_name": { "default_value": "Hellbot Magna 2 300 Dual" },
"machine_width": {
"default_value": 300
},
"machine_height": {
"default_value": 400
},
"machine_depth": {
"default_value": 300
},
"machine_heated_bed": {
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
},
"machine_extruder_count": {
"default_value": 2
},
"machine_start_gcode": {
"default_value": "G21\nG90\nM107\nG28 X0 Y0\nG28 Z0\nG1 Z15.0 F300\nT0\nG92 E0\nG1 F700 E-80\nT1\nG92 E0\nG1 F1000 X1 Y1 Z0.3\nG1 F600 X200 E60\nG1 F1000 Y3\nG1 F600 X1 E120\nT1\nG92 E0\nG28 X0 Y0\nG1 F700 E-80\nT0\nG92 E0"
},
"machine_end_gcode": {
"default_value": "M104 T0 S0\nM104 T1 S0\nM140 S0\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84"
}
}
}

View file

@ -4,7 +4,7 @@
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "MUX team",
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "hellbot_magna.obj",
@ -19,6 +19,7 @@
"machine_name": {
"default_value": "Hellbot Magna 1"
},
"machine_heated_bed": { "default_value": true },
"machine_width": {
"default_value": 220
},

View file

@ -4,7 +4,7 @@
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "MUX team",
"author": "Hellbot Development Team",
"manufacturer": "Hellbot",
"file_formats": "text/x-gcode",
"platform": "hellbot_magna.obj",
@ -26,6 +26,9 @@
"machine_depth": {
"default_value": 220
},
"machine_heated_bed": {
"default_value": true
},
"machine_height": {
"default_value": 260
},

View file

@ -21,14 +21,14 @@
"machine_depth": { "default_value": 300 },
"machine_height": { "default_value": 350 },
"machine_head_with_fans_polygon": { "default_value": [
[-20, -10],
[-20, 10],
[10, -10],
[10, 10]
[-30, -20],
[-30, 40],
[30, -20],
[30, 40]
]
},
"machine_start_gcode": { "default_value": ";Start GCode by ideagen3D\n\nG1 Z15.0 F6000 ;Move the platform down 15mm\n\n;Initialize Temperature\nM140 S{material_bed_temperature_layer_0} ;heat bed and continue\nM104 S{material_print_temperature_layer_0} ;heat nozzle and continue\nM190 S{material_bed_temperature_layer_0} ;wait for bed temperature to reach inital layer temperature\nM109 S{material_print_temperature_layer_0} ;wait for hot end temperature to reach inital layer temperature\n\nG28 ;Home\n\n;Prime the extruder\nG92 E0\nG1 X1 Y280 Z0.2 ;Prepare to Purge\nG1 Y20 Z0.2 F1500.0 E15 ;Purge line\nG92 E0" },
"machine_start_gcode": { "default_value": ";Start GCode by ideagen3D\n\nG1 Z15.0 F6000 ;Move the platform down 15mm\n\n;Initialize Temperature\nM140 S{material_bed_temperature_layer_0} ;heat bed and continue\nM104 S{material_print_temperature_layer_0} ;heat nozzle and continue\nM190 S{material_bed_temperature_layer_0} ;wait for bed temperature to reach inital layer temperature\nM109 S{material_print_temperature_layer_0} ;wait for hot end temperature to reach inital layer temperature\n\nG28 M420 S1 ; Home & Enable Bed Levelling\n\n;Prime the extruder\nG92 E0\nG1 X1 Y280 Z0.2 ;Prepare to Purge\nG1 Y20 Z0.2 F1500.0 E15 ;Purge line\nG92 E0" },
"machine_end_gcode": { "default_value": ";End GCode by ideagen3D\n\nM104 S0 ;Set nozzle temperature to 0\nM140 S0 ;Set Bed temperature to 0\n\nG92 E1 ;Prepare to retract filament\nG1 E-1 F300 ;Retract filament\nG28 X0 Y0 ;Home X and Y\nM84 ;Disable Steppers" },
"gantry_height": { "value": 350 }
"gantry_height": { "value": 65 }
}
}
}

View file

@ -0,0 +1,469 @@
{
"name": "INAT Base description",
"version": 2,
"inherits": "fdmprinter",
"metadata": {
"author": "INAT s.r.o.",
"manufacturer": "INAT s.r.o.",
"file_formats": "text/x-gcode",
"visible": false,
"has_materials": true,
"has_variants": false,
"variants_name": "Extruder:",
"preferred_variant_name": "0.4mm",
"has_machine_quality": true,
"preferred_material": "generic_pla",
"preferred_quality_type": "standard",
"machine_extruder_trains": {
"0": "inat_extruder_0"
},
"first_start_actions": [
"MachineSettingsAction"
]
},
"overrides": {
"machine_start_gcode": {
"default_value": "G28 ;Home\nG0 Z0.6 F200 ;Move nozzle down\nM192 ; Wait for probe temperature to settle\nG28 Z\nG29\nG0 X0 Y0 Z30 F6000\nM84 E\nM0\nG1 Z15.0 F6000 ;Move the platform down 15mm\n"
},
"machine_end_gcode": {
"default_value": "M400\nM104 S0\nM140 S0\nM107\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 R5 X\nG0 Y300 F3000\nM84\n"
},
"material_diameter": {
"default_value": 1.75
},
"machine_shape": {
"default_value": "rectangular"
},
"machine_heated_bed": {
"default_value": true
},
"machine_heated_build_volume": {
"default_value": false
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_head_with_fans_polygon": {
"default_value": [
[
-86,
66
],
[
44,
66
],
[
44,
-96
],
[
-86,
-96
]
]
},
"gantry_height": { "value": 34 },
"machine_nozzle_size": {
"default_value": 0.4
},
"machine_max_feedrate_x": {
"value": 200
},
"machine_max_feedrate_y": {
"value": 200
},
"machine_max_feedrate_z": {
"value": 10
},
"machine_max_feedrate_e": {
"value": 100
},
"machine_max_acceleration_x": {
"value": 500
},
"machine_max_acceleration_y": {
"value": 500
},
"machine_max_acceleration_z": {
"value": 200
},
"machine_max_acceleration_e": {
"value": 2000
},
"machine_acceleration": {
"value": 500
},
"machine_max_jerk_xy": {
"value": 8
},
"machine_max_jerk_z": {
"value": 2.0
},
"machine_max_jerk_e": {
"value": 5.0
},
"layer_height": {
"value": 0.2
},
"layer_height_0": {
"value": "machine_nozzle_size / 2.0"
},
"line_width": {
"value": "1.05 * machine_nozzle_size",
"minimum_value_warning": "0.8 * machine_nozzle_size",
"maximum_value_warning": "1.6 * machine_nozzle_size"
},
"wall_thickness": {
"value": "max(3*wall_line_width, 1)"
},
"roofing_layer_count": {
"value": 2
},
"top_bottom_thickness": {
"value": 1.0
},
"top_bottom_pattern": {
"value": "'zigzag'"
},
"optimize_wall_printing_order": {
"value": true
},
"xy_offset_layer_0": {
"value": "-0.5*line_width"
},
"skin_outline_count": {
"value": 2
},
"infill_sparse_density": {
"value": 30
},
"infill_pattern": {
"value": "'zigzag' if infill_sparse_density > 50 else 'cubic'"
},
"infill_sparse_thickness": {
"value": "layer_height if (2*layer_height > 0.8*machine_nozzle_size) else 2*layer_height"
},
"infill_before_walls": {
"value": false
},
"expand_skins_expand_distance": {
"value": "4"
},
"default_material_print_temperature": {
"maximum_value_warning": "470",
"maximum_value": "470"
},
"material_print_temperature": {
"maximum_value_warning": "470",
"maximum_value": "470"
},
"material_print_temperature_layer_0": {
"value": "material_print_temperature",
"maximum_value_warning": "470",
"maximum_value": "470"
},
"material_initial_print_temperature": {
"value": "material_print_temperature",
"maximum_value_warning": "470",
"maximum_value": "470"
},
"material_final_print_temperature": {
"value": "material_print_temperature",
"maximum_value_warning": "470",
"maximum_value": "470"
},
"default_material_bed_temperature": {
"maximum_value_warning": "150",
"maximum_value": "150"
},
"material_bed_temperature": {
"maximum_value_warning": "150",
"maximum_value": "150"
},
"material_bed_temperature_layer_0": {
"maximum_value_warning": "150",
"maximum_value": "150"
},
"speed_infill": {
"value": "speed_print"
},
"speed_wall": {
"value": "speed_print"
},
"speed_wall_0": {
"value": "0.5 * speed_wall"
},
"speed_wall_x": {
"value": "speed_wall"
},
"speed_roofing": {
"value": "speed_wall_0"
},
"speed_topbottom": {
"value": "speed_print"
},
"speed_travel": {
"value": "150"
},
"speed_layer_0": {
"value": "30"
},
"speed_travel_layer_0": {
"value": "0.5 * speed_travel"
},
"speed_z_hop": {
"value": 10
},
"acceleration_print": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500",
"value": 500
},
"acceleration_infill": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_wall": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_wall_0": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_wall_x": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_roofing": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_topbottom": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_support": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_support_infill": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_support_interface": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_support_roof": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_support_bottom": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_prime_tower": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_travel": {
"value": "acceleration_print",
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_layer_0": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_print_layer_0": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_travel_layer_0": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"acceleration_skirt_brim": {
"minimum_value_warning": "100",
"maximum_value_warning": "1500"
},
"jerk_print": {
"maximum_value_warning": "20",
"value": 8
},
"jerk_infill": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_wall": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_wall_0": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_wall_x": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_roofing": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_topbottom": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_support": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_support_infill": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_support_interface": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_support_roof": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_support_bottom": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_prime_tower": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_travel": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_layer_0": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_print_layer_0": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_travel_layer_0": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"jerk_skirt_brim": {
"maximum_value_warning": "20",
"value": "jerk_print"
},
"retraction_amount": {
"value": 1.8,
"maximum_value_warning": "2.0"
},
"retraction_speed": {
"value": 45
},
"retraction_combing": {
"value": "infill"
},
"retraction_hop_enabled": {
"value": true
},
"retraction_hop": {
"value": "3*layer_height"
},
"cool_fan_speed_min": {
"value": "0.5*cool_fan_speed"
},
"cool_min_layer_time_fan_speed_max": {
"value": 10
},
"support_angle": {
"value": 60
},
"support_wall_count": {
"value": "1 if (support_structure == 'tree') else 0"
},
"support_infill_rate": {
"value": 10
},
"support_infill_angles": {
"value": "[45]"
},
"support_z_distance": {
"value": "layer_height if (2*layer_height > 0.8*machine_nozzle_size) else (2*layer_height)"
},
"support_join_distance": {
"value": 5.0
},
"support_offset": {
"value": 3.0
},
"support_infill_sparse_thickness": {
"value": "infill_sparse_thickness"
},
"support_interface_enable": {
"value": true
},
"support_interface_height": {
"value": 1.0
},
"support_interface_density": {
"value": 80
},
"support_interface_pattern": {
"value": "'grid'"
},
"support_interface_offset": {
"value": "support_offset"
},
"support_fan_enable": {
"value": true
},
"support_use_towers": {
"value": false
},
"support_tree_angle": {
"value": 60
},
"adhesion_type": {
"value": "'skirt'"
},
"skirt_line_count": {
"value": 5
},
"skirt_brim_minimal_length": {
"value": 500
},
"skirt_gap": {
"value": 10
},
"brim_outside_only": {
"value": false
},
"raft_margin": {
"value": 10
},
"raft_airgap": {
"value": "0.5 * layer_height"
},
"raft_interface_thickness": {
"value": "0.8*machine_nozzle_size",
"maximum_value_warning": "0.8 * machine_nozzle_size"
},
"raft_interface_line_width": {
"value": "line_width"
},
"raft_base_line_width": {
"value": "raft_interface_line_width"
},
"raft_base_speed": {
"value": "speed_layer_0"
}
}
}

Some files were not shown because too many files have changed in this diff Show more