mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 14:37:29 -06:00
Merge remote-tracking branch 'origin/main' into optimized-prime-tower
This commit is contained in:
commit
e642d85fcd
478 changed files with 7034 additions and 722 deletions
8
.github/ISSUE_TEMPLATE/SlicingCrash.yaml
vendored
8
.github/ISSUE_TEMPLATE/SlicingCrash.yaml
vendored
|
@ -5,6 +5,13 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### 💥 Slicing Crash Analysis Tool 💥
|
||||
We are taking steps to analyze an increase in reported crashes more systematically. We'll need some help with that. 😇
|
||||
Before filling out the report below, we want you to try a special Cura 5.7 Alpha.
|
||||
This version of Cura has an updated slicing engine that will automatically send a report to the Cura Team for analysis.
|
||||
#### [You can find the downloads here](https://github.com/Ultimaker/Cura/discussions/18080) ####
|
||||
If you still encounter a crash you are still welcome to report the issue so we can use your model as a test case, you can find instructions on how to do that below.
|
||||
|
||||
### Project File
|
||||
**⚠️ Before you continue, we need your project file to troubleshoot a slicing crash.**
|
||||
It contains the printer and settings we need for troubleshooting.
|
||||
|
@ -68,4 +75,3 @@ body:
|
|||
description: You can add the zip file and additional information that is relevant to the issue in the comments below.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ exe = EXE(
|
|||
target_arch={{ target_arch }},
|
||||
codesign_identity=os.getenv('CODESIGN_IDENTITY', None),
|
||||
entitlements_file={{ entitlements_file }},
|
||||
icon={{ icon }}
|
||||
icon={{ icon }},
|
||||
contents_directory='.'
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
|
@ -70,188 +71,7 @@ coll = COLLECT(
|
|||
)
|
||||
|
||||
{% if macos == true %}
|
||||
# PyInstaller seems to copy everything in the resource folder for the MacOS, this causes issues with codesigning and notarizing
|
||||
# The folder structure should adhere to the one specified in Table 2-5
|
||||
# https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1
|
||||
# The class below is basically ducktyping the BUNDLE class of PyInstaller and using our own `assemble` method for more fine-grain and specific
|
||||
# control. Some code of the method below is copied from:
|
||||
# https://github.com/pyinstaller/pyinstaller/blob/22d1d2a5378228744cc95f14904dae1664df32c4/PyInstaller/building/osx.py#L115
|
||||
#-----------------------------------------------------------------------------
|
||||
# Copyright (c) 2005-2022, PyInstaller Development Team.
|
||||
#
|
||||
# Distributed under the terms of the GNU General Public License (version 2
|
||||
# or later) with exception for distributing the bootloader.
|
||||
#
|
||||
# The full license is in the file COPYING.txt, distributed with this software.
|
||||
#
|
||||
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
import plistlib
|
||||
import shutil
|
||||
import PyInstaller.utils.osx as osxutils
|
||||
from pathlib import Path
|
||||
from PyInstaller.building.osx import BUNDLE
|
||||
from PyInstaller.building.utils import (_check_path_overlap, _rmtree, add_suffix_to_extension, checkCache)
|
||||
from PyInstaller.building.datastruct import logger
|
||||
from PyInstaller.building.icon import normalize_icon_type
|
||||
|
||||
|
||||
class UMBUNDLE(BUNDLE):
|
||||
def assemble(self):
|
||||
from PyInstaller.config import CONF
|
||||
|
||||
if _check_path_overlap(self.name) and os.path.isdir(self.name):
|
||||
_rmtree(self.name)
|
||||
logger.info("Building BUNDLE %s", self.tocbasename)
|
||||
|
||||
# Create a minimal Mac bundle structure.
|
||||
macos_path = Path(self.name, "Contents", "MacOS")
|
||||
resources_path = Path(self.name, "Contents", "Resources")
|
||||
frameworks_path = Path(self.name, "Contents", "Frameworks")
|
||||
os.makedirs(macos_path)
|
||||
os.makedirs(resources_path)
|
||||
os.makedirs(frameworks_path)
|
||||
|
||||
# Makes sure the icon exists and attempts to convert to the proper format if applicable
|
||||
self.icon = normalize_icon_type(self.icon, ("icns",), "icns", CONF["workpath"])
|
||||
|
||||
# Ensure icon path is absolute
|
||||
self.icon = os.path.abspath(self.icon)
|
||||
|
||||
# Copy icns icon to Resources directory.
|
||||
shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources'))
|
||||
|
||||
# Key/values for a minimal Info.plist file
|
||||
info_plist_dict = {
|
||||
"CFBundleDisplayName": self.appname,
|
||||
"CFBundleName": self.appname,
|
||||
|
||||
# Required by 'codesign' utility.
|
||||
# The value for CFBundleIdentifier is used as the default unique name of your program for Code Signing
|
||||
# purposes. It even identifies the APP for access to restricted OS X areas like Keychain.
|
||||
#
|
||||
# The identifier used for signing must be globally unique. The usual form for this identifier is a
|
||||
# hierarchical name in reverse DNS notation, starting with the toplevel domain, followed by the company
|
||||
# name, followed by the department within the company, and ending with the product name. Usually in the
|
||||
# form: com.mycompany.department.appname
|
||||
# CLI option --osx-bundle-identifier sets this value.
|
||||
"CFBundleIdentifier": self.bundle_identifier,
|
||||
"CFBundleExecutable": os.path.basename(self.exename),
|
||||
"CFBundleIconFile": os.path.basename(self.icon),
|
||||
"CFBundleInfoDictionaryVersion": "6.0",
|
||||
"CFBundlePackageType": "APPL",
|
||||
"CFBundleVersionString": self.version,
|
||||
"CFBundleShortVersionString": self.version,
|
||||
}
|
||||
|
||||
# Set some default values. But they still can be overwritten by the user.
|
||||
if self.console:
|
||||
# Setting EXE console=True implies LSBackgroundOnly=True.
|
||||
info_plist_dict['LSBackgroundOnly'] = True
|
||||
else:
|
||||
# Let's use high resolution by default.
|
||||
info_plist_dict['NSHighResolutionCapable'] = True
|
||||
|
||||
# Merge info_plist settings from spec file
|
||||
if isinstance(self.info_plist, dict) and self.info_plist:
|
||||
info_plist_dict.update(self.info_plist)
|
||||
|
||||
plist_filename = os.path.join(self.name, "Contents", "Info.plist")
|
||||
with open(plist_filename, "wb") as plist_fh:
|
||||
plistlib.dump(info_plist_dict, plist_fh)
|
||||
|
||||
links = []
|
||||
_QT_BASE_PATH = {'PySide2', 'PySide6', 'PyQt5', 'PyQt6', 'PySide6'}
|
||||
for inm, fnm, typ in self.toc:
|
||||
# Adjust name for extensions, if applicable
|
||||
inm, fnm, typ = add_suffix_to_extension(inm, fnm, typ)
|
||||
inm = Path(inm)
|
||||
fnm = Path(fnm)
|
||||
# Copy files from cache. This ensures that are used files with relative paths to dynamic library
|
||||
# dependencies (@executable_path)
|
||||
if typ in ('EXTENSION', 'BINARY') or (typ == 'DATA' and inm.suffix == '.so'):
|
||||
if any(['.' in p for p in inm.parent.parts]):
|
||||
inm = Path(inm.name)
|
||||
fnm = Path(checkCache(
|
||||
str(fnm),
|
||||
strip = self.strip,
|
||||
upx = self.upx,
|
||||
upx_exclude = self.upx_exclude,
|
||||
dist_nm = str(inm),
|
||||
target_arch = self.target_arch,
|
||||
codesign_identity = self.codesign_identity,
|
||||
entitlements_file = self.entitlements_file,
|
||||
strict_arch_validation = (typ == 'EXTENSION'),
|
||||
))
|
||||
frame_dst = frameworks_path.joinpath(inm)
|
||||
if not frame_dst.exists():
|
||||
if frame_dst.is_dir():
|
||||
os.makedirs(frame_dst, exist_ok = True)
|
||||
else:
|
||||
os.makedirs(frame_dst.parent, exist_ok = True)
|
||||
shutil.copy(fnm, frame_dst, follow_symlinks = True)
|
||||
macos_dst = macos_path.joinpath(inm)
|
||||
if not macos_dst.exists():
|
||||
if macos_dst.is_dir():
|
||||
os.makedirs(macos_dst, exist_ok = True)
|
||||
else:
|
||||
os.makedirs(macos_dst.parent, exist_ok = True)
|
||||
|
||||
# Create relative symlink to the framework
|
||||
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Frameworks").joinpath(
|
||||
frame_dst.relative_to(frameworks_path))
|
||||
try:
|
||||
macos_dst.symlink_to(symlink_to)
|
||||
except FileExistsError:
|
||||
pass
|
||||
else:
|
||||
if typ == 'DATA':
|
||||
if any(['.' in p for p in inm.parent.parts]) or inm.suffix == '.so':
|
||||
# Skip info dist egg and some not needed folders in tcl and tk, since they all contain dots in their files
|
||||
logger.warning(f"Skipping DATA file {inm}")
|
||||
continue
|
||||
res_dst = resources_path.joinpath(inm)
|
||||
if not res_dst.exists():
|
||||
if res_dst.is_dir():
|
||||
os.makedirs(res_dst, exist_ok = True)
|
||||
else:
|
||||
os.makedirs(res_dst.parent, exist_ok = True)
|
||||
shutil.copy(fnm, res_dst, follow_symlinks = True)
|
||||
macos_dst = macos_path.joinpath(inm)
|
||||
if not macos_dst.exists():
|
||||
if macos_dst.is_dir():
|
||||
os.makedirs(macos_dst, exist_ok = True)
|
||||
else:
|
||||
os.makedirs(macos_dst.parent, exist_ok = True)
|
||||
|
||||
# Create relative symlink to the resource
|
||||
symlink_to = Path(*[".." for p in macos_dst.relative_to(macos_path).parts], "Resources").joinpath(
|
||||
res_dst.relative_to(resources_path))
|
||||
try:
|
||||
macos_dst.symlink_to(symlink_to)
|
||||
except FileExistsError:
|
||||
pass
|
||||
else:
|
||||
macos_dst = macos_path.joinpath(inm)
|
||||
if not macos_dst.exists():
|
||||
if macos_dst.is_dir():
|
||||
os.makedirs(macos_dst, exist_ok = True)
|
||||
else:
|
||||
os.makedirs(macos_dst.parent, exist_ok = True)
|
||||
shutil.copy(fnm, macos_dst, follow_symlinks = True)
|
||||
|
||||
# Sign the bundle
|
||||
logger.info('Signing the BUNDLE...')
|
||||
try:
|
||||
osxutils.sign_binary(self.name, self.codesign_identity, self.entitlements_file, deep = True)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error while signing the bundle: {e}")
|
||||
logger.warning("You will need to sign the bundle manually!")
|
||||
|
||||
logger.info(f"Building BUNDLE {self.tocbasename} completed successfully.")
|
||||
|
||||
app = UMBUNDLE(
|
||||
app = BUNDLE(
|
||||
coll,
|
||||
name='{{ display_name }}.app',
|
||||
icon={{ icon }},
|
||||
|
@ -271,9 +91,10 @@ app = UMBUNDLE(
|
|||
'CFBundleURLSchemes': ['cura', 'slicer'],
|
||||
}],
|
||||
'CFBundleDocumentTypes': [{
|
||||
'CFBundleTypeRole': 'Viewer',
|
||||
'CFBundleTypeExtensions': ['*'],
|
||||
'CFBundleTypeName': 'Model Files',
|
||||
}]
|
||||
},
|
||||
){% endif %}
|
||||
'CFBundleTypeRole': 'Viewer',
|
||||
'CFBundleTypeExtensions': ['stl', 'obj', '3mf', 'gcode', 'ufp'],
|
||||
'CFBundleTypeName': 'Model Files',
|
||||
}]
|
||||
},
|
||||
)
|
||||
{% endif %}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
version: "5.7.0-alpha.0"
|
||||
version: "5.7.0-alpha.1"
|
||||
requirements:
|
||||
- "uranium/(latest)@ultimaker/testing"
|
||||
- "curaengine/(latest)@ultimaker/testing"
|
||||
- "cura_binary_data/(latest)@ultimaker/testing"
|
||||
- "fdm_materials/(latest)@ultimaker/testing"
|
||||
- "curaengine_plugin_gradual_flow/(latest)@ultimaker/stable"
|
||||
- "curaengine_plugin_gradual_flow/0.1.0-beta.2"
|
||||
- "dulcificum/latest@ultimaker/testing"
|
||||
- "pysavitar/5.3.0"
|
||||
- "pynest2d/5.3.0"
|
||||
|
@ -118,7 +118,6 @@ pyinstaller:
|
|||
- "sqlite3"
|
||||
- "trimesh"
|
||||
- "win32ctypes"
|
||||
- "PyQt6"
|
||||
- "PyQt6.QtNetwork"
|
||||
- "PyQt6.sip"
|
||||
- "stl"
|
||||
|
@ -160,6 +159,10 @@ pycharm_targets:
|
|||
module_name: Cura
|
||||
name: pytest in TestGCodeListDecorator.py
|
||||
script_name: tests/TestGCodeListDecorator.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestHitChecker.py
|
||||
script_name: tests/TestHitChecker.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestIntentManager.py
|
||||
|
@ -188,6 +191,10 @@ pycharm_targets:
|
|||
module_name: Cura
|
||||
name: pytest in TestPrintInformation.py
|
||||
script_name: tests/TestPrintInformation.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestPrintOrderManager.py
|
||||
script_name: tests/TestPrintOrderManager.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestProfileRequirements.py
|
||||
|
|
22
conanfile.py
22
conanfile.py
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Template
|
||||
|
@ -150,6 +151,7 @@ class CuraConan(ConanFile):
|
|||
return "None"
|
||||
|
||||
def _conan_installs(self):
|
||||
self.output.info("Collecting conan installs")
|
||||
conan_installs = {}
|
||||
|
||||
# list of conan installs
|
||||
|
@ -161,13 +163,22 @@ class CuraConan(ConanFile):
|
|||
return conan_installs
|
||||
|
||||
def _python_installs(self):
|
||||
self.output.info("Collecting python installs")
|
||||
python_installs = {}
|
||||
|
||||
# list of python installs
|
||||
python_ins_cmd = f"python -c \"import pkg_resources; print(';'.join([(s.key+','+ s.version) for s in pkg_resources.working_set]))\""
|
||||
from six import StringIO
|
||||
run_env = VirtualRunEnv(self)
|
||||
env = run_env.environment()
|
||||
env.prepend_path("PYTHONPATH", str(self._site_packages.as_posix()))
|
||||
venv_vars = env.vars(self, scope = "run")
|
||||
|
||||
outer = '"' if self.settings.os == "Windows" else "'"
|
||||
inner = "'" if self.settings.os == "Windows" else '"'
|
||||
buffer = StringIO()
|
||||
self.run(python_ins_cmd, run_environment= True, env = "conanrun", output=buffer)
|
||||
with venv_vars.apply():
|
||||
self.run(f"""python -c {outer}import pkg_resources; print({inner};{inner}.join([(s.key+{inner},{inner}+ s.version) for s in pkg_resources.working_set])){outer}""",
|
||||
env = "conanrun",
|
||||
output = buffer)
|
||||
|
||||
packages = str(buffer.getvalue()).split("-----------------\n")
|
||||
packages = packages[1].strip('\r\n').split(";")
|
||||
|
@ -420,7 +431,6 @@ class CuraConan(ConanFile):
|
|||
)
|
||||
|
||||
if self.options.get_safe("enable_i18n", False) and self._i18n_options["extract"]:
|
||||
# Update the po and pot files
|
||||
vb = VirtualBuildEnv(self)
|
||||
vb.generate()
|
||||
|
||||
|
@ -505,10 +515,14 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
|
|||
|
||||
if self.in_local_cache:
|
||||
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "site-packages"))
|
||||
self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "site-packages"))
|
||||
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "plugins"))
|
||||
self.env_info.PYTHONPATH.append(os.path.join(self.package_folder, "plugins"))
|
||||
else:
|
||||
self.runenv_info.append_path("PYTHONPATH", self.source_folder)
|
||||
self.env_info.PYTHONPATH.append(self.source_folder)
|
||||
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.source_folder, "plugins"))
|
||||
self.env_info.PYTHONPATH.append(os.path.join(self.source_folder, "plugins"))
|
||||
|
||||
def package_id(self):
|
||||
self.info.clear()
|
||||
|
|
|
@ -104,7 +104,8 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
|||
from cura.Settings.SidebarCustomMenuItemsModel import SidebarCustomMenuItemsModel
|
||||
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
||||
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
|
||||
from cura.UI import CuraSplashScreen, MachineActionManager, PrintInformation
|
||||
from cura.UI import CuraSplashScreen, PrintInformation
|
||||
from cura.UI.MachineActionManager import MachineActionManager
|
||||
from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
|
||||
from cura.UI.MachineSettingsManager import MachineSettingsManager
|
||||
from cura.UI.ObjectsModel import ObjectsModel
|
||||
|
@ -125,6 +126,7 @@ from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel
|
|||
from .Machines.Models.MachineListModel import MachineListModel
|
||||
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
|
||||
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
|
||||
from .PrintOrderManager import PrintOrderManager
|
||||
from .SingleInstance import SingleInstance
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -179,6 +181,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
# Variables set from CLI
|
||||
self._files_to_open = []
|
||||
self._urls_to_open = []
|
||||
self._use_single_instance = False
|
||||
|
||||
self._single_instance = None
|
||||
|
@ -186,7 +189,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
|
||||
|
||||
self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager]
|
||||
self._machine_action_manager: Optional[MachineActionManager] = None
|
||||
|
||||
self.empty_container = None # type: EmptyInstanceContainer
|
||||
self.empty_definition_changes_container = None # type: EmptyInstanceContainer
|
||||
|
@ -202,6 +205,7 @@ class CuraApplication(QtApplication):
|
|||
self._container_manager = None
|
||||
|
||||
self._object_manager = None
|
||||
self._print_order_manager = None
|
||||
self._extruders_model = None
|
||||
self._extruders_model_with_optional = None
|
||||
self._build_plate_model = None
|
||||
|
@ -333,7 +337,7 @@ class CuraApplication(QtApplication):
|
|||
for filename in self._cli_args.file:
|
||||
url = QUrl(filename)
|
||||
if url.scheme() in self._supported_url_schemes:
|
||||
self._open_url_queue.append(url)
|
||||
self._urls_to_open.append(url)
|
||||
else:
|
||||
self._files_to_open.append(os.path.abspath(filename))
|
||||
|
||||
|
@ -352,11 +356,11 @@ class CuraApplication(QtApplication):
|
|||
self.__addAllEmptyContainers()
|
||||
self.__setLatestResouceVersionsForVersionUpgrade()
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
||||
self._machine_action_manager = MachineActionManager(self)
|
||||
self._machine_action_manager.initialize()
|
||||
|
||||
def __sendCommandToSingleInstance(self):
|
||||
self._single_instance = SingleInstance(self, self._files_to_open)
|
||||
self._single_instance = SingleInstance(self, self._files_to_open, self._urls_to_open)
|
||||
|
||||
# If we use single instance, try to connect to the single instance server, send commands, and then exit.
|
||||
# If we cannot find an existing single instance server, this is the only instance, so just keep going.
|
||||
|
@ -373,9 +377,15 @@ class CuraApplication(QtApplication):
|
|||
Resources.addExpectedDirNameInData(dir_name)
|
||||
|
||||
app_root = os.path.abspath(os.path.join(os.path.dirname(sys.executable)))
|
||||
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
|
||||
|
||||
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||
if platform.system() == "Darwin":
|
||||
Resources.addSecureSearchPath(os.path.join(app_root, "Resources", "share", "cura", "resources"))
|
||||
Resources.addSecureSearchPath(
|
||||
os.path.join(self._app_install_dir, "Resources", "share", "cura", "resources"))
|
||||
else:
|
||||
Resources.addSecureSearchPath(os.path.join(app_root, "share", "cura", "resources"))
|
||||
Resources.addSecureSearchPath(os.path.join(self._app_install_dir, "share", "cura", "resources"))
|
||||
|
||||
if not hasattr(sys, "frozen"):
|
||||
cura_data_root = os.environ.get('CURA_DATA_ROOT', None)
|
||||
if cura_data_root:
|
||||
|
@ -899,6 +909,7 @@ class CuraApplication(QtApplication):
|
|||
# initialize info objects
|
||||
self._print_information = PrintInformation.PrintInformation(self)
|
||||
self._cura_actions = CuraActions.CuraActions(self)
|
||||
self._print_order_manager = PrintOrderManager(self.getObjectsModel().getNodes)
|
||||
self.processEvents()
|
||||
# Initialize setting visibility presets model.
|
||||
self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self.getPreferences(), parent = self)
|
||||
|
@ -956,6 +967,8 @@ class CuraApplication(QtApplication):
|
|||
self.callLater(self._openFile, file_name)
|
||||
for file_name in self._open_file_queue: # Open all the files that were queued up while plug-ins were loading.
|
||||
self.callLater(self._openFile, file_name)
|
||||
for url in self._urls_to_open:
|
||||
self.callLater(self._openUrl, url)
|
||||
for url in self._open_url_queue:
|
||||
self.callLater(self._openUrl, url)
|
||||
|
||||
|
@ -979,6 +992,7 @@ class CuraApplication(QtApplication):
|
|||
t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis])
|
||||
|
||||
Selection.selectionChanged.connect(self.onSelectionChanged)
|
||||
self._print_order_manager.printOrderChanged.connect(self._onPrintOrderChanged)
|
||||
|
||||
# Set default background color for scene
|
||||
self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
|
||||
|
@ -1068,6 +1082,10 @@ class CuraApplication(QtApplication):
|
|||
def getTextManager(self, *args) -> "TextManager":
|
||||
return self._text_manager
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def getWorkplaceDropToBuildplate(self, drop_to_build_plate: bool) ->None:
|
||||
return self._physics.setAppPerModelDropDown(drop_to_build_plate)
|
||||
|
||||
def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions":
|
||||
if self._cura_formula_functions is None:
|
||||
self._cura_formula_functions = CuraFormulaFunctions(self)
|
||||
|
@ -1094,6 +1112,10 @@ class CuraApplication(QtApplication):
|
|||
self._object_manager = ObjectsModel(self)
|
||||
return self._object_manager
|
||||
|
||||
@pyqtSlot(str, result = "QVariantList")
|
||||
def getSupportedActionMachineList(self, definition_id: str) -> List["MachineAction"]:
|
||||
return self._machine_action_manager.getSupportedActions(self._machine_manager.getDefinitionByMachineId(definition_id))
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getExtrudersModel(self, *args) -> "ExtrudersModel":
|
||||
if self._extruders_model is None:
|
||||
|
@ -1129,18 +1151,16 @@ class CuraApplication(QtApplication):
|
|||
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
||||
return self._setting_inheritance_manager
|
||||
|
||||
def getMachineActionManager(self, *args: Any) -> MachineActionManager.MachineActionManager:
|
||||
@pyqtSlot(result = QObject)
|
||||
def getMachineActionManager(self, *args: Any) -> MachineActionManager:
|
||||
"""Get the machine action manager
|
||||
|
||||
We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
||||
It wants to give this function an engine and script engine, but we don't care about that.
|
||||
"""
|
||||
|
||||
return cast(MachineActionManager.MachineActionManager, self._machine_action_manager)
|
||||
return self._machine_action_manager
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getMachineActionManagerQml(self)-> MachineActionManager.MachineActionManager:
|
||||
return cast(QObject, self._machine_action_manager)
|
||||
|
||||
@pyqtSlot(result = QObject)
|
||||
def getMaterialManagementModel(self) -> MaterialManagementModel:
|
||||
|
@ -1250,6 +1270,7 @@ class CuraApplication(QtApplication):
|
|||
self.processEvents()
|
||||
engine.rootContext().setContextProperty("Printer", self)
|
||||
engine.rootContext().setContextProperty("CuraApplication", self)
|
||||
engine.rootContext().setContextProperty("PrintOrderManager", self._print_order_manager)
|
||||
engine.rootContext().setContextProperty("PrintInformation", self._print_information)
|
||||
engine.rootContext().setContextProperty("CuraActions", self._cura_actions)
|
||||
engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion)
|
||||
|
@ -1264,7 +1285,7 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterSingletonType(IntentManager, "Cura", 1, 6, self.getIntentManager, "IntentManager")
|
||||
qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, self.getSettingInheritanceManager, "SettingInheritanceManager")
|
||||
qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 0, self.getSimpleModeSettingsManagerWrapper, "SimpleModeSettingsManager")
|
||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
|
||||
qmlRegisterSingletonType(MachineActionManager, "Cura", 1, 0, self.getMachineActionManagerWrapper, "MachineActionManager")
|
||||
|
||||
self.processEvents()
|
||||
qmlRegisterType(NetworkingUtil, "Cura", 1, 5, "NetworkingUtil")
|
||||
|
@ -1745,8 +1766,12 @@ class CuraApplication(QtApplication):
|
|||
Selection.remove(node)
|
||||
Selection.add(group_node)
|
||||
|
||||
all_nodes = self.getObjectsModel().getNodes()
|
||||
PrintOrderManager.updatePrintOrdersAfterGroupOperation(all_nodes, group_node, selected_nodes)
|
||||
|
||||
@pyqtSlot()
|
||||
def ungroupSelected(self) -> None:
|
||||
all_nodes = self.getObjectsModel().getNodes()
|
||||
selected_objects = Selection.getAllSelectedObjects().copy()
|
||||
for node in selected_objects:
|
||||
if node.callDecoration("isGroup"):
|
||||
|
@ -1754,21 +1779,30 @@ class CuraApplication(QtApplication):
|
|||
|
||||
group_parent = node.getParent()
|
||||
children = node.getChildren().copy()
|
||||
for child in children:
|
||||
# Ungroup only 1 level deep
|
||||
if child.getParent() != node:
|
||||
continue
|
||||
|
||||
# Ungroup only 1 level deep
|
||||
children_to_ungroup = list(filter(lambda child: child.getParent() == node, children))
|
||||
for child in children_to_ungroup:
|
||||
# Set the parent of the children to the parent of the group-node
|
||||
op.addOperation(SetParentOperation(child, group_parent))
|
||||
|
||||
# Add all individual nodes to the selection
|
||||
Selection.add(child)
|
||||
|
||||
PrintOrderManager.updatePrintOrdersAfterUngroupOperation(all_nodes, node, children_to_ungroup)
|
||||
op.push()
|
||||
# Note: The group removes itself from the scene once all its children have left it,
|
||||
# see GroupDecorator._onChildrenChanged
|
||||
|
||||
def _onPrintOrderChanged(self) -> None:
|
||||
# update object list
|
||||
scene = self.getController().getScene()
|
||||
scene.sceneChanged.emit(scene.getRoot())
|
||||
|
||||
# reset if already was sliced
|
||||
Application.getInstance().getBackend().needsSlicing()
|
||||
Application.getInstance().getBackend().tickle()
|
||||
|
||||
def _createSplashScreen(self) -> Optional[CuraSplashScreen.CuraSplashScreen]:
|
||||
if self._is_headless:
|
||||
return None
|
||||
|
|
88
cura/HitChecker.py
Normal file
88
cura/HitChecker.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
from typing import List, Dict
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class HitChecker:
|
||||
"""Checks if nodes can be printed without causing any collisions and interference"""
|
||||
|
||||
def __init__(self, nodes: List[CuraSceneNode]) -> None:
|
||||
self._hit_map = self._buildHitMap(nodes)
|
||||
|
||||
def anyTwoNodesBlockEachOther(self, nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if any 2 nodes block each other"""
|
||||
for a in nodes:
|
||||
for b in nodes:
|
||||
if self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def canPrintBefore(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if node doesn't block other_nodes and can be printed before them"""
|
||||
no_hits = all(not self._hit_map[node][other_node] for other_node in other_nodes)
|
||||
return no_hits
|
||||
|
||||
def canPrintAfter(self, node: CuraSceneNode, other_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True if node doesn't hit other nodes and can be printed after them"""
|
||||
no_hits = all(not self._hit_map[other_node][node] for other_node in other_nodes)
|
||||
return no_hits
|
||||
|
||||
def calculateScore(self, a: CuraSceneNode, b: CuraSceneNode) -> int:
|
||||
"""Calculate score simply sums the number of other objects it 'blocks'
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: sum of the number of other objects
|
||||
"""
|
||||
|
||||
score_a = sum(self._hit_map[a].values())
|
||||
score_b = sum(self._hit_map[b].values())
|
||||
return score_a - score_b
|
||||
|
||||
def canPrintNodesInProvidedOrder(self, ordered_nodes: List[CuraSceneNode]) -> bool:
|
||||
"""Returns True If nodes don't have any hits in provided order"""
|
||||
for node_index, node in enumerate(ordered_nodes):
|
||||
nodes_before = ordered_nodes[:node_index - 1] if node_index - 1 >= 0 else []
|
||||
nodes_after = ordered_nodes[node_index + 1:] if node_index + 1 < len(ordered_nodes) else []
|
||||
if not self.canPrintBefore(node, nodes_after) or not self.canPrintAfter(node, nodes_before):
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _buildHitMap(nodes: List[CuraSceneNode]) -> Dict[CuraSceneNode, CuraSceneNode]:
|
||||
"""Pre-computes all hits between all objects
|
||||
|
||||
:nodes: nodes that need to be checked for collisions
|
||||
:return: dictionary where hit_map[node1][node2] is False if there node1 can be printed before node2
|
||||
"""
|
||||
hit_map = {j: {i: HitChecker._checkHit(j, i) for i in nodes} for j in nodes}
|
||||
return hit_map
|
||||
|
||||
@staticmethod
|
||||
def _checkHit(a: CuraSceneNode, b: CuraSceneNode) -> bool:
|
||||
"""Checks if a can be printed before b
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: False if a can be printed before b
|
||||
"""
|
||||
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
a_hit_hull = a.callDecoration("getConvexHullBoundary")
|
||||
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
|
||||
# Adhesion areas must never overlap, regardless of printing order
|
||||
# This would cause over-extrusion
|
||||
a_hit_hull = a.callDecoration("getAdhesionArea")
|
||||
b_hit_hull = b.callDecoration("getAdhesionArea")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
|
@ -16,6 +16,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To downlo
|
|||
|
||||
catalog = i18nCatalog("cura")
|
||||
TOKEN_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
REQUEST_TIMEOUT = 5 # Seconds
|
||||
|
||||
|
||||
class AuthorizationHelpers:
|
||||
|
@ -53,7 +54,8 @@ class AuthorizationHelpers:
|
|||
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
||||
headers_dict = headers,
|
||||
callback = lambda response: self.parseTokenResponse(response, callback),
|
||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback)
|
||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback),
|
||||
timeout = REQUEST_TIMEOUT
|
||||
)
|
||||
|
||||
def getAccessTokenUsingRefreshToken(self, refresh_token: str, callback: Callable[[AuthenticationResponse], None]) -> None:
|
||||
|
@ -77,7 +79,9 @@ class AuthorizationHelpers:
|
|||
data = urllib.parse.urlencode(data).encode("UTF-8"),
|
||||
headers_dict = headers,
|
||||
callback = lambda response: self.parseTokenResponse(response, callback),
|
||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback)
|
||||
error_callback = lambda response, _: self.parseTokenResponse(response, callback),
|
||||
urgent = True,
|
||||
timeout = REQUEST_TIMEOUT
|
||||
)
|
||||
|
||||
def parseTokenResponse(self, token_response: QNetworkReply, callback: Callable[[AuthenticationResponse], None]) -> None:
|
||||
|
@ -122,7 +126,8 @@ class AuthorizationHelpers:
|
|||
check_token_url,
|
||||
headers_dict = headers,
|
||||
callback = lambda reply: self._parseUserProfile(reply, success_callback, failed_callback),
|
||||
error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None
|
||||
error_callback = lambda _, _2: failed_callback() if failed_callback is not None else None,
|
||||
timeout = REQUEST_TIMEOUT
|
||||
)
|
||||
|
||||
def _parseUserProfile(self, reply: QNetworkReply, success_callback: Optional[Callable[[UserProfile], None]], failed_callback: Optional[Callable[[], None]] = None) -> None:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Copyright (c) 2024 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
|
@ -6,13 +6,14 @@ from datetime import datetime, timedelta
|
|||
from typing import Callable, Dict, Optional, TYPE_CHECKING, Union
|
||||
from urllib.parse import urlencode, quote_plus
|
||||
|
||||
from PyQt6.QtCore import QUrl
|
||||
from PyQt6.QtCore import QUrl, QTimer
|
||||
from PyQt6.QtGui import QDesktopServices
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Signal import Signal
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To download log-in tokens.
|
||||
from cura.OAuth2.AuthorizationHelpers import AuthorizationHelpers, TOKEN_TIMESTAMP_FORMAT
|
||||
from cura.OAuth2.LocalAuthorizationServer import LocalAuthorizationServer
|
||||
from cura.OAuth2.Models import AuthenticationResponse, BaseModel
|
||||
|
@ -25,6 +26,8 @@ if TYPE_CHECKING:
|
|||
|
||||
MYCLOUD_LOGOFF_URL = "https://account.ultimaker.com/logoff?utm_source=cura&utm_medium=software&utm_campaign=change-account-before-adding-printers"
|
||||
|
||||
REFRESH_TOKEN_MAX_RETRIES = 15
|
||||
REFRESH_TOKEN_RETRY_INTERVAL = 1000
|
||||
|
||||
class AuthorizationService:
|
||||
"""The authorization service is responsible for handling the login flow, storing user credentials and providing
|
||||
|
@ -57,6 +60,12 @@ class AuthorizationService:
|
|||
|
||||
self.onAuthStateChanged.connect(self._authChanged)
|
||||
|
||||
self._refresh_token_retries = 0
|
||||
self._refresh_token_retry_timer = QTimer()
|
||||
self._refresh_token_retry_timer.setInterval(REFRESH_TOKEN_RETRY_INTERVAL)
|
||||
self._refresh_token_retry_timer.setSingleShot(True)
|
||||
self._refresh_token_retry_timer.timeout.connect(self.refreshAccessToken)
|
||||
|
||||
def _authChanged(self, logged_in):
|
||||
if logged_in and self._unable_to_get_data_message is not None:
|
||||
self._unable_to_get_data_message.hide()
|
||||
|
@ -167,16 +176,29 @@ class AuthorizationService:
|
|||
return
|
||||
|
||||
def process_auth_data(response: AuthenticationResponse) -> None:
|
||||
self._currently_refreshing_token = False
|
||||
|
||||
if response.success:
|
||||
self._refresh_token_retries = 0
|
||||
self._storeAuthData(response)
|
||||
HttpRequestManager.getInstance().setDelayRequests(False)
|
||||
self.onAuthStateChanged.emit(logged_in = True)
|
||||
else:
|
||||
Logger.warning("Failed to get a new access token from the server.")
|
||||
self.onAuthStateChanged.emit(logged_in = False)
|
||||
if self._refresh_token_retries >= REFRESH_TOKEN_MAX_RETRIES:
|
||||
self._refresh_token_retries = 0
|
||||
Logger.warning("Failed to get a new access token from the server, giving up.")
|
||||
HttpRequestManager.getInstance().setDelayRequests(False)
|
||||
self.onAuthStateChanged.emit(logged_in = False)
|
||||
else:
|
||||
# Retry a bit later, network may be offline right now and will hopefully be back soon
|
||||
Logger.warning("Failed to get a new access token from the server, retrying later.")
|
||||
self._refresh_token_retries += 1
|
||||
self._refresh_token_retry_timer.start()
|
||||
|
||||
if self._currently_refreshing_token:
|
||||
Logger.debug("Was already busy refreshing token. Do not start a new request.")
|
||||
return
|
||||
HttpRequestManager.getInstance().setDelayRequests(True)
|
||||
self._currently_refreshing_token = True
|
||||
self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token, process_auth_data)
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ from UM.Scene.Iterator import Iterator
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from functools import cmp_to_key
|
||||
|
||||
from cura.HitChecker import HitChecker
|
||||
from cura.PrintOrderManager import PrintOrderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class OneAtATimeIterator(Iterator.Iterator):
|
||||
"""Iterator that returns a list of nodes in the order that they need to be printed
|
||||
|
||||
|
@ -16,8 +21,6 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
|
||||
def __init__(self, scene_node) -> None:
|
||||
super().__init__(scene_node) # Call super to make multiple inheritance work.
|
||||
self._hit_map = [[]] # type: List[List[bool]] # For each node, which other nodes this hits. A grid of booleans on which nodes hit which.
|
||||
self._original_node_list = [] # type: List[SceneNode] # The nodes that need to be checked for collisions.
|
||||
|
||||
def _fillStack(self) -> None:
|
||||
"""Fills the ``_node_stack`` with a list of scene nodes that need to be printed in order. """
|
||||
|
@ -38,104 +41,50 @@ class OneAtATimeIterator(Iterator.Iterator):
|
|||
self._node_stack = node_list[:]
|
||||
return
|
||||
|
||||
# Copy the list
|
||||
self._original_node_list = node_list[:]
|
||||
hit_checker = HitChecker(node_list)
|
||||
|
||||
# Initialise the hit map (pre-compute all hits between all objects)
|
||||
self._hit_map = [[self._checkHit(i, j) for i in node_list] for j in node_list]
|
||||
if PrintOrderManager.isUserDefinedPrintOrderEnabled():
|
||||
self._node_stack = self._getNodesOrderedByUser(hit_checker, node_list)
|
||||
else:
|
||||
self._node_stack = self._getNodesOrderedAutomatically(hit_checker, node_list)
|
||||
|
||||
# Check if we have to files that block each other. If this is the case, there is no solution!
|
||||
for a in range(0, len(node_list)):
|
||||
for b in range(0, len(node_list)):
|
||||
if a != b and self._hit_map[a][b] and self._hit_map[b][a]:
|
||||
return
|
||||
# update print orders so that user can try to arrange the nodes automatically first
|
||||
# and if result is not satisfactory he/she can switch to manual mode and change it
|
||||
for index, node in enumerate(self._node_stack):
|
||||
node.printOrder = index + 1
|
||||
|
||||
@staticmethod
|
||||
def _getNodesOrderedByUser(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
|
||||
nodes_ordered_by_user = sorted(node_list, key=lambda n: n.printOrder)
|
||||
if hit_checker.canPrintNodesInProvidedOrder(nodes_ordered_by_user):
|
||||
return nodes_ordered_by_user
|
||||
return [] # No solution
|
||||
|
||||
@staticmethod
|
||||
def _getNodesOrderedAutomatically(hit_checker: HitChecker, node_list: List[CuraSceneNode]) -> List[CuraSceneNode]:
|
||||
# Check if we have two files that block each other. If this is the case, there is no solution!
|
||||
if hit_checker.anyTwoNodesBlockEachOther(node_list):
|
||||
return [] # No solution
|
||||
|
||||
# Sort the original list so that items that block the most other objects are at the beginning.
|
||||
# This does not decrease the worst case running time, but should improve it in most cases.
|
||||
sorted(node_list, key = cmp_to_key(self._calculateScore))
|
||||
node_list = sorted(node_list, key = cmp_to_key(hit_checker.calculateScore))
|
||||
|
||||
todo_node_list = [_ObjectOrder([], node_list)]
|
||||
while len(todo_node_list) > 0:
|
||||
current = todo_node_list.pop()
|
||||
for node in current.todo:
|
||||
# Check if the object can be placed with what we have and still allows for a solution in the future
|
||||
if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo):
|
||||
if hit_checker.canPrintAfter(node, current.order) and hit_checker.canPrintBefore(node, current.todo):
|
||||
# We found a possible result. Create new todo & order list.
|
||||
new_todo_list = current.todo[:]
|
||||
new_todo_list.remove(node)
|
||||
new_order = current.order[:] + [node]
|
||||
if len(new_todo_list) == 0:
|
||||
# We have no more nodes to check, so quit looking.
|
||||
self._node_stack = new_order
|
||||
return
|
||||
return new_order # Solution found!
|
||||
todo_node_list.append(_ObjectOrder(new_order, new_todo_list))
|
||||
self._node_stack = [] #No result found!
|
||||
|
||||
|
||||
# Check if first object can be printed before the provided list (using the hit map)
|
||||
def _checkHitMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[node_index][other_node_index]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _checkBlockMultiple(self, node: SceneNode, other_nodes: List[SceneNode]) -> bool:
|
||||
"""Check for a node whether it hits any of the other nodes.
|
||||
|
||||
:param node: The node to check whether it collides with the other nodes.
|
||||
:param other_nodes: The nodes to check for collisions.
|
||||
:return: returns collision between nodes
|
||||
"""
|
||||
|
||||
node_index = self._original_node_list.index(node)
|
||||
for other_node in other_nodes:
|
||||
other_node_index = self._original_node_list.index(other_node)
|
||||
if self._hit_map[other_node_index][node_index] and node_index != other_node_index:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _calculateScore(self, a: SceneNode, b: SceneNode) -> int:
|
||||
"""Calculate score simply sums the number of other objects it 'blocks'
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: sum of the number of other objects
|
||||
"""
|
||||
|
||||
score_a = sum(self._hit_map[self._original_node_list.index(a)])
|
||||
score_b = sum(self._hit_map[self._original_node_list.index(b)])
|
||||
return score_a - score_b
|
||||
|
||||
def _checkHit(self, a: SceneNode, b: SceneNode) -> bool:
|
||||
"""Checks if a can be printed before b
|
||||
|
||||
:param a: node
|
||||
:param b: node
|
||||
:return: true if a can be printed before b
|
||||
"""
|
||||
|
||||
if a == b:
|
||||
return False
|
||||
|
||||
a_hit_hull = a.callDecoration("getConvexHullBoundary")
|
||||
b_hit_hull = b.callDecoration("getConvexHullHeadFull")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
|
||||
# Adhesion areas must never overlap, regardless of printing order
|
||||
# This would cause over-extrusion
|
||||
a_hit_hull = a.callDecoration("getAdhesionArea")
|
||||
b_hit_hull = b.callDecoration("getAdhesionArea")
|
||||
overlap = a_hit_hull.intersectsPolygon(b_hit_hull)
|
||||
|
||||
if overlap:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return [] # No result found!
|
||||
|
||||
|
||||
class _ObjectOrder:
|
||||
|
|
|
@ -38,7 +38,14 @@ class PlatformPhysics:
|
|||
self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models
|
||||
|
||||
Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False)
|
||||
Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True)
|
||||
Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", False)
|
||||
self._app_per_model_drop = Application.getInstance().getPreferences().getValue("physics/automatic_drop_down")
|
||||
|
||||
def getAppPerModelDropDown(self):
|
||||
return self._app_per_model_drop
|
||||
|
||||
def setAppPerModelDropDown(self, drop_to_buildplate):
|
||||
self._app_per_model_drop = drop_to_buildplate
|
||||
|
||||
def _onSceneChanged(self, source):
|
||||
if not source.callDecoration("isSliceable"):
|
||||
|
@ -71,6 +78,7 @@ class PlatformPhysics:
|
|||
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
|
||||
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
|
||||
random.shuffle(nodes)
|
||||
|
||||
for node in nodes:
|
||||
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
|
||||
continue
|
||||
|
@ -80,7 +88,10 @@ class PlatformPhysics:
|
|||
# Move it downwards if bottom is above platform
|
||||
move_vector = Vector()
|
||||
|
||||
if node.getSetting(SceneNodeSettings.AutoDropDown, app_automatic_drop_down) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||
# if per model drop is different then app_automatic_drop, in case of 3mf loading when user changes this setting for that model
|
||||
if (self._app_per_model_drop != app_automatic_drop_down):
|
||||
node.setSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop)
|
||||
if node.getSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down
|
||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||
move_vector = move_vector.set(y = -bbox.bottom + z_offset)
|
||||
|
||||
|
@ -168,6 +179,8 @@ class PlatformPhysics:
|
|||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||
op.push()
|
||||
|
||||
# setting this drop to model same as app_automatic_drop_down
|
||||
self._app_per_model_drop = app_automatic_drop_down
|
||||
# After moving, we have to evaluate the boundary checks for nodes
|
||||
build_volume.updateNodeBoundaryCheck()
|
||||
|
||||
|
|
171
cura/PrintOrderManager.py
Normal file
171
cura/PrintOrderManager.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
from typing import List, Callable, Optional, Any
|
||||
|
||||
from PyQt6.QtCore import pyqtProperty, pyqtSignal, QObject, pyqtSlot
|
||||
from UM.Application import Application
|
||||
from UM.Scene.Selection import Selection
|
||||
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
|
||||
class PrintOrderManager(QObject):
|
||||
"""Allows to order the object list to set the print sequence manually"""
|
||||
|
||||
def __init__(self, get_nodes: Callable[[], List[CuraSceneNode]]) -> None:
|
||||
super().__init__()
|
||||
self._get_nodes = get_nodes
|
||||
self._configureEvents()
|
||||
|
||||
_settingsChanged = pyqtSignal()
|
||||
_uiActionsOutdated = pyqtSignal()
|
||||
printOrderChanged = pyqtSignal()
|
||||
|
||||
@pyqtSlot()
|
||||
def swapSelectedAndPreviousNodes(self) -> None:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
self._swapPrintOrders(selected_node, previous_node)
|
||||
|
||||
@pyqtSlot()
|
||||
def swapSelectedAndNextNodes(self) -> None:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
self._swapPrintOrders(selected_node, next_node)
|
||||
|
||||
@pyqtProperty(str, notify=_uiActionsOutdated)
|
||||
def previousNodeName(self) -> str:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
return self._getNodeName(previous_node)
|
||||
|
||||
@pyqtProperty(str, notify=_uiActionsOutdated)
|
||||
def nextNodeName(self) -> str:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
return self._getNodeName(next_node)
|
||||
|
||||
@pyqtProperty(bool, notify=_uiActionsOutdated)
|
||||
def shouldEnablePrintBeforeAction(self) -> bool:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
can_swap_with_previous_node = selected_node is not None and previous_node is not None
|
||||
return can_swap_with_previous_node
|
||||
|
||||
@pyqtProperty(bool, notify=_uiActionsOutdated)
|
||||
def shouldEnablePrintAfterAction(self) -> bool:
|
||||
selected_node, previous_node, next_node = self._getSelectedAndNeighborNodes()
|
||||
can_swap_with_next_node = selected_node is not None and next_node is not None
|
||||
return can_swap_with_next_node
|
||||
|
||||
@pyqtProperty(bool, notify=_settingsChanged)
|
||||
def shouldShowEditPrintOrderActions(self) -> bool:
|
||||
return PrintOrderManager.isUserDefinedPrintOrderEnabled()
|
||||
|
||||
@staticmethod
|
||||
def isUserDefinedPrintOrderEnabled() -> bool:
|
||||
stack = Application.getInstance().getGlobalContainerStack()
|
||||
is_enabled = stack and \
|
||||
stack.getProperty("print_sequence", "value") == "one_at_a_time" and \
|
||||
stack.getProperty("user_defined_print_order_enabled", "value")
|
||||
return bool(is_enabled)
|
||||
|
||||
@staticmethod
|
||||
def initializePrintOrders(nodes: List[CuraSceneNode]) -> None:
|
||||
"""Just created (loaded from file) nodes have print order 0.
|
||||
|
||||
This method initializes print orders with max value to put nodes at the end of object list"""
|
||||
max_print_order = max(map(lambda n: n.printOrder, nodes), default=0)
|
||||
for node in nodes:
|
||||
if node.printOrder == 0:
|
||||
max_print_order += 1
|
||||
node.printOrder = max_print_order
|
||||
|
||||
@staticmethod
|
||||
def updatePrintOrdersAfterGroupOperation(
|
||||
all_nodes: List[CuraSceneNode],
|
||||
group_node: CuraSceneNode,
|
||||
grouped_nodes: List[CuraSceneNode]
|
||||
) -> None:
|
||||
group_node.printOrder = min(map(lambda n: n.printOrder, grouped_nodes))
|
||||
|
||||
all_nodes.append(group_node)
|
||||
for node in grouped_nodes:
|
||||
all_nodes.remove(node)
|
||||
|
||||
# reassign print orders so there won't be gaps like 1 2 5 6 7
|
||||
sorted_nodes = sorted(all_nodes, key=lambda n: n.printOrder)
|
||||
for i, node in enumerate(sorted_nodes):
|
||||
node.printOrder = i + 1
|
||||
|
||||
@staticmethod
|
||||
def updatePrintOrdersAfterUngroupOperation(
|
||||
all_nodes: List[CuraSceneNode],
|
||||
group_node: CuraSceneNode,
|
||||
ungrouped_nodes: List[CuraSceneNode]
|
||||
) -> None:
|
||||
all_nodes.remove(group_node)
|
||||
nodes_to_update_print_order = filter(lambda n: n.printOrder > group_node.printOrder, all_nodes)
|
||||
for node in nodes_to_update_print_order:
|
||||
node.printOrder += len(ungrouped_nodes) - 1
|
||||
|
||||
for i, child in enumerate(ungrouped_nodes):
|
||||
child.printOrder = group_node.printOrder + i
|
||||
all_nodes.append(child)
|
||||
|
||||
def _swapPrintOrders(self, node1: CuraSceneNode, node2: CuraSceneNode) -> None:
|
||||
if node1 and node2:
|
||||
node1.printOrder, node2.printOrder = node2.printOrder, node1.printOrder # swap print orders
|
||||
self.printOrderChanged.emit() # update object list first
|
||||
self._uiActionsOutdated.emit() # then update UI actions
|
||||
|
||||
def _getSelectedAndNeighborNodes(self
|
||||
) -> (Optional[CuraSceneNode], Optional[CuraSceneNode], Optional[CuraSceneNode]):
|
||||
nodes = self._get_nodes()
|
||||
ordered_nodes = sorted(nodes, key=lambda n: n.printOrder)
|
||||
selected_node = PrintOrderManager._getSingleSelectedNode()
|
||||
if selected_node and selected_node in ordered_nodes:
|
||||
selected_node_index = ordered_nodes.index(selected_node)
|
||||
else:
|
||||
selected_node_index = None
|
||||
|
||||
if selected_node_index is not None and selected_node_index - 1 >= 0:
|
||||
previous_node = ordered_nodes[selected_node_index - 1]
|
||||
else:
|
||||
previous_node = None
|
||||
|
||||
if selected_node_index is not None and selected_node_index + 1 < len(ordered_nodes):
|
||||
next_node = ordered_nodes[selected_node_index + 1]
|
||||
else:
|
||||
next_node = None
|
||||
|
||||
return selected_node, previous_node, next_node
|
||||
|
||||
@staticmethod
|
||||
def _getNodeName(node: CuraSceneNode, max_length: int = 30) -> str:
|
||||
node_name = node.getName() if node else ""
|
||||
truncated_node_name = node_name[:max_length]
|
||||
return truncated_node_name
|
||||
|
||||
@staticmethod
|
||||
def _getSingleSelectedNode() -> Optional[CuraSceneNode]:
|
||||
if len(Selection.getAllSelectedObjects()) == 1:
|
||||
selected_node = Selection.getSelectedObject(0)
|
||||
return selected_node
|
||||
return None
|
||||
|
||||
def _configureEvents(self) -> None:
|
||||
Selection.selectionChanged.connect(self._onSelectionChanged)
|
||||
self._global_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
self._onGlobalStackChanged()
|
||||
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.disconnect(self._onSettingsChanged)
|
||||
self._global_stack.containersChanged.disconnect(self._onSettingsChanged)
|
||||
|
||||
self._global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
if self._global_stack:
|
||||
self._global_stack.propertyChanged.connect(self._onSettingsChanged)
|
||||
self._global_stack.containersChanged.connect(self._onSettingsChanged)
|
||||
|
||||
def _onSettingsChanged(self, *args: Any) -> None:
|
||||
self._settingsChanged.emit()
|
||||
|
||||
def _onSelectionChanged(self) -> None:
|
||||
self._uiActionsOutdated.emit()
|
|
@ -25,10 +25,19 @@ class CuraSceneNode(SceneNode):
|
|||
if not no_setting_override:
|
||||
self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled
|
||||
self._outside_buildarea = False
|
||||
self._print_order = 0
|
||||
|
||||
def setOutsideBuildArea(self, new_value: bool) -> None:
|
||||
self._outside_buildarea = new_value
|
||||
|
||||
@property
|
||||
def printOrder(self):
|
||||
return self._print_order
|
||||
|
||||
@printOrder.setter
|
||||
def printOrder(self, new_value):
|
||||
self._print_order = new_value
|
||||
|
||||
def isOutsideBuildArea(self) -> bool:
|
||||
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
|
||||
|
||||
|
@ -157,3 +166,6 @@ class CuraSceneNode(SceneNode):
|
|||
|
||||
def transformChanged(self) -> None:
|
||||
self._transformChanged()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "{print_order}. {name}".format(print_order = self._print_order, name = self.getName())
|
||||
|
|
|
@ -316,7 +316,13 @@ class ExtruderManager(QObject):
|
|||
# Starts with the adhesion extruder.
|
||||
adhesion_type = global_stack.getProperty("adhesion_type", "value")
|
||||
if adhesion_type in {"skirt", "brim"}:
|
||||
return max(0, int(global_stack.getProperty("skirt_brim_extruder_nr", "value"))) # optional skirt/brim extruder defaults to zero
|
||||
skirt_brim_extruder_nr = global_stack.getProperty("skirt_brim_extruder_nr", "value")
|
||||
# if the skirt_brim_extruder_nr is -1, then we use the first used extruder
|
||||
if skirt_brim_extruder_nr == -1:
|
||||
used_extruders = self.getUsedExtruderStacks()
|
||||
return used_extruders[0].position
|
||||
else:
|
||||
return skirt_brim_extruder_nr
|
||||
if adhesion_type == "raft":
|
||||
return global_stack.getProperty("raft_base_extruder_nr", "value")
|
||||
|
||||
|
|
|
@ -5,16 +5,18 @@ import json
|
|||
import os
|
||||
from typing import List, Optional
|
||||
|
||||
from PyQt6.QtCore import QUrl
|
||||
from PyQt6.QtNetwork import QLocalServer, QLocalSocket
|
||||
|
||||
from UM.Qt.QtApplication import QtApplication #For typing.
|
||||
from UM.Qt.QtApplication import QtApplication # For typing.
|
||||
from UM.Logger import Logger
|
||||
|
||||
|
||||
class SingleInstance:
|
||||
def __init__(self, application: QtApplication, files_to_open: Optional[List[str]]) -> None:
|
||||
def __init__(self, application: QtApplication, files_to_open: Optional[List[str]], url_to_open: Optional[List[str]]) -> None:
|
||||
self._application = application
|
||||
self._files_to_open = files_to_open
|
||||
self._url_to_open = url_to_open
|
||||
|
||||
self._single_instance_server = None
|
||||
|
||||
|
@ -33,7 +35,7 @@ class SingleInstance:
|
|||
return False
|
||||
|
||||
# We only send the files that need to be opened.
|
||||
if not self._files_to_open:
|
||||
if not self._files_to_open and not self._url_to_open:
|
||||
Logger.log("i", "No file need to be opened, do nothing.")
|
||||
return True
|
||||
|
||||
|
@ -55,8 +57,12 @@ class SingleInstance:
|
|||
payload = {"command": "open", "filePath": os.path.abspath(filename)}
|
||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||
|
||||
for url in self._url_to_open:
|
||||
payload = {"command": "open-url", "urlPath": url.toString()}
|
||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii"))
|
||||
|
||||
payload = {"command": "close-connection"}
|
||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding = "ascii"))
|
||||
single_instance_socket.write(bytes(json.dumps(payload) + "\n", encoding="ascii"))
|
||||
|
||||
single_instance_socket.flush()
|
||||
single_instance_socket.waitForDisconnected()
|
||||
|
@ -72,7 +78,7 @@ class SingleInstance:
|
|||
|
||||
def _onClientConnected(self) -> None:
|
||||
Logger.log("i", "New connection received on our single-instance server")
|
||||
connection = None #type: Optional[QLocalSocket]
|
||||
connection = None # type: Optional[QLocalSocket]
|
||||
if self._single_instance_server:
|
||||
connection = self._single_instance_server.nextPendingConnection()
|
||||
|
||||
|
@ -81,7 +87,7 @@ class SingleInstance:
|
|||
|
||||
def __readCommands(self, connection: QLocalSocket) -> None:
|
||||
line = connection.readLine()
|
||||
while len(line) != 0: # There is also a .canReadLine()
|
||||
while len(line) != 0: # There is also a .canReadLine()
|
||||
try:
|
||||
payload = json.loads(str(line, encoding = "ascii").strip())
|
||||
command = payload["command"]
|
||||
|
@ -94,13 +100,19 @@ class SingleInstance:
|
|||
elif command == "open":
|
||||
self._application.callLater(lambda f = payload["filePath"]: self._application._openFile(f))
|
||||
|
||||
#command: Load a url link in Cura
|
||||
elif command == "open-url":
|
||||
url = QUrl(payload["urlPath"])
|
||||
self._application.callLater(lambda: self._application._openUrl(url))
|
||||
|
||||
|
||||
# Command: Activate the window and bring it to the top.
|
||||
elif command == "focus":
|
||||
# Operating systems these days prevent windows from moving around by themselves.
|
||||
# 'alert' or flashing the icon in the taskbar is the best thing we do now.
|
||||
main_window = self._application.getMainWindow()
|
||||
if main_window is not None:
|
||||
self._application.callLater(lambda: main_window.alert(0)) # type: ignore # I don't know why MyPy complains here
|
||||
self._application.callLater(lambda: main_window.alert(0)) # type: ignore # I don't know why MyPy complains here
|
||||
|
||||
# Command: Close the socket connection. We're done.
|
||||
elif command == "close-connection":
|
||||
|
|
|
@ -14,6 +14,9 @@ from UM.Scene.SceneNode import SceneNode
|
|||
from UM.Scene.Selection import Selection
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
from cura.PrintOrderManager import PrintOrderManager
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
||||
|
@ -76,6 +79,9 @@ class ObjectsModel(ListModel):
|
|||
self._build_plate_number = nr
|
||||
self._update()
|
||||
|
||||
def getNodes(self) -> List[CuraSceneNode]:
|
||||
return list(map(lambda n: n["node"], self.items))
|
||||
|
||||
def _updateSceneDelayed(self, source) -> None:
|
||||
if not isinstance(source, Camera):
|
||||
self._update_timer.start()
|
||||
|
@ -175,6 +181,10 @@ class ObjectsModel(ListModel):
|
|||
|
||||
all_nodes = self._renameNodes(name_to_node_info_dict)
|
||||
|
||||
user_defined_print_order_enabled = PrintOrderManager.isUserDefinedPrintOrderEnabled()
|
||||
if user_defined_print_order_enabled:
|
||||
PrintOrderManager.initializePrintOrders(all_nodes)
|
||||
|
||||
for node in all_nodes:
|
||||
if hasattr(node, "isOutsideBuildArea"):
|
||||
is_outside_build_area = node.isOutsideBuildArea() # type: ignore
|
||||
|
@ -223,8 +233,13 @@ class ObjectsModel(ListModel):
|
|||
# for anti overhang meshes and groups the extruder nr is irrelevant
|
||||
extruder_number = -1
|
||||
|
||||
if not user_defined_print_order_enabled:
|
||||
name = node.getName()
|
||||
else:
|
||||
name = "{print_order}. {name}".format(print_order = node.printOrder, name = node.getName())
|
||||
|
||||
nodes.append({
|
||||
"name": node.getName(),
|
||||
"name": name,
|
||||
"selected": Selection.isSelected(node),
|
||||
"outside_build_area": is_outside_build_area,
|
||||
"buildplate_number": node_build_plate_number,
|
||||
|
@ -234,5 +249,5 @@ class ObjectsModel(ListModel):
|
|||
"node": node
|
||||
})
|
||||
|
||||
nodes = sorted(nodes, key=lambda n: n["name"])
|
||||
nodes = sorted(nodes, key=lambda n: n["name"] if not user_defined_print_order_enabled else n["node"].printOrder)
|
||||
self.setItems(nodes)
|
||||
|
|
|
@ -177,6 +177,9 @@ class ThreeMFReader(MeshReader):
|
|||
else:
|
||||
Logger.log("w", "Unable to find extruder in position %s", setting_value)
|
||||
continue
|
||||
if key == "print_order":
|
||||
um_node.printOrder = int(setting_value)
|
||||
continue
|
||||
if key in known_setting_keys:
|
||||
setting_container.setProperty(key, "value", setting_value)
|
||||
else:
|
||||
|
|
|
@ -299,6 +299,11 @@ class WorkspaceDialog(QObject):
|
|||
|
||||
Application.getInstance().getBackend().close()
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def setDropToBuildPlateForModel(self, drop_to_buildplate: bool) -> None:
|
||||
CuraApplication.getInstance().getWorkplaceDropToBuildplate(drop_to_buildplate)
|
||||
|
||||
|
||||
def setMaterialConflict(self, material_conflict: bool) -> None:
|
||||
if self._has_material_conflict != material_conflict:
|
||||
self._has_material_conflict = material_conflict
|
||||
|
|
|
@ -300,6 +300,25 @@ UM.Dialog
|
|||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: dropToBuildPlate
|
||||
width: parent.width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
UM.CheckBox
|
||||
{
|
||||
id: checkDropModels
|
||||
text: catalog.i18nc("@text:window", "Drop models to buildplate")
|
||||
checked: UM.Preferences.getValue("physics/automatic_drop_down")
|
||||
onCheckedChanged: manager.setDropToBuildPlateForModel(checked)
|
||||
}
|
||||
function reloadValue()
|
||||
{
|
||||
checkDropModels.checked = UM.Preferences.getValue("physics/automatic_drop_down")
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: clearBuildPlateWarning
|
||||
|
@ -422,6 +441,7 @@ UM.Dialog
|
|||
materialSection.reloadValues()
|
||||
profileSection.reloadValues()
|
||||
printerSection.reloadValues()
|
||||
dropToBuildPlate.reloadValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,16 +10,14 @@ from UM.Math.Vector import Vector
|
|||
from UM.Logger import Logger
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Application import Application
|
||||
from UM.Message import Message
|
||||
from UM.Resources import Resources
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.EmptyInstanceContainer import EmptyInstanceContainer
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from cura.Settings import CuraContainerStack
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Snapshot import Snapshot
|
||||
|
||||
from PyQt6.QtCore import QBuffer
|
||||
|
@ -137,6 +135,9 @@ class ThreeMFWriter(MeshWriter):
|
|||
for key in changed_setting_keys:
|
||||
savitar_node.setSetting("cura:" + key, str(stack.getProperty(key, "value")))
|
||||
|
||||
if isinstance(um_node, CuraSceneNode):
|
||||
savitar_node.setSetting("cura:print_order", str(um_node.printOrder))
|
||||
|
||||
# Store the metadata.
|
||||
for key, value in um_node.metadata.items():
|
||||
savitar_node.setSetting(key, value)
|
||||
|
|
|
@ -76,6 +76,7 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._default_engine_location = executable_name
|
||||
|
||||
search_path = [
|
||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..", "Resources")),
|
||||
os.path.abspath(os.path.dirname(sys.executable)),
|
||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "bin")),
|
||||
os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||
from .src import DigitalFactoryFileProvider, DigitalFactoryOutputDevicePlugin, DigitalFactoryController
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
import json
|
||||
from json import JSONDecodeError
|
||||
import re
|
||||
from time import time
|
||||
from typing import List, Any, Optional, Union, Type, Tuple, Dict, cast, TypeVar, Callable
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ from typing import List, Optional
|
|||
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Qt.ListModel import ListModel
|
||||
from .DigitalFactoryProjectResponse import DigitalFactoryProjectResponse
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Platform import Platform
|
||||
|
||||
from . import GCodeGzWriter
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ from UM.Settings.InstanceContainer import InstanceContainer
|
|||
from cura.Machines.ContainerTree import ContainerTree
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
|
||||
from typing import Optional, TYPE_CHECKING, Dict, List
|
||||
|
||||
from .Constants import PACKAGES_URL
|
||||
from .PackageModel import PackageModel
|
||||
from .RemotePackageList import RemotePackageList
|
||||
from PyQt6.QtCore import pyqtSignal, QObject, pyqtProperty, QCoreApplication
|
||||
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To request the package list from the API.
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import re
|
||||
from enum import Enum
|
||||
from typing import Any, cast, Dict, List, Optional
|
||||
|
||||
from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal, pyqtSlot
|
||||
|
@ -12,7 +11,6 @@ from cura.CuraApplication import CuraApplication
|
|||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry # To get names of materials we're compatible with.
|
||||
from UM.i18n import i18nCatalog # To translate placeholder names if data is not present.
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
|
|
@ -11,7 +11,7 @@ from UM.Settings.SettingInstance import SettingInstance
|
|||
from UM.Logger import Logger
|
||||
import UM.Settings.Models.SettingVisibilityHandler
|
||||
|
||||
from cura.Settings.ExtruderManager import ExtruderManager #To get global-inherits-stack setting values from different extruders.
|
||||
from cura.Settings.ExtruderManager import ExtruderManager # To get global-inherits-stack setting values from different extruders.
|
||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
# M163 - Set Mix Factor
|
||||
# M164 - Save Mix - saves to T2 as a unique mix
|
||||
|
||||
import re #To perform the search and replace.
|
||||
import re # To perform the search and replace.
|
||||
from ..Script import Script
|
||||
|
||||
class ColorMix(Script):
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
# Description: This plugin is now an option in 'Display Info on LCD'
|
||||
|
||||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
from UM.Message import Message
|
||||
|
||||
class DisplayFilenameAndLayerOnLCD(Script):
|
||||
|
|
|
@ -30,9 +30,6 @@
|
|||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
import UM.Util
|
||||
import configparser
|
||||
from UM.Preferences import Preferences
|
||||
import time
|
||||
import datetime
|
||||
import math
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
from ..Script import Script
|
||||
|
||||
import re
|
||||
import datetime
|
||||
from UM.Message import Message
|
||||
|
||||
class DisplayProgressOnLCD(Script):
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
from typing import List
|
||||
from ..Script import Script
|
||||
|
||||
from UM.Application import Application #To get the current printer's settings.
|
||||
from UM.Application import Application # To get the current printer's settings.
|
||||
|
||||
class FilamentChange(Script):
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
from ..Script import Script
|
||||
import re
|
||||
from UM.Application import Application #To get the current printer's settings.
|
||||
from UM.Application import Application # To get the current printer's settings.
|
||||
from UM.Logger import Logger
|
||||
|
||||
from typing import List, Tuple
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (c) 2017 Ghostkeeper
|
||||
# The PostProcessingPlugin is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import re #To perform the search and replace.
|
||||
import re # To perform the search and replace.
|
||||
|
||||
from ..Script import Script
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ from UM.Application import Application
|
|||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.FileHandler.WriteFileJob import WriteFileJob
|
||||
from UM.FileHandler.FileWriter import FileWriter #To check against the write modes (text vs. binary).
|
||||
from UM.FileHandler.FileWriter import FileWriter # To check against the write modes (text vs. binary).
|
||||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.OutputDevice.OutputDevice import OutputDevice
|
||||
from UM.OutputDevice import OutputDeviceError
|
||||
|
|
|
@ -372,7 +372,10 @@ class SimulationView(CuraView):
|
|||
self._minimum_path_num = min(self._minimum_path_num, self._current_path_num)
|
||||
# update _current time when the path is changed by user
|
||||
if self._current_path_num < self._max_paths and round(self._current_path_num)== self._current_path_num:
|
||||
self._current_time = self.cumulativeLineDuration()[int(self._current_path_num)]
|
||||
actual_path_num = int(self._current_path_num)
|
||||
cumulative_line_duration = self.cumulativeLineDuration()
|
||||
if actual_path_num < len(cumulative_line_duration):
|
||||
self._current_time = cumulative_line_duration[actual_path_num]
|
||||
|
||||
self._startUpdateTopLayers()
|
||||
self.currentPathNumChanged.emit()
|
||||
|
|
|
@ -5,7 +5,7 @@ import json
|
|||
import os
|
||||
import platform
|
||||
import time
|
||||
from typing import cast, Optional, Set, TYPE_CHECKING
|
||||
from typing import Optional, Set, TYPE_CHECKING
|
||||
|
||||
from PyQt6.QtCore import pyqtSlot, QObject
|
||||
from PyQt6.QtNetwork import QNetworkRequest
|
||||
|
|
|
@ -16,8 +16,6 @@ from UM.Application import Application
|
|||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Math.Color import Color
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Platform import Platform
|
||||
from UM.Event import Event
|
||||
|
||||
from UM.View.RenderBatch import RenderBatch
|
||||
|
|
|
@ -22,7 +22,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.Settings.CuraStackBuilder import CuraStackBuilder
|
||||
from cura.Settings.GlobalStack import GlobalStack
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ try:
|
|||
except ImportError:
|
||||
Logger.log("w", "Could not import UFPWriter; libCharon may be missing")
|
||||
|
||||
from UM.i18n import i18nCatalog #To translate the file format description.
|
||||
from UM.Mesh.MeshWriter import MeshWriter #For the binary mode flag.
|
||||
from UM.i18n import i18nCatalog # To translate the file format description.
|
||||
from UM.Mesh.MeshWriter import MeshWriter # For the binary mode flag.
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
|
||||
from .avr_isp import ispBase
|
||||
from .avr_isp.stk500v2 import Stk500v2
|
||||
|
||||
from time import time, sleep
|
||||
from serial import Serial, SerialException
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import os
|
|||
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.Mesh.MeshWriter import MeshWriter #To get the g-code output.
|
||||
from UM.Message import Message #Show an error when already printing.
|
||||
from UM.PluginRegistry import PluginRegistry #To get the g-code output.
|
||||
from UM.Mesh.MeshWriter import MeshWriter # To get the g-code output.
|
||||
from UM.Message import Message # Show an error when already printing.
|
||||
from UM.PluginRegistry import PluginRegistry # To get the g-code output.
|
||||
from UM.Qt.Duration import DurationFormat
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
@ -19,7 +19,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController
|
|||
from .AutoDetectBaudJob import AutoDetectBaudJob
|
||||
from .AvrFirmwareUpdater import AvrFirmwareUpdater
|
||||
|
||||
from io import StringIO #To write the g-code output.
|
||||
from io import StringIO # To write the g-code output.
|
||||
from queue import Queue
|
||||
from serial import Serial, SerialException, SerialTimeoutException
|
||||
from threading import Thread, Event
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To write config files to strings as if they were files.
|
||||
import os.path #To get the path to write new user profiles to.
|
||||
import configparser # To read config files.
|
||||
import io # To write config files to strings as if they were files.
|
||||
import os.path # To get the path to write new user profiles to.
|
||||
from typing import Dict, List, Optional, Set, Tuple
|
||||
import urllib #To serialise the user container file name properly.
|
||||
import urllib # To serialise the user container file name properly.
|
||||
import urllib.parse
|
||||
|
||||
import UM.VersionUpgrade #To indicate that a file is of incorrect format.
|
||||
import UM.VersionUpgradeManager #To schedule more files to be upgraded.
|
||||
from UM.Resources import Resources #To get the config storage path.
|
||||
import UM.VersionUpgrade # To indicate that a file is of incorrect format.
|
||||
import UM.VersionUpgradeManager # To schedule more files to be upgraded.
|
||||
from UM.Resources import Resources # To get the config storage path.
|
||||
|
||||
## Creates a new machine instance instance by parsing a serialised machine
|
||||
# instance in version 1 of the file format.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To output config files to string.
|
||||
import configparser # To read config files.
|
||||
import io # To output config files to string.
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import UM.VersionUpgrade #To indicate that a file is of the wrong format.
|
||||
import UM.VersionUpgrade # To indicate that a file is of the wrong format.
|
||||
|
||||
## Creates a new preferences instance by parsing a serialised preferences file
|
||||
# in version 1 of the file format.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import configparser #To read config files.
|
||||
import io #To write config files to strings as if they were files.
|
||||
import configparser # To read config files.
|
||||
import io # To write config files to strings as if they were files.
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
import UM.VersionUpgrade
|
||||
|
|
|
@ -7,7 +7,6 @@ from PyQt6.QtGui import QOpenGLContext, QImage
|
|||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Color import Color
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Platform import Platform
|
||||
from UM.Event import Event
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
import copy
|
||||
import io
|
||||
import json #To parse the product-to-id mapping file.
|
||||
import os.path #To find the product-to-id mapping.
|
||||
from typing import Any, Dict, List, Optional, Tuple, cast, Set, Union
|
||||
import json # To parse the product-to-id mapping file.
|
||||
import os.path # To find the product-to-id mapping.
|
||||
from typing import Any, Dict, List, Optional, Tuple, cast, Set
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pytest
|
||||
pyinstaller==5.8.0
|
||||
pyinstaller==6.3.0
|
||||
pyinstaller-hooks-contrib
|
||||
pyyaml
|
||||
sip==6.5.1
|
||||
|
|
|
@ -9,16 +9,20 @@
|
|||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_layer_0": { "value": 3000 },
|
||||
"acceleration_print": { "value": 3000 },
|
||||
"acceleration_travel": { "value": 5000 },
|
||||
"acceleration_print":
|
||||
{
|
||||
"maximum_value_warning": "20000",
|
||||
"value": 10000
|
||||
},
|
||||
"acceleration_wall": { "value": "acceleration_print/2" },
|
||||
"cool_fan_full_layer": { "value": 2 },
|
||||
"infill_line_width": { "value": "line_width + 0.05" },
|
||||
"infill_overlap": { "value": "0 if infill_sparse_density < 40.01 and infill_pattern != 'concentric' else -5" },
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 35 else 'grid'" },
|
||||
"initial_layer_line_width_factor": { "value": "100.0 if resolveOrValue('adhesion_type') == 'raft' else 125 if line_width < 0.5 else 110" },
|
||||
"machine_acceleration": { "value": 3000 },
|
||||
"machine_acceleration": { "value": 5000 },
|
||||
"machine_depth": { "default_value": 230 },
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
|
||||
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth - 5} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
|
@ -32,12 +36,12 @@
|
|||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_height": { "default_value": 270 },
|
||||
"machine_max_acceleration_e": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 5000 },
|
||||
"machine_max_acceleration_y": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 20000 },
|
||||
"machine_max_acceleration_y": { "value": 20000 },
|
||||
"machine_name": { "default_value": "ELEGOO NEPTUNE 4" },
|
||||
"machine_nozzle_cool_down_speed": { "value": 0.75 },
|
||||
"machine_nozzle_heat_up_speed": { "value": 1.6 },
|
||||
"machine_start_gcode": { "default_value": "G28 ;home\nG92 E0 ;Reset Extruder\nG1 Z4.0 F3000 ;Move Z Axis up\nG92 E0 ;Reset Extruder\nG1 X1.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X1.1 Y80.0 Z0.28 F1500.0 E10 ;Draw the first line\nG1 X1.4 Y80.0 Z0.28 F5000.0 ;Move to side a little\nG1 X1.4 Y20 Z0.28 F1500.0 E20 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up" },
|
||||
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 / 4 PRO\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X67.5 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X67.5 Y0 Z0.4 F300 ;Move to start position\nG1 X167.5 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X162.5 F3000\nG92 E0 ;Reset Extruder" },
|
||||
"machine_width": { "default_value": 235 },
|
||||
"retraction_amount": { "default_value": 0.5 },
|
||||
"retraction_count_max": { "value": 80 },
|
||||
|
|
62
resources/definitions/elegoo_neptune_4max.def.json
Normal file
62
resources/definitions/elegoo_neptune_4max.def.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "ELEGOO NEPTUNE 4 Max",
|
||||
"inherits": "elegoo_neptune_4",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "mastercaution",
|
||||
"platform": "elegoo_platform_max.3mf",
|
||||
"platform_offset": [
|
||||
-2.1,
|
||||
-0.2,
|
||||
0
|
||||
],
|
||||
"quality_definition": "elegoo_neptune_4"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_print":
|
||||
{
|
||||
"maximum_value_warning": "15000",
|
||||
"value": 2500
|
||||
},
|
||||
"machine_depth": { "default_value": 430 },
|
||||
"machine_height": { "default_value": 482 },
|
||||
"machine_max_acceleration_e": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 15000 },
|
||||
"machine_max_acceleration_y": { "value": 15000 },
|
||||
"machine_name": { "default_value": "ELEGOO NEPTUNE 4 Max" },
|
||||
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 MAX\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X165 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X165 Y0 Z0.4 F300 ;Move to start position\nG1 X265 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X260 F3000\nG92 E0 ;Reset Extruder" },
|
||||
"machine_width": { "default_value": 430 },
|
||||
"nozzle_disallowed_areas":
|
||||
{
|
||||
"default_value": [
|
||||
[
|
||||
[-215, -215],
|
||||
[-215, 215],
|
||||
[-211, 215],
|
||||
[-211, -215]
|
||||
],
|
||||
[
|
||||
[215, 215],
|
||||
[215, -215],
|
||||
[211, -215],
|
||||
[211, 215]
|
||||
],
|
||||
[
|
||||
[-215, -215],
|
||||
[215, -215],
|
||||
[-215, -211],
|
||||
[215, -211]
|
||||
],
|
||||
[
|
||||
[-215, 215],
|
||||
[215, 215],
|
||||
[-215, 211],
|
||||
[215, 211]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
59
resources/definitions/elegoo_neptune_4plus.def.json
Normal file
59
resources/definitions/elegoo_neptune_4plus.def.json
Normal file
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "ELEGOO NEPTUNE 4 Plus",
|
||||
"inherits": "elegoo_neptune_4",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "mastercaution",
|
||||
"platform": "elegoo_platform_max.3mf",
|
||||
"platform_offset": [
|
||||
-2.1,
|
||||
-0.2,
|
||||
0
|
||||
],
|
||||
"quality_definition": "elegoo_neptune_4"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_print": { "value": 4000 },
|
||||
"machine_acceleration": { "value": 4000 },
|
||||
"machine_depth": { "default_value": 330 },
|
||||
"machine_height": { "default_value": 387 },
|
||||
"machine_max_acceleration_e": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 20000 },
|
||||
"machine_max_acceleration_y": { "value": 20000 },
|
||||
"machine_name": { "default_value": "ELEGOO NEPTUNE 4 Plus" },
|
||||
"machine_start_gcode": { "default_value": ";ELEGOO NEPTUNE 4 PLUS\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140 ;Start heating extruder\nM190 S{material_bed_temperature_layer_0} ;Wait for the bed to reach print temp\nG90\nG28 ;home\nG1 Z10 F300\nG1 X115 Y0 F6000\nG1 Z0 F300\nM109 S{material_print_temperature_layer_0} ;Wait for extruder to reach print temp\nG92 E0 ;Reset Extruder\nG1 X115 Y0 Z0.4 F300 ;Move to start position\nG1 X215 E30 F400 ;Draw the first line\nG1 Z0.6 F120.0 ;Move to side a little\nG1 X210 F3000\nG92 E0 ;Reset Extruder" },
|
||||
"machine_width": { "default_value": 330 },
|
||||
"nozzle_disallowed_areas":
|
||||
{
|
||||
"default_value": [
|
||||
[
|
||||
[-165, -165],
|
||||
[-165, 165],
|
||||
[-161, 165],
|
||||
[-161, -165]
|
||||
],
|
||||
[
|
||||
[165, 165],
|
||||
[165, -165],
|
||||
[161, -165],
|
||||
[161, 165]
|
||||
],
|
||||
[
|
||||
[-165, -165],
|
||||
[165, -165],
|
||||
[-165, -161],
|
||||
[165, -161]
|
||||
],
|
||||
[
|
||||
[-165, 165],
|
||||
[165, 165],
|
||||
[-165, 161],
|
||||
[165, 161]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1684,7 +1684,7 @@
|
|||
"value": "skin_line_width * 2",
|
||||
"default_value": 1,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "skin_line_width * 3",
|
||||
"maximum_value_warning": "skin_line_width * 10",
|
||||
"type": "float",
|
||||
"enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'",
|
||||
"limit_to_extruder": "top_bottom_extruder_nr",
|
||||
|
@ -5151,7 +5151,7 @@
|
|||
"unit": "mm",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "machine_nozzle_size",
|
||||
"maximum_value_warning": "5*layer_height",
|
||||
"default_value": 0.1,
|
||||
"limit_to_extruder": "support_interface_extruder_nr if support_interface_enable else support_infill_extruder_nr",
|
||||
"enabled": "support_enable or support_meshes_present",
|
||||
|
@ -6469,6 +6469,18 @@
|
|||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "raft_surface_extruder_nr"
|
||||
},
|
||||
"raft_surface_monotonic":
|
||||
{
|
||||
"label": "Monotonic Raft Top Surface Order",
|
||||
"description": "Print raft top surface lines in an ordering that causes them to always overlap with adjacent lines in a single direction. This takes slightly more time to print, but makes the surface look more consistent, which is also visible on the model bottom surface.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": "skin_monotonic",
|
||||
"enabled": "resolveOrValue('adhesion_type') == 'raft' and raft_surface_layers > 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"limit_to_extruder": "raft_surface_extruder_nr"
|
||||
},
|
||||
"raft_wall_count":
|
||||
{
|
||||
"label": "Raft Wall Count",
|
||||
|
@ -7269,6 +7281,16 @@
|
|||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"user_defined_print_order_enabled":
|
||||
{
|
||||
"label": "Set Print Sequence Manually",
|
||||
"description": "Allows to order the object list to set the print sequence manually. First object from the list will be printed first.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"enabled": "print_sequence == 'one_at_a_time'"
|
||||
},
|
||||
"infill_mesh":
|
||||
{
|
||||
"label": "Infill Mesh",
|
||||
|
|
15
resources/definitions/flashforge_adventurer3.def.json
Normal file
15
resources/definitions/flashforge_adventurer3.def.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Adventurer 3",
|
||||
"inherits": "flashforge_adventurer3c",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Jeremie-C",
|
||||
"supports_network_connection": true
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_name": { "default_value": "Adventurer 3" }
|
||||
}
|
||||
}
|
33
resources/definitions/flashforge_adventurer3c.def.json
Normal file
33
resources/definitions/flashforge_adventurer3c.def.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Adventurer 3C",
|
||||
"inherits": "flashforge_adventurer_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Jeremie-C",
|
||||
"quality_definition": "flashforge_adventurer3"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"default_material_bed_temperature": { "maximum_value_warning": "100" },
|
||||
"gantry_height": { "value": "150" },
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
"machine_depth": { "default_value": 150 },
|
||||
"machine_end_gcode": { "default_value": ";end gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM132 X Y A B\nM652\nG91\nM18" },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-20, 10],
|
||||
[-20, -10],
|
||||
[10, 10],
|
||||
[10, -10]
|
||||
]
|
||||
},
|
||||
"machine_height": { "default_value": 150 },
|
||||
"machine_name": { "default_value": "Adventurer 3C" },
|
||||
"machine_start_gcode": { "default_value": ";Start Gcode\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\n;End Start" },
|
||||
"machine_width": { "default_value": 150 },
|
||||
"speed_print": { "maximum_value_warning": 100 }
|
||||
}
|
||||
}
|
33
resources/definitions/flashforge_adventurer4.def.json
Normal file
33
resources/definitions/flashforge_adventurer4.def.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Adventurer 4",
|
||||
"inherits": "flashforge_adventurer_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Jeremie-C",
|
||||
"quality_definition": "flashforge_adventurer4",
|
||||
"supports_network_connection": true
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"default_material_bed_temperature": { "maximum_value_warning": "110" },
|
||||
"gantry_height": { "value": "250" },
|
||||
"machine_depth": { "default_value": 200 },
|
||||
"machine_end_gcode": { "default_value": ";End Gcode\nM104 S0 T0\nM140 S0 T0\nG162 Z F1800\nG28 X Y\nM132 X Y A B\nM652\nG91\nM18" },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-20, 10],
|
||||
[-20, -10],
|
||||
[10, 10],
|
||||
[10, -10]
|
||||
]
|
||||
},
|
||||
"machine_height": { "default_value": 250 },
|
||||
"machine_name": { "default_value": "Adventurer 4" },
|
||||
"machine_start_gcode": { "default_value": ";Start Gcode\nG28\nM132 X Y Z A B\nG1 Z50.000 F420\nG161 X Y F3300\nM7 T0\nM6 T0\nM651 S255\n;End Start" },
|
||||
"machine_use_extruder_offset_to_offset_coords": { "default_value": false },
|
||||
"machine_width": { "default_value": 220 }
|
||||
}
|
||||
}
|
14
resources/definitions/flashforge_adventurer4lite.def.json
Normal file
14
resources/definitions/flashforge_adventurer4lite.def.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Adventurer 4 Lite",
|
||||
"inherits": "flashforge_adventurer4",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Jeremie-C"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_name": { "default_value": "Adventurer 4 Lite" }
|
||||
}
|
||||
}
|
34
resources/definitions/flashforge_adventurer_base.def.json
Normal file
34
resources/definitions/flashforge_adventurer_base.def.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Flashforge Adventurer Base",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata":
|
||||
{
|
||||
"visible": false,
|
||||
"author": "Jeremie-C",
|
||||
"manufacturer": "Flashforge",
|
||||
"file_formats": "application/gx;text/x-gcode",
|
||||
"first_start_actions": [ "MachineSettingsAction" ],
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
"has_variants": true,
|
||||
"machine_extruder_trains": { "0": "flashforge_adventurer_extruder_0" },
|
||||
"preferred_material": "generic_pla",
|
||||
"preferred_quality_type": "normal",
|
||||
"preferred_variant_name": "0.4mm Nozzle",
|
||||
"variants_name": "Nozzle Size"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"adhesion_type": { "default_value": "skirt" },
|
||||
"default_material_print_temperature": { "maximum_value_warning": "265" },
|
||||
"layer_height":
|
||||
{
|
||||
"maximum_value_warning": "0.4",
|
||||
"minimum_value_warning": "0.1"
|
||||
},
|
||||
"machine_center_is_zero": { "default_value": true },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_heated_bed": { "default_value": true }
|
||||
}
|
||||
}
|
20
resources/definitions/ratrig_base.def.json
Normal file
20
resources/definitions/ratrig_base.def.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig Printer",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata":
|
||||
{
|
||||
"visible": false,
|
||||
"author": "nu-hin",
|
||||
"manufacturer": "RatRig",
|
||||
"file_formats": "text/x-gcode",
|
||||
"exclude_materials": [],
|
||||
"first_start_actions": [ "MachineSettingsAction" ],
|
||||
"has_materials": true,
|
||||
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
|
||||
"preferred_material": "generic_pla",
|
||||
"preferred_quality_type": "standard",
|
||||
"quality_definition": "ratrig_base",
|
||||
"supported_actions": [ "MachineSettingsAction" ]
|
||||
}
|
||||
}
|
23
resources/definitions/ratrig_vcore3_200.def.json
Normal file
23
resources/definitions/ratrig_vcore3_200.def.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Core 3 200mm",
|
||||
"inherits": "ratrig_vcore3_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"platform": "ratrig_vcore3_200.stl",
|
||||
"platform_offset": [
|
||||
0,
|
||||
5,
|
||||
0
|
||||
],
|
||||
"weight": 16
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_depth": { "default_value": 200 },
|
||||
"machine_height": { "default_value": 200 },
|
||||
"machine_name": { "default_value": "RatRig V-Core 3 200mm" },
|
||||
"machine_width": { "default_value": 200 }
|
||||
}
|
||||
}
|
23
resources/definitions/ratrig_vcore3_300.def.json
Normal file
23
resources/definitions/ratrig_vcore3_300.def.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Core 3 300mm",
|
||||
"inherits": "ratrig_vcore3_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"platform": "ratrig_vcore3_300.stl",
|
||||
"platform_offset": [
|
||||
0,
|
||||
5,
|
||||
0
|
||||
],
|
||||
"weight": 16
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_depth": { "default_value": 300 },
|
||||
"machine_height": { "default_value": 300 },
|
||||
"machine_name": { "default_value": "RatRig V-Core 3 300mm" },
|
||||
"machine_width": { "default_value": 300 }
|
||||
}
|
||||
}
|
23
resources/definitions/ratrig_vcore3_400.def.json
Normal file
23
resources/definitions/ratrig_vcore3_400.def.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Core 3 400mm",
|
||||
"inherits": "ratrig_vcore3_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"platform": "ratrig_vcore3_400.stl",
|
||||
"platform_offset": [
|
||||
0,
|
||||
3,
|
||||
0
|
||||
],
|
||||
"weight": 16
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_depth": { "default_value": 400 },
|
||||
"machine_height": { "default_value": 400 },
|
||||
"machine_name": { "default_value": "RatRig V-Core 3 400mm" },
|
||||
"machine_width": { "default_value": 400 }
|
||||
}
|
||||
}
|
23
resources/definitions/ratrig_vcore3_500.def.json
Normal file
23
resources/definitions/ratrig_vcore3_500.def.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Core 3 500mm",
|
||||
"inherits": "ratrig_vcore3_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"platform": "ratrig_vcore3_500.stl",
|
||||
"platform_offset": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"weight": 16
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"machine_depth": { "default_value": 500 },
|
||||
"machine_height": { "default_value": 500 },
|
||||
"machine_name": { "default_value": "RatRig V-Core 3 500mm" },
|
||||
"machine_width": { "default_value": 500 }
|
||||
}
|
||||
}
|
116
resources/definitions/ratrig_vcore3_base.def.json
Normal file
116
resources/definitions/ratrig_vcore3_base.def.json
Normal file
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Core 3",
|
||||
"inherits": "ratrig_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": false,
|
||||
"has_machine_quality": true,
|
||||
"has_variants": true,
|
||||
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
|
||||
"preferred_variant_name": "0.4mm Nozzle",
|
||||
"variants_name": "Nozzle Size"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_enabled": { "value": true },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_roofing": { "enabled": "acceleration_enabled and roofing_layer_count > 0 and top_layers > 0" },
|
||||
"acceleration_topbottom": { "value": "acceleration_print / 3" },
|
||||
"acceleration_travel": { "value": 3000 },
|
||||
"acceleration_travel_layer_0": { "value": "acceleration_travel / 3" },
|
||||
"adaptive_layer_height_variation": { "value": 0.04 },
|
||||
"adaptive_layer_height_variation_step": { "value": 0.04 },
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
"brim_replaces_support": { "value": false },
|
||||
"brim_width": { "value": "3" },
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_min_layer_time": { "value": 2 },
|
||||
"fill_outline_gaps": { "value": false },
|
||||
"gantry_height": { "value": 30 },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30 },
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_wipe_dist": { "value": 0 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"layer_height_0": { "default_value": 0.2 },
|
||||
"machine_acceleration": { "value": 3000 },
|
||||
"machine_end_gcode": { "default_value": "END_PRINT" },
|
||||
"machine_extruder_count": { "default_value": 1 },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-40, 90],
|
||||
[-40, -30],
|
||||
[40, -30],
|
||||
[40, 90]
|
||||
]
|
||||
},
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_max_acceleration_e": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 9000 },
|
||||
"machine_max_acceleration_y": { "value": 9000 },
|
||||
"machine_max_acceleration_z": { "value": 100 },
|
||||
"machine_max_feedrate_e": { "value": 60 },
|
||||
"machine_max_feedrate_x": { "value": 500 },
|
||||
"machine_max_feedrate_y": { "value": 500 },
|
||||
"machine_max_feedrate_z": { "value": 10 },
|
||||
"machine_max_jerk_e": { "value": 5 },
|
||||
"machine_max_jerk_xy": { "value": 5 },
|
||||
"machine_max_jerk_z": { "value": 0.4 },
|
||||
"machine_name": { "default_value": "RatRig V-Core 3" },
|
||||
"machine_shape": { "default_value": "rectangular" },
|
||||
"machine_show_variants": { "default_value": true },
|
||||
"machine_start_gcode": { "default_value": "START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0}" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"meshfix_maximum_resolution": { "value": "0.25" },
|
||||
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
|
||||
"minimum_interface_area": { "value": 10 },
|
||||
"minimum_support_area": { "value": 2 },
|
||||
"optimize_wall_printing_order": { "value": true },
|
||||
"retraction_amount": { "value": "machine_nozzle_size * 2" },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_speed": { "value": 40 },
|
||||
"roofing_layer_count": { "value": 1 },
|
||||
"skin_overlap": { "value": 18 },
|
||||
"skirt_brim_minimal_length": { "default_value": 30 },
|
||||
"skirt_gap": { "value": 10 },
|
||||
"skirt_line_count": { "value": 3 },
|
||||
"speed_layer_0": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_print": { "value": 100 },
|
||||
"speed_roofing": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_support": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_support_interface": { "value": "speed_topbottom" },
|
||||
"speed_topbottom": { "value": "math.floor(speed_print / 2)" },
|
||||
"speed_travel": { "value": 250 },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_z_hop": { "value": 5 },
|
||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width/2.0/layer_height)))" },
|
||||
"support_brim_width": { "value": 4 },
|
||||
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
|
||||
"support_interface_density": { "value": 33.333 },
|
||||
"support_interface_enable": { "value": true },
|
||||
"support_interface_height": { "value": "layer_height * 4" },
|
||||
"support_interface_pattern": { "value": "'grid'" },
|
||||
"support_pattern": { "value": "'zigzag'" },
|
||||
"support_use_towers": { "value": false },
|
||||
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
|
||||
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
|
||||
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
|
||||
"support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" },
|
||||
"top_bottom_pattern": { "value": "'lines'" },
|
||||
"top_bottom_thickness": { "value": "layer_height_0 + layer_height * 3" },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
"wall_0_wipe_dist": { "value": 0 },
|
||||
"wall_thickness": { "value": "line_width * 2" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"z_seam_type": { "value": "'back'" }
|
||||
}
|
||||
}
|
127
resources/definitions/ratrig_vminion.def.json
Normal file
127
resources/definitions/ratrig_vminion.def.json
Normal file
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "RatRig V-Minion",
|
||||
"inherits": "ratrig_base",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"platform": "ratrig_vminion.stl",
|
||||
"has_machine_quality": true,
|
||||
"has_variants": true,
|
||||
"machine_extruder_trains": { "0": "ratrig_base_extruder_0" },
|
||||
"platform_offset": [
|
||||
0,
|
||||
5,
|
||||
0
|
||||
],
|
||||
"preferred_variant_name": "0.4mm Nozzle",
|
||||
"variants_name": "Nozzle Size",
|
||||
"weight": 8
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_enabled": { "value": true },
|
||||
"acceleration_layer_0": { "value": "acceleration_topbottom" },
|
||||
"acceleration_roofing": { "enabled": "acceleration_enabled and roofing_layer_count > 0 and top_layers > 0" },
|
||||
"acceleration_topbottom": { "value": "acceleration_print / 3" },
|
||||
"acceleration_travel": { "value": 3000 },
|
||||
"acceleration_travel_layer_0": { "value": "acceleration_travel / 3" },
|
||||
"adaptive_layer_height_variation": { "value": 0.04 },
|
||||
"adaptive_layer_height_variation_step": { "value": 0.04 },
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
"brim_replaces_support": { "value": false },
|
||||
"brim_width": { "value": "3" },
|
||||
"cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" },
|
||||
"cool_min_layer_time": { "value": 2 },
|
||||
"fill_outline_gaps": { "value": false },
|
||||
"gantry_height": { "value": 30 },
|
||||
"infill_before_walls": { "value": false },
|
||||
"infill_overlap": { "value": 30 },
|
||||
"infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" },
|
||||
"infill_wipe_dist": { "value": 0 },
|
||||
"layer_height": { "default_value": 0.2 },
|
||||
"layer_height_0": { "default_value": 0.2 },
|
||||
"machine_acceleration": { "value": 3000 },
|
||||
"machine_depth": { "default_value": 180 },
|
||||
"machine_end_gcode": { "default_value": "END_PRINT" },
|
||||
"machine_extruder_count": { "default_value": 1 },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
[-40, 90],
|
||||
[-40, -30],
|
||||
[40, -30],
|
||||
[40, 90]
|
||||
]
|
||||
},
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_height": { "default_value": 180 },
|
||||
"machine_max_acceleration_e": { "value": 5000 },
|
||||
"machine_max_acceleration_x": { "value": 9000 },
|
||||
"machine_max_acceleration_y": { "value": 9000 },
|
||||
"machine_max_acceleration_z": { "value": 100 },
|
||||
"machine_max_feedrate_e": { "value": 60 },
|
||||
"machine_max_feedrate_x": { "value": 500 },
|
||||
"machine_max_feedrate_y": { "value": 500 },
|
||||
"machine_max_feedrate_z": { "value": 10 },
|
||||
"machine_max_jerk_e": { "value": 5 },
|
||||
"machine_max_jerk_xy": { "value": 5 },
|
||||
"machine_max_jerk_z": { "value": 0.4 },
|
||||
"machine_name": { "default_value": "RatRig V-Minion" },
|
||||
"machine_shape": { "default_value": "rectangular" },
|
||||
"machine_show_variants": { "default_value": true },
|
||||
"machine_start_gcode": { "default_value": "START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0}" },
|
||||
"machine_width": { "default_value": 180 },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature" },
|
||||
"meshfix_maximum_resolution": { "value": "0.25" },
|
||||
"meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" },
|
||||
"minimum_interface_area": { "value": 10 },
|
||||
"minimum_support_area": { "value": 2 },
|
||||
"optimize_wall_printing_order": { "value": true },
|
||||
"retraction_amount": { "value": "machine_nozzle_size * 2" },
|
||||
"retraction_combing": { "value": "'off' if retraction_hop_enabled else 'noskin'" },
|
||||
"retraction_combing_max_distance": { "value": 30 },
|
||||
"retraction_count_max": { "value": 100 },
|
||||
"retraction_extrusion_window": { "value": 10 },
|
||||
"retraction_speed": { "value": 40 },
|
||||
"roofing_layer_count": { "value": 1 },
|
||||
"skin_overlap": { "value": 18 },
|
||||
"skirt_brim_minimal_length": { "default_value": 30 },
|
||||
"skirt_gap": { "value": 10 },
|
||||
"skirt_line_count": { "value": 3 },
|
||||
"speed_layer_0": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_print": { "value": 100 },
|
||||
"speed_roofing": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_support": { "value": "math.floor(speed_print * 3 / 10)" },
|
||||
"speed_support_interface": { "value": "speed_topbottom" },
|
||||
"speed_topbottom": { "value": "math.floor(speed_print / 2)" },
|
||||
"speed_travel": { "value": 250 },
|
||||
"speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" },
|
||||
"speed_wall": { "value": "math.floor(speed_print / 2)" },
|
||||
"speed_wall_x": { "value": "speed_wall" },
|
||||
"speed_z_hop": { "value": 5 },
|
||||
"support_angle": { "value": "math.floor(math.degrees(math.atan(line_width/2.0/layer_height)))" },
|
||||
"support_brim_width": { "value": 4 },
|
||||
"support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" },
|
||||
"support_interface_density": { "value": 33.333 },
|
||||
"support_interface_enable": { "value": true },
|
||||
"support_interface_height": { "value": "layer_height * 4" },
|
||||
"support_interface_pattern": { "value": "'grid'" },
|
||||
"support_pattern": { "value": "'zigzag'" },
|
||||
"support_use_towers": { "value": false },
|
||||
"support_xy_distance": { "value": "wall_line_width_0 * 2" },
|
||||
"support_xy_distance_overhang": { "value": "wall_line_width_0" },
|
||||
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
|
||||
"support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" },
|
||||
"top_bottom_pattern": { "value": "'lines'" },
|
||||
"top_bottom_thickness": { "value": "layer_height_0 + layer_height * 3" },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"travel_retract_before_outer_wall": { "value": true },
|
||||
"wall_0_wipe_dist": { "value": 0 },
|
||||
"wall_thickness": { "value": "line_width * 2" },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"z_seam_type": { "value": "'back'" }
|
||||
}
|
||||
}
|
|
@ -106,6 +106,7 @@
|
|||
"retraction_combing_max_distance": { "value": 15 },
|
||||
"retraction_count_max": { "value": 25 },
|
||||
"retraction_extrusion_window": { "value": 1 },
|
||||
"retraction_min_travel": { "value": 5 },
|
||||
"roofing_layer_count": { "value": "1" },
|
||||
"roofing_material_flow": { "value": "material_flow" },
|
||||
"skin_angles": { "value": "[] if infill_pattern not in ['cross', 'cross_3d'] else [20, 110]" },
|
||||
|
|
|
@ -156,7 +156,6 @@
|
|||
"retraction_hop": { "value": "2" },
|
||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||
"retraction_hop_only_when_collides": { "value": "True" },
|
||||
"retraction_min_travel": { "value": "5" },
|
||||
"retraction_prime_speed": { "value": "15" },
|
||||
"skin_overlap": { "value": "10" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
|
|
|
@ -373,7 +373,6 @@
|
|||
"retraction_hop": { "value": 0.4 },
|
||||
"retraction_hop_enabled": { "value": true },
|
||||
"retraction_hop_only_when_collides": { "value": false },
|
||||
"retraction_min_travel": { "value": "line_width * 4" },
|
||||
"retraction_prime_speed": { "value": "retraction_speed" },
|
||||
"retraction_speed": { "value": 5 },
|
||||
"roofing_layer_count": { "value": 2 },
|
||||
|
@ -386,6 +385,7 @@
|
|||
"skin_preshrink": { "value": 0 },
|
||||
"skirt_brim_material_flow": { "value": "material_flow" },
|
||||
"skirt_brim_minimal_length": { "value": 500 },
|
||||
"small_skin_width": { "value": 4 },
|
||||
"speed_equalize_flow_width_factor": { "value": 0 },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
"speed_print": { "value": 50 },
|
||||
|
@ -426,7 +426,7 @@
|
|||
"travel_avoid_other_parts": { "value": false },
|
||||
"wall_0_inset": { "value": 0 },
|
||||
"wall_0_material_flow": { "value": "material_flow" },
|
||||
"wall_0_wipe_dist": { "value": 0 },
|
||||
"wall_0_wipe_dist": { "value": 0.8 },
|
||||
"wall_material_flow": { "value": "material_flow" },
|
||||
"wall_x_material_flow": { "value": "material_flow" },
|
||||
"xy_offset": { "value": 0 },
|
||||
|
|
|
@ -108,7 +108,6 @@
|
|||
"retraction_hop": { "value": "2" },
|
||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||
"retraction_hop_only_when_collides": { "value": "True" },
|
||||
"retraction_min_travel": { "value": "5" },
|
||||
"retraction_prime_speed": { "value": "15" },
|
||||
"retraction_speed": { "value": "45" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
|
|
|
@ -110,7 +110,6 @@
|
|||
"retraction_hop": { "value": "2" },
|
||||
"retraction_hop_enabled": { "value": "extruders_enabled_count > 1" },
|
||||
"retraction_hop_only_when_collides": { "value": "True" },
|
||||
"retraction_min_travel": { "value": "5" },
|
||||
"retraction_prime_speed": { "value": "15" },
|
||||
"retraction_speed": { "value": "45" },
|
||||
"speed_prime_tower": { "value": "speed_topbottom" },
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
"machine_endstop_positive_direction_y": { "default_value": true },
|
||||
"machine_endstop_positive_direction_z": { "default_value": false },
|
||||
"machine_feeder_wheel_diameter": { "default_value": 7.5 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (RepRap)" },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_head_with_fans_polygon":
|
||||
{
|
||||
"default_value": [
|
||||
|
@ -76,7 +76,7 @@
|
|||
"machine_max_jerk_xy": { "default_value": 20 },
|
||||
"machine_max_jerk_z": { "default_value": 1 },
|
||||
"machine_name": { "default_value": "VORON2" },
|
||||
"machine_start_gcode": { "default_value": "print_start" },
|
||||
"machine_start_gcode": { "default_value": ";Nozzle diameter = {machine_nozzle_size}\n;Filament type = {material_type}\n;Filament name = {material_name}\n;Filament weight = {filament_weight}\n; M190 S{material_bed_temperature_layer_0}\n; M109 S{material_print_temperature_layer_0}\nprint_start EXTRUDER={material_print_temperature_layer_0} BED={material_bed_temperature_layer_0} CHAMBER={build_volume_temperature}" },
|
||||
"machine_steps_per_mm_x": { "default_value": 80 },
|
||||
"machine_steps_per_mm_y": { "default_value": 80 },
|
||||
"machine_steps_per_mm_z": { "default_value": 400 },
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "flashforge_adventurer_base",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
16
resources/extruders/ratrig_base_extruder_0.def.json
Normal file
16
resources/extruders/ratrig_base_extruder_0.def.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "ratrig_base",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
|
@ -4946,6 +4946,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Rozdělit modely"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Tisknout před"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Tisknout po"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Odinstalovat"
|
||||
|
|
|
@ -2583,6 +2583,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Tisková sekvence"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Nastavit tiskovou sekvenci ručně"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Umožňuje řadit seznam objektů pro ruční nastavení tiskové sekvence. První objekt ze seznamu bude vytisknut jako první."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Rychlost tisku"
|
||||
|
|
|
@ -4565,6 +4565,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -4930,6 +4930,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Gruppierung für Modelle aufheben"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Vor dem Drucken"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Nach dem Drucken"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Deinstallieren"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Druckreihenfolge"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Druckreihenfolge manuell einstellen"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Ermöglicht das Ordnen der Objektliste, um die Druckreihenfolge manuell festzulegen. Das erste Objekt aus der Liste wird zuerst gedruckt."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Druckgeschwindigkeit"
|
||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir después"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Secuencia de impresión"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Establecer secuencia de impresión manualmente"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permite ordenar la lista de objetos para establecer la secuencia de impresión manualmente. El primer objeto de la lista se imprimirá primero."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocidad de impresión"
|
||||
|
|
|
@ -4588,6 +4588,14 @@ msgctxt "print_sequence option one_at_a_time"
|
|||
msgid "One at a Time"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "infill_mesh label"
|
||||
msgid "Infill Mesh"
|
||||
msgstr ""
|
||||
|
|
|
@ -4899,6 +4899,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Poista mallien ryhmitys"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Tulosta ennen"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Tulosta jälkeen"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2578,6 +2578,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Tulostusjärjestys"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Aseta tulostusjärjestys manuaalisesti"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Mahdollistaa kohteiden järjestämisen tulostusjärjestyksen manuaaliseen asettamiseen. Listan ensimmäinen kohde tulostetaan ensin."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Tulostusnopeus"
|
||||
|
|
|
@ -4928,6 +4928,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Dégrouper les modèles"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimer avant"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimer après"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Désinstaller"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Séquence d'impression"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Définir la séquence d'impression manuellement"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permet de classer la liste des objets pour définir manuellement la séquence d'impression. Le premier objet de la liste sera imprimé en premier."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Vitesse d’impression"
|
||||
|
|
|
@ -4913,6 +4913,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Csoport bontása"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Nyomtatás előtt"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Nyomtatás után"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Nyomtatási sorrend"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Nyomtatási sorrend kézi beállítása"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Lehetővé teszi az objektumlista rendezését a nyomtatási sorrend kézi beállításához. A lista első objektuma lesz először nyomtatva."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Nyomtatási sebesség"
|
||||
|
|
|
@ -4931,6 +4931,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Separa modelli"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Stampa prima"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Stampa dopo"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Disinstalla"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sequenza di stampa"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Imposta manualmente la sequenza di stampa"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Consente di ordinare l'elenco degli oggetti per impostare manualmente la sequenza di stampa. Il primo oggetto dell'elenco sarà stampato per primo."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocità di stampa"
|
||||
|
|
|
@ -4914,6 +4914,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "モデルを非グループ化"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "印刷前"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "印刷後"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "アンインストール"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "印刷頻度"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "手動で印刷順序を設定する"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "オブジェクトリストを並べ替えて、手動で印刷順序を設定することができます。リストの最初のオブジェクトが最初に印刷されます。"
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "印刷速度"
|
||||
|
|
|
@ -4917,6 +4917,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "모델 그룹 해제"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "인쇄 전"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "인쇄 후"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "설치 제거"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "프린팅 순서"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "수동으로 인쇄 순서 설정"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "객체 목록을 정렬하여 수동으로 인쇄 순서를 설정할 수 있습니다. 목록의 첫 번째 객체가 먼저 인쇄됩니다."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "프린팅 속도"
|
||||
|
|
|
@ -4925,6 +4925,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Groeperen van Modellen Opheffen"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Afdrukken voor"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Afdrukken na"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "De-installeren"
|
||||
|
|
|
@ -2580,6 +2580,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Printvolgorde"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Handmatig afdrukvolgorde instellen"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Maakt het mogelijk de objectlijst te ordenen om de afdrukvolgorde handmatig in te stellen. Het eerste object van de lijst wordt als eerste afgedrukt."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Printsnelheid"
|
||||
|
|
|
@ -4916,6 +4916,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Rozgrupuj modele"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Drukuj przed"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Drukuj po"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr ""
|
||||
|
|
|
@ -2584,6 +2584,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sekwencja Wydruku"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Ręczne ustawienie kolejności drukowania"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Umożliwia ręczne ustawienie kolejności drukowania na liście obiektów. Pierwszy obiekt z listy zostanie wydrukowany jako pierwszy."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Prędkość Druku"
|
||||
|
|
|
@ -4942,6 +4942,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar Modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir depois"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
|
@ -2585,6 +2585,14 @@ msgctxt "print_sequence label"
|
|||
msgid "Print Sequence"
|
||||
msgstr "Sequência de Impressão"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled label"
|
||||
msgid "Set Print Sequence Manually"
|
||||
msgstr "Definir sequência de impressão manualmente"
|
||||
|
||||
msgctxt "user_defined_print_order_enabled description"
|
||||
msgid "Allows to order the object list to set the print sequence manually. First object from the list will be printed first."
|
||||
msgstr "Permite ordenar a lista de objetos para definir a sequência de impressão manualmente. O primeiro objeto da lista será impresso primeiro."
|
||||
|
||||
msgctxt "speed_print label"
|
||||
msgid "Print Speed"
|
||||
msgstr "Velocidade de Impressão"
|
||||
|
|
|
@ -4932,6 +4932,14 @@ msgctxt "@action:inmenu menubar:edit"
|
|||
msgid "Ungroup Models"
|
||||
msgstr "Desagrupar Modelos"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print Before"
|
||||
msgstr "Imprimir antes"
|
||||
|
||||
msgctxt "@action:inmenu menubar:edit"
|
||||
msgid "Print After"
|
||||
msgstr "Imprimir depois"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Uninstall"
|
||||
msgstr "Desinstalar"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue