Merge branch 'main' into add_biqu_b2
63
.github/ISSUE_TEMPLATE/SlicingCrash.yaml
vendored
|
|
@ -5,38 +5,25 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### ✨Try our improved Cura 5.7✨
|
||||
Before filling out the report below, we want you to try the latest Cura 5.7.
|
||||
This version of Cura has become significantly more reliable and 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/releases/latest) ####
|
||||
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.
|
||||
### ✨Are you stuck? Have you tried these two things? ✨
|
||||
1- Are you on a Cura version lower than Cura 5.7? We really recommend updating because it resolves a lot of slicing crashes!
|
||||
2- Have you tried fixing the model with software that repairs 3d files and makes them watertight?
|
||||
Are you seeing spots and dots on your model? That is Cura indicating that your model is not watertight.
|
||||
You can try doing a quick [Mesh Fix with the Meshtools Plugin](https://marketplace.ultimaker.com/app/cura/plugins/fieldofview/MeshTools) or other mesh editing software.
|
||||
|
||||
### 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.
|
||||
If you still encounter a crash you are welcome to report the issue so we can use your model as a test case.
|
||||
You can find instructions on how to share your model in a Package for Technical Support below.
|
||||
|
||||

|
||||
|
||||
To save a project file go to File -> Save project.
|
||||
Please make sure to .zip your project file.
|
||||
For big files, you may need to use [WeTransfer](https://wetransfer.com/) or similar file-sharing sites.
|
||||
|
||||
🤔 Before you share, please think to yourself. Is this a model that can be shared?
|
||||
Unfortunately we cannot help if this file is missing.
|
||||
Do you have the project file? Than let's continue ⬇️
|
||||
|
||||
### Questions
|
||||
🤔 Before you share, please think to yourself. Is this a model that can be shared on the internet?
|
||||
**Unfortunately, we cannot help if this file is missing.**
|
||||
|
||||
### Questions
|
||||
- type: input
|
||||
attributes:
|
||||
label: Cura Version
|
||||
placeholder: 5.6.0
|
||||
description: We work hard on improving our slicing crashes. If you are not on the latest version of Cura, [you can download it here](https://github.com/Ultimaker/Cura/releases/latest)
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
We work hard on improving our slicing crashes. Our most recent release is 5.7.1.
|
||||
If you are not on the latest version of Cura, [you can download it here](https://github.com/Ultimaker/Cura/releases/latest)
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating System
|
||||
|
|
@ -50,27 +37,13 @@ body:
|
|||
description: Which printer was selected in Cura?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Name abnormal settings
|
||||
description: Are there any settings that you might have changed that caused the crash? Does your model slice when you select the default profiles?
|
||||
placeholder:
|
||||
validations:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Describe model location
|
||||
description: Does your model slice if you rotate the model 90 degrees or if you move it away from the center of the buildplate?
|
||||
placeholder:
|
||||
validations:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Describe your model
|
||||
description: Have you sliced your model succesfully before? Is it watertight? Have you tried doing a quick [Mesh Fix with the Meshtools Plugin](https://marketplace.ultimaker.com/app/cura/plugins/fieldofview/MeshTools)?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Add your .zip here ⬇️
|
||||
description: You can add the zip file and additional information that is relevant to the issue in the comments below.
|
||||
label: Describe your problem and add the package for technical support as a .zip here ⬇️
|
||||
description: |
|
||||
If you still have Cura open with your crash > Click on Help on top bar > Click on Export Package For Technical Support > Compress the file into a zip > Add the file here to your GitHub issue 🔗
|
||||
|
||||
If you closed Cura, please open Cura to recreate the crash> Select your printer > Load your model > Select your print settings > Click on Help on top bar > Click on Export Package For Technical Support > Compress the file into a zip > Add the file here to your GitHub issue 🔗
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
11
.github/workflows/conan-package-resources.yml
vendored
|
|
@ -12,6 +12,7 @@ on:
|
|||
- 'resources/quality/**'
|
||||
- 'resources/variants/**'
|
||||
- 'resources/conanfile.py'
|
||||
- 'resources/conandata.yml'
|
||||
branches:
|
||||
- 'main'
|
||||
- 'CURA-*'
|
||||
|
|
@ -29,3 +30,13 @@ jobs:
|
|||
platform_mac: false
|
||||
install_system_dependencies: false
|
||||
secrets: inherit
|
||||
|
||||
signal-curator:
|
||||
needs: conan-package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger Curator Workflow
|
||||
run: |
|
||||
gh workflow run --repo ultimaker/curator -r main package.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.CURATOR_TRIGGER_PAT_C3PO }}
|
||||
|
|
|
|||
10
.github/workflows/nightly-stable.yml
vendored
|
|
@ -1,16 +1,16 @@
|
|||
name: Nightly build - stable release
|
||||
run-name: Nightly build - stable release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Daily at 5:15 CET
|
||||
- cron: '15 4 * * *'
|
||||
# on:
|
||||
# schedule:
|
||||
# # Daily at 5:15 CET
|
||||
# - cron: '15 4 * * *'
|
||||
|
||||
jobs:
|
||||
build-nightly:
|
||||
uses: ./.github/workflows/nightly.yml
|
||||
with:
|
||||
cura_conan_version: "cura/[*]@ultimaker/stable"
|
||||
cura_conan_version: "cura/[*]"
|
||||
release_tag: "nightly-stable"
|
||||
caller_workflow: "nightly-stable.yml"
|
||||
secrets: inherit
|
||||
|
|
|
|||
8
.github/workflows/nightly-testing.yml
vendored
|
|
@ -1,10 +1,10 @@
|
|||
name: Nightly build - dev release
|
||||
run-name: Nightly build - dev release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Daily at 4:15 CET
|
||||
- cron: '15 3 * * *'
|
||||
# on:
|
||||
# schedule:
|
||||
# # Daily at 4:15 CET
|
||||
# - cron: '15 3 * * *'
|
||||
|
||||
jobs:
|
||||
build-nightly:
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ jobs:
|
|||
uses: ultimaker/cura-workflows/.github/workflows/cura-installers.yml@main
|
||||
needs: [parse-version, create-packages]
|
||||
with:
|
||||
cura_conan_version: cura/${{ inputs.cura_version }}@ultimaker/stable
|
||||
cura_conan_version: cura/${{ inputs.cura_version }}
|
||||
conan_args: "-c user.sentry:environment=production"
|
||||
secrets: inherit
|
||||
|
||||
|
|
|
|||
|
|
@ -17,3 +17,5 @@ CuraLatestURL = "{{ cura_latest_url }}"
|
|||
ConanInstalls = {{ conan_installs }}
|
||||
|
||||
PythonInstalls = {{ python_installs }}
|
||||
|
||||
DependenciesDescriptions = {{ dependencies_description }}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
version: "5.10.0-alpha.0"
|
||||
version: "5.11.0-alpha.0"
|
||||
requirements:
|
||||
- "cura_resources/5.10.0-alpha.0@ultimaker/testing"
|
||||
- "uranium/5.10.0-alpha.0@ultimaker/testing"
|
||||
- "curaengine/5.10.0-alpha.0@ultimaker/testing"
|
||||
- "cura_binary_data/5.10.0-alpha.0@ultimaker/testing"
|
||||
- "fdm_materials/5.10.0-alpha.0@ultimaker/testing"
|
||||
- "dulcificum/5.10.0@ultimaker/stable"
|
||||
- "pysavitar/5.10.0@ultimaker/stable"
|
||||
- "pynest2d/5.10.0@ultimaker/stable"
|
||||
- "cura_resources/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "uranium/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "curaengine/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "cura_binary_data/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "fdm_materials/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "dulcificum/5.10.0"
|
||||
- "pysavitar/5.11.0-alpha.0"
|
||||
- "pynest2d/5.10.0"
|
||||
requirements_internal:
|
||||
- "fdm_materials/5.10.0-alpha.0@internal/testing"
|
||||
- "cura_private_data/5.10.0-alpha.0@internal/testing"
|
||||
- "fdm_materials/5.11.0-alpha.0@ultimaker/testing"
|
||||
- "cura_private_data/5.11.0-alpha.0@internal/testing"
|
||||
requirements_enterprise:
|
||||
- "native_cad_plugin/2.0.0"
|
||||
urls:
|
||||
|
|
@ -125,6 +125,7 @@ pyinstaller:
|
|||
- "PyQt6.sip"
|
||||
- "stl"
|
||||
- "keyrings.alt"
|
||||
- "pynavlib"
|
||||
collect_all_WINDOWS_ONLY:
|
||||
- "PyQt6.Qt"
|
||||
- "PyQt6.Qt6"
|
||||
|
|
@ -264,6 +265,10 @@ pycharm_targets:
|
|||
module_name: Cura
|
||||
name: pytest in TestSettingVisibilityPresets.py
|
||||
script_name: tests/Settings/TestSettingVisibilityPresets.py
|
||||
- jinja_path: .run_templates/pycharm_cura_test.run.xml.jinja
|
||||
module_name: Cura
|
||||
name: pytest in TestStartEndGCode.py
|
||||
script_name: tests/Machines/TestStartEndGCode.py
|
||||
|
||||
pip_requirements_core:
|
||||
any_os:
|
||||
|
|
@ -567,6 +572,7 @@ pip_requirements_core:
|
|||
hashes:
|
||||
- sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5
|
||||
- sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413
|
||||
|
||||
Windows:
|
||||
twisted-iocpsupport:
|
||||
version: "1.0.2"
|
||||
|
|
@ -588,6 +594,22 @@ pip_requirements_core:
|
|||
hashes:
|
||||
- sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8
|
||||
- sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755
|
||||
pynavlib:
|
||||
version: "0.9.4"
|
||||
hashes:
|
||||
- sha256:fdd5ab5b6e0a2c9bbcebb154ac7303daf845865a1649be04e1bd8e8e5889401f
|
||||
- sha256:493c4b3cacc939b021a694d99723106dbd7ee5515ad4dfc1c7fc8219ef20cf3a
|
||||
- sha256:332831553a70be05fe58c43a08109b42970cfedc6086ffb4306859142a0e9210
|
||||
- sha256:9173f61ad83172c306b92bbe38f949889c158cd6dfdc924db01f257a437bf2a6
|
||||
|
||||
Macos:
|
||||
pynavlib:
|
||||
version: "0.9.4"
|
||||
hashes:
|
||||
- sha256:567efd0af97f9014326898b209eea94d9f5cc58e9f589ccf8354584568fcb87d
|
||||
- sha256:f0d7ce426e816788aa96b419fd7da263eafb99aca46ce3b6e5dbaf2bbf6b614a
|
||||
- sha256:33962a322033a78db05a8c2cc3d59e057fbea5b04879c3c54e2fe3041d691a12
|
||||
- sha256:d06d94b1dee4ba024b4a121869e572f571673a3b8c15b4055f52236d43c19a02
|
||||
|
||||
pip_requirements_dev:
|
||||
any_os:
|
||||
|
|
@ -607,3 +629,10 @@ python_translation_source_folders:
|
|||
qml_translation_source_folders:
|
||||
- resources/qml
|
||||
- plugins
|
||||
|
||||
extra_dependencies:
|
||||
conan:
|
||||
version: "2.7.1"
|
||||
sources_url: https://github.com/conan-io/conan
|
||||
license: MIT
|
||||
summary: Conan C/C++ package manager
|
||||
|
|
|
|||
246
conanfile.py
|
|
@ -1,6 +1,14 @@
|
|||
import json
|
||||
import os
|
||||
import requests
|
||||
import yaml
|
||||
import tempfile
|
||||
import tarfile
|
||||
from datetime import datetime
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from git import Repo
|
||||
from git.exc import GitCommandError
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
|
|
@ -11,7 +19,7 @@ from conan.tools.env import VirtualRunEnv, Environment, VirtualBuildEnv
|
|||
from conan.tools.scm import Version
|
||||
from conan.errors import ConanInvalidConfiguration, ConanException
|
||||
|
||||
required_conan_version = ">=2.7.0"
|
||||
required_conan_version = ">=2.7.0" # When changing the version, also change the one in conandata.yml/extra_dependencies
|
||||
|
||||
|
||||
class CuraConan(ConanFile):
|
||||
|
|
@ -27,7 +35,7 @@ class CuraConan(ConanFile):
|
|||
generators = "VirtualPythonEnv"
|
||||
tool_requires = "gettext/0.22.5"
|
||||
|
||||
python_requires = "translationextractor/[>=2.2.0]@ultimaker/stable"
|
||||
python_requires = "translationextractor/[>=2.2.0]"
|
||||
|
||||
options = {
|
||||
"enterprise": [True, False],
|
||||
|
|
@ -37,6 +45,7 @@ class CuraConan(ConanFile):
|
|||
"cura_debug_mode": [True, False], # FIXME: Use profiles
|
||||
"internal": [True, False],
|
||||
"i18n_extract": [True, False],
|
||||
"skip_licenses_download": [True, False],
|
||||
}
|
||||
default_options = {
|
||||
"enterprise": False,
|
||||
|
|
@ -46,6 +55,7 @@ class CuraConan(ConanFile):
|
|||
"cura_debug_mode": False, # Not yet implemented
|
||||
"internal": False,
|
||||
"i18n_extract": False,
|
||||
"skip_licenses_download": False,
|
||||
}
|
||||
|
||||
def set_version(self):
|
||||
|
|
@ -135,6 +145,183 @@ class CuraConan(ConanFile):
|
|||
|
||||
return python_installs
|
||||
|
||||
@staticmethod
|
||||
def _is_repository_url(url):
|
||||
# That will not work for ALL open-source projects, but should already get a large majority of them
|
||||
return (url.startswith("https://github.com/") or url.startswith("https://gitlab.com/")) and "conan-center-index" not in url
|
||||
|
||||
def _retrieve_pip_license(self, package, sources_url, dependency_description):
|
||||
# Download the sources to get the license file inside
|
||||
self.output.info(f"Retrieving license for {package}")
|
||||
response = requests.get(sources_url)
|
||||
response.raise_for_status()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
sources_path = os.path.join(temp_dir, "sources.tar.gz")
|
||||
with open(sources_path, 'wb') as sources_file:
|
||||
sources_file.write(response.content)
|
||||
|
||||
with tarfile.open(sources_path, 'r:gz') as sources_archive:
|
||||
license_file = "LICENSE"
|
||||
|
||||
for source_file in sources_archive.getnames():
|
||||
if Path(source_file).name == license_file:
|
||||
sources_archive.extract(source_file, temp_dir)
|
||||
|
||||
license_file_path = os.path.join(temp_dir, source_file)
|
||||
with open(license_file_path, 'r', encoding='utf8') as file:
|
||||
dependency_description["license_full"] = file.read()
|
||||
|
||||
def _make_pip_dependency_description(self, package, version, dependencies):
|
||||
url = ["https://pypi.org/pypi", package]
|
||||
if version is not None:
|
||||
url.append(version)
|
||||
url.append("json")
|
||||
|
||||
data = requests.get("/".join(url)).json()
|
||||
|
||||
dependency_description = {
|
||||
"summary": data["info"]["summary"],
|
||||
"version": data["info"]["version"],
|
||||
"license": data["info"]["license"]
|
||||
}
|
||||
|
||||
for url_data in data["urls"]:
|
||||
if url_data["packagetype"] == "sdist":
|
||||
sources_url = url_data["url"]
|
||||
dependency_description["sources_url"] = sources_url
|
||||
|
||||
if not self.options.skip_licenses_download:
|
||||
self._retrieve_pip_license(package, sources_url, dependency_description)
|
||||
|
||||
for source_url, check_source in [("source", False),
|
||||
("Source", False),
|
||||
("Source Code", False),
|
||||
("Repository", False),
|
||||
("Code", False),
|
||||
("homepage", True),
|
||||
("Homepage", True)]:
|
||||
try:
|
||||
url = data["info"]["project_urls"][source_url]
|
||||
if check_source and not self._is_repository_url(url):
|
||||
# That will not work for ALL open-source projects, but should already get a large majority of them
|
||||
self.output.warning(f"Source URL for {package} ({url}) doesn't seem to be a supported repository")
|
||||
continue
|
||||
dependency_description["sources_url"] = url
|
||||
break
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if dependency_description["license"] is not None and len(dependency_description["license"]) > 32:
|
||||
# Some packages have their full license in this field
|
||||
dependency_description["license_full"] = dependency_description["license"]
|
||||
dependency_description["license"] = data["info"]["name"]
|
||||
|
||||
dependencies[data["info"]["name"]] = dependency_description
|
||||
|
||||
@staticmethod
|
||||
def _get_license_from_repository(sources_url, version, license_file_name = None):
|
||||
if sources_url.startswith("https://github.com/Ultimaker/") and "private" in sources_url:
|
||||
return None
|
||||
|
||||
git_url = sources_url
|
||||
if git_url.endswith('/'):
|
||||
git_url = git_url[:-1]
|
||||
if not git_url.endswith(".git"):
|
||||
git_url = f"{git_url}.git"
|
||||
git_url = git_url.replace("/cgit/", "/")
|
||||
|
||||
tags = [f"v{version}", version]
|
||||
files = ["LICENSE", "LICENSE.txt", "LICENSE.md", "COPYRIGHT", "COPYING", "COPYING.LIB"] if license_file_name is None else [license_file_name]
|
||||
|
||||
with tempfile.TemporaryDirectory() as clone_dir:
|
||||
repo = Repo.clone_from(git_url, clone_dir, depth=1, no_checkout=True)
|
||||
|
||||
for tag in tags:
|
||||
try:
|
||||
repo.git.fetch('--depth', '1', 'origin', 'tag', tag)
|
||||
except GitCommandError:
|
||||
continue
|
||||
|
||||
repo.git.sparse_checkout('init', '--cone')
|
||||
for file_name in files:
|
||||
repo.git.sparse_checkout('add', file_name)
|
||||
|
||||
try:
|
||||
repo.git.checkout(tag)
|
||||
except GitCommandError:
|
||||
pass
|
||||
|
||||
for file_name in files:
|
||||
license_file = os.path.join(clone_dir, file_name)
|
||||
if os.path.exists(license_file):
|
||||
with open(license_file, 'r', encoding='utf8') as file:
|
||||
return file.read()
|
||||
|
||||
break
|
||||
|
||||
def _make_conan_dependency_description(self, dependency, dependencies):
|
||||
dependency_description = {
|
||||
"summary": dependency.description,
|
||||
"version": str(dependency.ref.version),
|
||||
"license": ', '.join(dependency.license) if (isinstance(dependency.license, list) or isinstance(dependency.license, tuple)) else dependency.license,
|
||||
}
|
||||
|
||||
for source_url, check_source in [(dependency.homepage, True),
|
||||
(dependency.url, True),
|
||||
(dependency.homepage, False),
|
||||
(dependency.url, False)]:
|
||||
if source_url is None:
|
||||
continue
|
||||
|
||||
is_repository_source = self._is_repository_url(source_url)
|
||||
if not check_source or is_repository_source:
|
||||
dependency_description["sources_url"] = source_url
|
||||
|
||||
if is_repository_source and not self.options.skip_licenses_download:
|
||||
self.output.info(f"Retrieving license for {dependency.ref.name}")
|
||||
dependency_description["license_full"] = self._get_license_from_repository(source_url, str(dependency.ref.version))
|
||||
|
||||
break
|
||||
|
||||
dependencies[dependency.ref.name] = dependency_description
|
||||
|
||||
def _make_extra_dependency_description(self, dependency_name, dependency_data, dependencies):
|
||||
sources_url = dependency_data["sources_url"]
|
||||
version = dependency_data["version"]
|
||||
home_url = dependency_data["home_url"] if "home_url" in dependency_data else sources_url
|
||||
|
||||
dependency_description = {
|
||||
"summary": dependency_data["summary"],
|
||||
"version": version,
|
||||
"license": dependency_data["license"],
|
||||
"sources_url": home_url,
|
||||
}
|
||||
|
||||
if not self.options.skip_licenses_download:
|
||||
self.output.info(f"Retrieving license for {dependency_name}")
|
||||
license_file = dependency_data["license_file"] if "license_file" in dependency_data else None
|
||||
dependency_description["license_full"] = self._get_license_from_repository(sources_url, version, license_file)
|
||||
|
||||
dependencies[dependency_name] = dependency_description
|
||||
|
||||
def _dependencies_description(self):
|
||||
dependencies = {}
|
||||
|
||||
for dependency in [self] + list(self.dependencies.values()):
|
||||
self._make_conan_dependency_description(dependency, dependencies)
|
||||
|
||||
if "extra_dependencies" in dependency.conan_data:
|
||||
for dependency_name, dependency_data in dependency.conan_data["extra_dependencies"].items():
|
||||
self._make_extra_dependency_description(dependency_name, dependency_data, dependencies)
|
||||
|
||||
pip_requirements_summary = os.path.abspath(Path(self.generators_folder, "pip_requirements_summary.yml") )
|
||||
with open(pip_requirements_summary, 'r') as file:
|
||||
for package_name, package_version in yaml.safe_load(file).items():
|
||||
self._make_pip_dependency_description(package_name, package_version, dependencies)
|
||||
|
||||
return dependencies
|
||||
|
||||
def _generate_cura_version(self, location):
|
||||
with open(os.path.join(self.recipe_folder, "CuraVersion.py.jinja"), "r") as f:
|
||||
cura_version_py = Template(f.read())
|
||||
|
|
@ -149,7 +336,7 @@ class CuraConan(ConanFile):
|
|||
|
||||
self.output.info(f"Write CuraVersion.py to {self.recipe_folder}")
|
||||
|
||||
with open(os.path.join(location, "CuraVersion.py"), "w") as f:
|
||||
with open(os.path.join(location, "CuraVersion.py"), "wb") as f:
|
||||
f.write(cura_version_py.render(
|
||||
cura_app_name = self.name,
|
||||
cura_app_display_name = self._app_name,
|
||||
|
|
@ -165,7 +352,8 @@ class CuraConan(ConanFile):
|
|||
cura_latest_url=self.conan_data["urls"][self._urls]["cura_latest_url"],
|
||||
conan_installs=self._conan_installs(),
|
||||
python_installs=self._python_installs(),
|
||||
))
|
||||
dependencies_description=self._dependencies_description(),
|
||||
).encode("utf-8"))
|
||||
|
||||
def _delete_unwanted_binaries(self, root):
|
||||
dynamic_binary_file_exts = [".so", ".dylib", ".dll", ".pyd", ".pyi"]
|
||||
|
|
@ -228,7 +416,8 @@ class CuraConan(ConanFile):
|
|||
pyinstaller_metadata = self.conan_data["pyinstaller"]
|
||||
datas = []
|
||||
for data in pyinstaller_metadata["datas"].values():
|
||||
if (not self.options.internal and data.get("internal", False)) or (not self.options.enterprise and data.get("enterprise_only", False)):
|
||||
if (not self.options.internal and data.get("internal", False)) or (
|
||||
not self.options.enterprise and data.get("enterprise_only", False)):
|
||||
continue
|
||||
|
||||
if "oses" in data and self.settings.os not in data["oses"]:
|
||||
|
|
@ -344,7 +533,7 @@ class CuraConan(ConanFile):
|
|||
copy(self, "*", os.path.join(self.recipe_folder, "plugins"), os.path.join(self.export_sources_folder, "plugins"))
|
||||
copy(self, "*", os.path.join(self.recipe_folder, "resources"), os.path.join(self.export_sources_folder, "resources"), excludes = "*.mo")
|
||||
copy(self, "*", os.path.join(self.recipe_folder, "tests"), os.path.join(self.export_sources_folder, "tests"))
|
||||
copy(self, "*", os.path.join(self.recipe_folder, "cura"), os.path.join(self.export_sources_folder, "cura"), excludes="CuraVersion.py")
|
||||
copy(self, "*", os.path.join(self.recipe_folder, "cura"), os.path.join(self.export_sources_folder, "cura"))
|
||||
copy(self, "*", os.path.join(self.recipe_folder, "packaging"), os.path.join(self.export_sources_folder, "packaging"))
|
||||
copy(self, "*", os.path.join(self.recipe_folder, ".run_templates"), os.path.join(self.export_sources_folder, ".run_templates"))
|
||||
copy(self, "cura_app.py", self.recipe_folder, self.export_sources_folder)
|
||||
|
|
@ -375,6 +564,30 @@ class CuraConan(ConanFile):
|
|||
self.cpp.package.bindirs = ["bin"]
|
||||
self.cpp.package.resdirs = ["resources", "plugins", "packaging"]
|
||||
|
||||
def _make_internal_distinct(self):
|
||||
test_colors_path = Path(self.source_folder, "resources", "themes", "daily_test_colors.json")
|
||||
if not test_colors_path.exists():
|
||||
print(f"Could not find '{str(test_colors_path)}'. Won't generate rotating colors for alpha builds.")
|
||||
return
|
||||
if "alpha" in self.version:
|
||||
with test_colors_path.open("r") as test_colors_file:
|
||||
test_colors = json.load(test_colors_file)
|
||||
biweekly_day = (datetime.now() - datetime(2025, 3, 14)).days % len(test_colors)
|
||||
for theme_dir in Path(self.source_folder, "resources", "themes").iterdir():
|
||||
if not theme_dir.is_dir():
|
||||
continue
|
||||
theme_path = Path(theme_dir, "theme.json")
|
||||
if not theme_path.exists():
|
||||
print(f"('Colorize-by-day' alpha builds): Skipping {str(theme_path)}, could not find file.")
|
||||
continue
|
||||
with theme_path.open("r") as theme_file:
|
||||
theme = json.load(theme_file)
|
||||
if theme["colors"]:
|
||||
theme["colors"]["main_window_header_background"] = test_colors[biweekly_day]
|
||||
with theme_path.open("w") as theme_file:
|
||||
json.dump(theme, theme_file)
|
||||
test_colors_path.unlink()
|
||||
|
||||
def generate(self):
|
||||
copy(self, "cura_app.py", self.source_folder, str(self._script_dir))
|
||||
|
||||
|
|
@ -389,8 +602,13 @@ class CuraConan(ConanFile):
|
|||
if self.options.enterprise:
|
||||
rmdir(self, str(Path(self.source_folder, "plugins", "NativeCADplugin")))
|
||||
native_cad_plugin = self.dependencies["native_cad_plugin"].cpp_info
|
||||
copy(self, "*", native_cad_plugin.resdirs[0], str(Path(self.source_folder, "plugins", "NativeCADplugin")), keep_path = True)
|
||||
copy(self, "bundled_*.json", native_cad_plugin.resdirs[1], str(Path(self.source_folder, "resources", "bundled_packages")), keep_path = False)
|
||||
copy(self, "*", native_cad_plugin.resdirs[0], str(Path(self.source_folder, "plugins", "NativeCADplugin")),
|
||||
keep_path = True)
|
||||
copy(self, "bundled_*.json", native_cad_plugin.resdirs[1],
|
||||
str(Path(self.source_folder, "resources", "bundled_packages")), keep_path = False)
|
||||
|
||||
# Make internal versions built on different days distinct, so people don't get confused while testing.
|
||||
self._make_internal_distinct()
|
||||
|
||||
# Copy resources of cura_binary_data
|
||||
cura_binary_data = self.dependencies["cura_binary_data"].cpp_info
|
||||
|
|
@ -427,10 +645,6 @@ class CuraConan(ConanFile):
|
|||
pot.generate()
|
||||
|
||||
def build(self):
|
||||
if self.settings.os == "Windows" and not self.conf.get("tools.microsoft.bash:path", check_type=str):
|
||||
self.output.warning("Skipping generation of binary translation files because Bash could not be found and is required")
|
||||
return
|
||||
|
||||
for po_file in Path(self.source_folder, "resources", "i18n").glob("**/*.po"):
|
||||
mo_file = Path(self.build_folder, po_file.with_suffix('.mo').relative_to(self.source_folder))
|
||||
mo_file = mo_file.parent.joinpath("LC_MESSAGES", mo_file.name)
|
||||
|
|
@ -441,7 +655,8 @@ class CuraConan(ConanFile):
|
|||
''' Note: this deploy step is actually used to prepare for building a Cura distribution with pyinstaller, which is not
|
||||
the original purpose in the Conan philosophy '''
|
||||
|
||||
copy(self, "*", os.path.join(self.package_folder, self.cpp.package.resdirs[2]), os.path.join(self.deploy_folder, "packaging"), keep_path = True)
|
||||
copy(self, "*", os.path.join(self.package_folder, self.cpp.package.resdirs[2]),
|
||||
os.path.join(self.deploy_folder, "packaging"), keep_path=True)
|
||||
|
||||
# Copy resources of Cura (keep folder structure) needed by pyinstaller to determine the module structure
|
||||
copy(self, "*", os.path.join(self.package_folder, self.cpp_info.bindirs[0]), str(self._base_dir), keep_path = False)
|
||||
|
|
@ -461,8 +676,6 @@ class CuraConan(ConanFile):
|
|||
copy(self, "*", uranium.resdirs[1], str(self._share_dir.joinpath("uranium", "plugins")), keep_path = True)
|
||||
copy(self, "*", uranium.libdirs[0], str(self._site_packages.joinpath("UM")), keep_path = True)
|
||||
|
||||
self._generate_cura_version(os.path.join(self._site_packages, "cura"))
|
||||
|
||||
self._delete_unwanted_binaries(self._site_packages)
|
||||
self._delete_unwanted_binaries(self.package_folder)
|
||||
self._delete_unwanted_binaries(self._base_dir)
|
||||
|
|
@ -483,6 +696,7 @@ class CuraConan(ConanFile):
|
|||
copy(self, "*", src = os.path.join(self.source_folder, "plugins"), dst = os.path.join(self.package_folder, self.cpp.package.resdirs[1]))
|
||||
copy(self, "*", src = os.path.join(self.source_folder, "packaging"), dst = os.path.join(self.package_folder, self.cpp.package.resdirs[2]))
|
||||
copy(self, "pip_requirements_*.txt", src = self.generators_folder, dst = os.path.join(self.package_folder, self.cpp.package.resdirs[-1]))
|
||||
copy(self, "pip_requirements_summary.yml", src = self.generators_folder, dst = os.path.join(self.package_folder, self.cpp.package.resdirs[-1]))
|
||||
|
||||
# Remove the fdm_materials from the package
|
||||
rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[0], "materials"))
|
||||
|
|
@ -498,4 +712,4 @@ class CuraConan(ConanFile):
|
|||
self.runenv_info.append_path("PYTHONPATH", os.path.join(self.package_folder, "plugins"))
|
||||
|
||||
def package_id(self):
|
||||
self.info.options.rm_safe("enable_i18n")
|
||||
self.info.options.rm_safe("i18n_extract")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Tuple, Optional, TYPE_CHECKING, Dict, Any
|
||||
|
||||
|
|
@ -9,14 +9,10 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Backups:
|
||||
"""The back-ups API provides a version-proof bridge between Cura's
|
||||
|
||||
BackupManager and plug-ins that hook into it.
|
||||
"""The back-ups API provides a version-proof bridge between Cura's BackupManager and plug-ins that hook into it.
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cura.API import CuraAPI
|
||||
api = CuraAPI()
|
||||
api.backups.createBackup()
|
||||
|
|
@ -26,19 +22,22 @@ class Backups:
|
|||
def __init__(self, application: "CuraApplication") -> None:
|
||||
self.manager = BackupsManager(application)
|
||||
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, Any]]]:
|
||||
def createBackup(self, available_remote_plugins: frozenset[str] = frozenset()) -> Tuple[Optional[bytes], Optional[Dict[str, Any]]]:
|
||||
"""Create a new back-up using the BackupsManager.
|
||||
|
||||
:return: Tuple containing a ZIP file with the back-up data and a dict with metadata about the back-up.
|
||||
"""
|
||||
|
||||
return self.manager.createBackup()
|
||||
return self.manager.createBackup(available_remote_plugins)
|
||||
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, Any]) -> None:
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, Any], auto_close: bool = True) -> None:
|
||||
"""Restore a back-up using the BackupsManager.
|
||||
|
||||
:param zip_file: A ZIP file containing the actual back-up data.
|
||||
:param meta_data: Some metadata needed for restoring a back-up, like the Cura version number.
|
||||
"""
|
||||
|
||||
return self.manager.restoreBackup(zip_file, meta_data)
|
||||
return self.manager.restoreBackup(zip_file, meta_data, auto_close=auto_close)
|
||||
|
||||
def shouldReinstallDownloadablePlugins(self) -> bool:
|
||||
return self.manager.shouldReinstallDownloadablePlugins()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from dataclasses import asdict
|
||||
|
||||
from typing import cast, Dict, TYPE_CHECKING
|
||||
from typing import cast, Dict, TYPE_CHECKING, Any
|
||||
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Settings.SettingFunction import SettingFunction
|
||||
|
|
@ -54,6 +54,15 @@ class Settings:
|
|||
|
||||
return self.application.getSidebarCustomMenuItems()
|
||||
|
||||
def getAllGlobalSettings(self) -> Dict[str, Any]:
|
||||
global_stack = cast(GlobalStack, self.application.getGlobalContainerStack())
|
||||
|
||||
all_settings = {}
|
||||
for setting in global_stack.getAllKeys():
|
||||
all_settings[setting] = self._retrieveValue(global_stack, setting)
|
||||
|
||||
return all_settings
|
||||
|
||||
def getSliceMetadata(self) -> Dict[str, Dict[str, Dict[str, str]]]:
|
||||
"""Get all changed settings and all settings. For each extruder and the global stack"""
|
||||
print_information = self.application.getPrintInformation()
|
||||
|
|
@ -71,24 +80,16 @@ class Settings:
|
|||
"quality": asdict(machine_manager.activeQualityDisplayNameMap()),
|
||||
}
|
||||
|
||||
def _retrieveValue(container: InstanceContainer, setting_: str):
|
||||
value_ = container.getProperty(setting_, "value")
|
||||
for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit.
|
||||
if not isinstance(value_, SettingFunction):
|
||||
return value_ # Success!
|
||||
value_ = value_(container)
|
||||
return 0 # Fallback value after breaking possibly endless loop.
|
||||
|
||||
global_stack = cast(GlobalStack, self.application.getGlobalContainerStack())
|
||||
|
||||
# Add global user or quality changes
|
||||
global_flattened_changes = InstanceContainer.createMergedInstanceContainer(global_stack.userChanges, global_stack.qualityChanges)
|
||||
for setting in global_flattened_changes.getAllKeys():
|
||||
settings["global"]["changes"][setting] = _retrieveValue(global_flattened_changes, setting)
|
||||
settings["global"]["changes"][setting] = self._retrieveValue(global_flattened_changes, setting)
|
||||
|
||||
# Get global all settings values without user or quality changes
|
||||
for setting in global_stack.getAllKeys():
|
||||
settings["global"]["all_settings"][setting] = _retrieveValue(global_stack, setting)
|
||||
settings["global"]["all_settings"][setting] = self._retrieveValue(global_stack, setting)
|
||||
|
||||
for i, extruder in enumerate(global_stack.extruderList):
|
||||
# Add extruder fields to settings dictionary
|
||||
|
|
@ -100,10 +101,19 @@ class Settings:
|
|||
# Add extruder user or quality changes
|
||||
extruder_flattened_changes = InstanceContainer.createMergedInstanceContainer(extruder.userChanges, extruder.qualityChanges)
|
||||
for setting in extruder_flattened_changes.getAllKeys():
|
||||
settings[f"extruder_{i}"]["changes"][setting] = _retrieveValue(extruder_flattened_changes, setting)
|
||||
settings[f"extruder_{i}"]["changes"][setting] = self._retrieveValue(extruder_flattened_changes, setting)
|
||||
|
||||
# Get extruder all settings values without user or quality changes
|
||||
for setting in extruder.getAllKeys():
|
||||
settings[f"extruder_{i}"]["all_settings"][setting] = _retrieveValue(extruder, setting)
|
||||
settings[f"extruder_{i}"]["all_settings"][setting] = self._retrieveValue(extruder, setting)
|
||||
|
||||
return settings
|
||||
|
||||
@staticmethod
|
||||
def _retrieveValue(container: InstanceContainer, setting_: str):
|
||||
value_ = container.getProperty(setting_, "value")
|
||||
for _ in range(0, 1024): # Prevent possibly endless loop by not using a limit.
|
||||
if not isinstance(value_, SettingFunction):
|
||||
return value_ # Success!
|
||||
value_ = value_(container)
|
||||
return 0 # Fallback value after breaking possibly endless loop.
|
||||
|
|
@ -14,7 +14,7 @@ DEFAULT_CURA_LATEST_URL = "https://software.ultimaker.com/latest.json"
|
|||
# Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for
|
||||
# example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the
|
||||
# CuraVersion.py.in template.
|
||||
CuraSDKVersion = "8.9.0"
|
||||
CuraSDKVersion = "8.10.0"
|
||||
|
||||
try:
|
||||
from cura.CuraVersion import CuraLatestURL
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class ArrangeObjectsJob(Job):
|
|||
|
||||
found_solution_for_all = False
|
||||
try:
|
||||
found_solution_for_all = arranger.arrange()
|
||||
found_solution_for_all = arranger.arrange(only_if_full_success = True)
|
||||
except: # If the thread crashes, the message should still close
|
||||
Logger.logException("e",
|
||||
"Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.")
|
||||
|
|
|
|||
|
|
@ -16,12 +16,16 @@ class Arranger:
|
|||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def arrange(self, add_new_nodes_in_scene: bool = False) -> bool:
|
||||
def arrange(self, add_new_nodes_in_scene: bool = False, only_if_full_success: bool = False) -> bool:
|
||||
"""
|
||||
Find placement for a set of scene nodes, and move them by using a single grouped operation.
|
||||
:param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations
|
||||
:return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects
|
||||
"""
|
||||
grouped_operation, not_fit_count = self.createGroupOperationForArrange(add_new_nodes_in_scene)
|
||||
grouped_operation.push()
|
||||
return not_fit_count == 0
|
||||
full_success = not_fit_count == 0
|
||||
|
||||
if full_success or not only_if_full_success:
|
||||
grouped_operation.push()
|
||||
|
||||
return full_success
|
||||
|
|
|
|||
|
|
@ -54,22 +54,6 @@ class Nest2DArrange(Arranger):
|
|||
machine_depth = self._build_volume.getDepth() - (edge_disallowed_size * 2)
|
||||
build_plate_bounding_box = Box(int(machine_width * self._factor), int(machine_depth * self._factor))
|
||||
|
||||
if self._fixed_nodes is None:
|
||||
self._fixed_nodes = []
|
||||
|
||||
# Add all the items we want to arrange
|
||||
node_items = []
|
||||
for node in self._nodes_to_arrange:
|
||||
hull_polygon = node.callDecoration("getConvexHull")
|
||||
if not hull_polygon or hull_polygon.getPoints is None:
|
||||
Logger.log("w", "Object {} cannot be arranged because it has no convex hull.".format(node.getName()))
|
||||
continue
|
||||
converted_points = []
|
||||
for point in hull_polygon.getPoints():
|
||||
converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor)))
|
||||
item = Item(converted_points)
|
||||
node_items.append(item)
|
||||
|
||||
# Use a tiny margin for the build_plate_polygon (the nesting doesn't like overlapping disallowed areas)
|
||||
half_machine_width = 0.5 * machine_width - 1
|
||||
half_machine_depth = 0.5 * machine_depth - 1
|
||||
|
|
@ -80,40 +64,66 @@ class Nest2DArrange(Arranger):
|
|||
[half_machine_width, half_machine_depth]
|
||||
], numpy.float32))
|
||||
|
||||
disallowed_areas = self._build_volume.getDisallowedAreas()
|
||||
for area in disallowed_areas:
|
||||
converted_points = []
|
||||
def _convert_points(points):
|
||||
if points is not None and len(points) > 2: # numpy array has to be explicitly checked against None
|
||||
converted_points = []
|
||||
for point in points:
|
||||
converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor)))
|
||||
return [converted_points]
|
||||
else:
|
||||
return []
|
||||
|
||||
polygons_nodes_to_arrange = []
|
||||
for node in self._nodes_to_arrange:
|
||||
hull_polygon = node.callDecoration("getConvexHull")
|
||||
if not hull_polygon or hull_polygon.getPoints is None:
|
||||
Logger.log("w", "Object {} cannot be arranged because it has no convex hull.".format(node.getName()))
|
||||
continue
|
||||
|
||||
polygons_nodes_to_arrange += _convert_points(hull_polygon.getPoints())
|
||||
|
||||
polygons_disallowed_areas = []
|
||||
for area in self._build_volume.getDisallowedAreas():
|
||||
# Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise)
|
||||
clipped_area = area.intersectionConvexHulls(build_plate_polygon)
|
||||
|
||||
if clipped_area.getPoints() is not None and len(
|
||||
clipped_area.getPoints()) > 2: # numpy array has to be explicitly checked against None
|
||||
for point in clipped_area.getPoints():
|
||||
converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor)))
|
||||
polygons_disallowed_areas += _convert_points(clipped_area.getPoints())
|
||||
|
||||
disallowed_area = Item(converted_points)
|
||||
polygons_fixed_nodes = []
|
||||
if self._fixed_nodes is None:
|
||||
self._fixed_nodes = []
|
||||
for node in self._fixed_nodes:
|
||||
hull_polygon = node.callDecoration("getConvexHull")
|
||||
|
||||
if hull_polygon is not None:
|
||||
polygons_fixed_nodes += _convert_points(hull_polygon.getPoints())
|
||||
|
||||
strategies = [NfpConfig.Alignment.CENTER,
|
||||
NfpConfig.Alignment.BOTTOM_LEFT,
|
||||
NfpConfig.Alignment.BOTTOM_RIGHT,
|
||||
NfpConfig.Alignment.TOP_LEFT,
|
||||
NfpConfig.Alignment.TOP_RIGHT]
|
||||
found_solution_for_all = False
|
||||
while not found_solution_for_all and len(strategies) > 0:
|
||||
|
||||
# Add all the items we want to arrange
|
||||
node_items = []
|
||||
for polygon in polygons_nodes_to_arrange:
|
||||
node_items.append(Item(polygon))
|
||||
|
||||
for polygon in polygons_disallowed_areas:
|
||||
disallowed_area = Item(polygon)
|
||||
disallowed_area.markAsDisallowedAreaInBin(0)
|
||||
node_items.append(disallowed_area)
|
||||
|
||||
for node in self._fixed_nodes:
|
||||
converted_points = []
|
||||
hull_polygon = node.callDecoration("getConvexHull")
|
||||
|
||||
if hull_polygon is not None and hull_polygon.getPoints() is not None and len(
|
||||
hull_polygon.getPoints()) > 2: # numpy array has to be explicitly checked against None
|
||||
for point in hull_polygon.getPoints():
|
||||
converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor)))
|
||||
item = Item(converted_points)
|
||||
for polygon in polygons_fixed_nodes:
|
||||
item = Item(polygon)
|
||||
item.markAsFixedInBin(0)
|
||||
node_items.append(item)
|
||||
|
||||
strategies = [NfpConfig.Alignment.CENTER] * 3 + [NfpConfig.Alignment.BOTTOM_LEFT] * 3
|
||||
found_solution_for_all = False
|
||||
while not found_solution_for_all and len(strategies) > 0:
|
||||
config = NfpConfig()
|
||||
config.accuracy = 1.0
|
||||
config.alignment = NfpConfig.Alignment.CENTER
|
||||
config.alignment = NfpConfig.Alignment.DONT_ALIGN
|
||||
config.starting_point = strategies[0]
|
||||
strategies = strategies[1:]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import tempfile
|
||||
|
||||
import json
|
||||
|
||||
import io
|
||||
import os
|
||||
|
|
@ -7,12 +10,13 @@ import re
|
|||
import shutil
|
||||
from copy import deepcopy
|
||||
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
|
||||
from typing import Dict, Optional, TYPE_CHECKING, List
|
||||
from typing import Callable, Dict, Optional, TYPE_CHECKING, List
|
||||
|
||||
from UM import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
from UM.Message import Message
|
||||
from UM.Platform import Platform
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Resources import Resources
|
||||
from UM.Version import Version
|
||||
|
||||
|
|
@ -30,6 +34,7 @@ class Backup:
|
|||
"""These files should be ignored when making a backup."""
|
||||
|
||||
IGNORED_FOLDERS = [] # type: List[str]
|
||||
"""These folders should be ignored when making a backup."""
|
||||
|
||||
SECRETS_SETTINGS = ["general/ultimaker_auth_data"]
|
||||
"""Secret preferences that need to obfuscated when making a backup of Cura"""
|
||||
|
|
@ -42,7 +47,7 @@ class Backup:
|
|||
self.zip_file = zip_file # type: Optional[bytes]
|
||||
self.meta_data = meta_data # type: Optional[Dict[str, str]]
|
||||
|
||||
def makeFromCurrent(self) -> None:
|
||||
def makeFromCurrent(self, available_remote_plugins: frozenset[str] = frozenset()) -> None:
|
||||
"""Create a back-up from the current user config folder."""
|
||||
|
||||
cura_release = self._application.getVersion()
|
||||
|
|
@ -68,7 +73,7 @@ class Backup:
|
|||
|
||||
# Create an empty buffer and write the archive to it.
|
||||
buffer = io.BytesIO()
|
||||
archive = self._makeArchive(buffer, version_data_dir)
|
||||
archive = self._makeArchive(buffer, version_data_dir, available_remote_plugins)
|
||||
if archive is None:
|
||||
return
|
||||
files = archive.namelist()
|
||||
|
|
@ -77,9 +82,7 @@ class Backup:
|
|||
machine_count = max(len([s for s in files if "machine_instances/" in s]) - 1, 0) # If people delete their profiles but not their preferences, it can still make a backup, and report -1 profiles. Server crashes on this.
|
||||
material_count = max(len([s for s in files if "materials/" in s]) - 1, 0)
|
||||
profile_count = max(len([s for s in files if "quality_changes/" in s]) - 1, 0)
|
||||
# We don't store plugins anymore, since if you can make backups, you have an account (and the plugins are
|
||||
# on the marketplace anyway)
|
||||
plugin_count = 0
|
||||
plugin_count = len([s for s in files if "plugin.json" in s])
|
||||
# Store the archive and metadata so the BackupManager can fetch them when needed.
|
||||
self.zip_file = buffer.getvalue()
|
||||
self.meta_data = {
|
||||
|
|
@ -92,22 +95,72 @@ class Backup:
|
|||
# Restore the obfuscated settings
|
||||
self._illuminate(**secrets)
|
||||
|
||||
def _makeArchive(self, buffer: "io.BytesIO", root_path: str) -> Optional[ZipFile]:
|
||||
def _fillToInstallsJson(self, file_path: str, reinstall_on_restore: frozenset[str], add_to_archive: Callable[[str, str], None]) -> Optional[str]:
|
||||
""" Moves all plugin-data (in a config-file) for plugins that could be (re)installed from the Marketplace from
|
||||
'installed' to 'to_installs' before adding that file to the archive.
|
||||
|
||||
Note that the 'filename'-entry in the package-data (of the plugins) might not be valid anymore on restore.
|
||||
We'll replace it on restore instead, as that's the time when the new package is downloaded.
|
||||
|
||||
:param file_path: Absolute path to the packages-file.
|
||||
:param reinstall_on_restore: A set of plugins that _can_ be reinstalled from the Marketplace.
|
||||
:param add_to_archive: A function/lambda that takes a filename and adds it to the archive (as the 2nd name).
|
||||
"""
|
||||
with open(file_path, "r") as file:
|
||||
data = json.load(file)
|
||||
reinstall, keep_in = {}, {}
|
||||
for install_id, install_info in data["installed"].items():
|
||||
(reinstall if install_id in reinstall_on_restore else keep_in)[install_id] = install_info
|
||||
data["installed"] = keep_in
|
||||
data["to_install"].update(reinstall)
|
||||
if data is not None:
|
||||
tmpfile = tempfile.NamedTemporaryFile(delete_on_close=False)
|
||||
with open(tmpfile.name, "w") as outfile:
|
||||
json.dump(data, outfile)
|
||||
add_to_archive(tmpfile.name, file_path)
|
||||
return tmpfile.name
|
||||
return None
|
||||
|
||||
def _findRedownloadablePlugins(self, available_remote_plugins: frozenset) -> (frozenset[str], frozenset[str]):
|
||||
""" Find all plugins that should be able to be reinstalled from the Marketplace.
|
||||
|
||||
:param plugins_path: Path to all plugins in the user-space.
|
||||
:return: Tuple of a set of plugin-ids and a set of plugin-paths.
|
||||
"""
|
||||
plugin_reg = PluginRegistry.getInstance()
|
||||
id = "id"
|
||||
plugins = [v for v in plugin_reg.getAllMetaData()
|
||||
if v[id] in available_remote_plugins and not plugin_reg.isBundledPlugin(v[id])]
|
||||
return frozenset([v[id] for v in plugins]), frozenset([v["location"] for v in plugins])
|
||||
|
||||
def _makeArchive(self, buffer: "io.BytesIO", root_path: str, available_remote_plugins: frozenset) -> Optional[ZipFile]:
|
||||
"""Make a full archive from the given root path with the given name.
|
||||
|
||||
:param root_path: The root directory to archive recursively.
|
||||
:return: The archive as bytes.
|
||||
"""
|
||||
ignore_string = re.compile("|".join(self.IGNORED_FILES + self.IGNORED_FOLDERS))
|
||||
reinstall_instead_ids, reinstall_instead_paths = self._findRedownloadablePlugins(available_remote_plugins)
|
||||
tmpfiles = []
|
||||
try:
|
||||
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
|
||||
for root, folders, files in os.walk(root_path):
|
||||
add_path_to_archive = lambda path, alt_path: archive.write(path, alt_path[len(root_path) + len(os.sep):])
|
||||
for root, folders, files in os.walk(root_path, topdown=True):
|
||||
for item_name in folders + files:
|
||||
absolute_path = os.path.join(root, item_name)
|
||||
if ignore_string.search(absolute_path):
|
||||
if ignore_string.search(absolute_path) or any([absolute_path.startswith(x) for x in reinstall_instead_paths]):
|
||||
continue
|
||||
archive.write(absolute_path, absolute_path[len(root_path) + len(os.sep):])
|
||||
if item_name == "packages.json":
|
||||
tmpfiles.append(
|
||||
self._fillToInstallsJson(absolute_path, reinstall_instead_ids, add_path_to_archive))
|
||||
else:
|
||||
add_path_to_archive(absolute_path, absolute_path)
|
||||
archive.close()
|
||||
for tmpfile_path in tmpfiles:
|
||||
try:
|
||||
os.remove(tmpfile_path)
|
||||
except IOError as ex:
|
||||
Logger.warning(f"Couldn't remove temporary file '{tmpfile_path}' because '{ex}'.")
|
||||
return archive
|
||||
except (IOError, OSError, BadZipfile) as error:
|
||||
Logger.log("e", "Could not create archive from user data directory: %s", error)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Dict, Optional, Tuple, TYPE_CHECKING
|
||||
|
|
@ -22,7 +22,10 @@ class BackupsManager:
|
|||
def __init__(self, application: "CuraApplication") -> None:
|
||||
self._application = application
|
||||
|
||||
def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, str]]]:
|
||||
def shouldReinstallDownloadablePlugins(self) -> bool:
|
||||
return True
|
||||
|
||||
def createBackup(self, available_remote_plugins: frozenset[str] = frozenset()) -> Tuple[Optional[bytes], Optional[Dict[str, str]]]:
|
||||
"""
|
||||
Get a back-up of the current configuration.
|
||||
|
||||
|
|
@ -31,17 +34,18 @@ class BackupsManager:
|
|||
|
||||
self._disableAutoSave()
|
||||
backup = Backup(self._application)
|
||||
backup.makeFromCurrent()
|
||||
backup.makeFromCurrent(available_remote_plugins if self.shouldReinstallDownloadablePlugins() else frozenset())
|
||||
self._enableAutoSave()
|
||||
# We don't return a Backup here because we want plugins only to interact with our API and not full objects.
|
||||
return backup.zip_file, backup.meta_data
|
||||
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str]) -> None:
|
||||
def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str], auto_close: bool = True) -> None:
|
||||
"""
|
||||
Restore a back-up from a given ZipFile.
|
||||
|
||||
:param zip_file: A bytes object containing the actual back-up.
|
||||
:param meta_data: A dict containing some metadata that is needed to restore the back-up correctly.
|
||||
:param auto_close: Normally, Cura will need to close immediately after restoring the back-up.
|
||||
"""
|
||||
|
||||
if not meta_data.get("cura_release", None):
|
||||
|
|
@ -54,7 +58,7 @@ class BackupsManager:
|
|||
backup = Backup(self._application, zip_file = zip_file, meta_data = meta_data)
|
||||
restored = backup.restore()
|
||||
|
||||
if restored:
|
||||
if restored and auto_close:
|
||||
# At this point, Cura will need to restart for the changes to take effect.
|
||||
# We don't want to store the data at this point as that would override the just-restored backup.
|
||||
self._application.windowClosed(save_data = False)
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ from cura.UI.MachineActionManager import MachineActionManager
|
|||
from cura.UI.AddPrinterPagesModel import AddPrinterPagesModel
|
||||
from cura.UI.MachineSettingsManager import MachineSettingsManager
|
||||
from cura.UI.ObjectsModel import ObjectsModel
|
||||
from cura.UI.OpenSourceDependenciesModel import OpenSourceDependenciesModel
|
||||
from cura.UI.RecommendedMode import RecommendedMode
|
||||
from cura.UI.TextManager import TextManager
|
||||
from cura.UI.WelcomePagesModel import WelcomePagesModel
|
||||
|
|
@ -139,7 +140,7 @@ class CuraApplication(QtApplication):
|
|||
# SettingVersion represents the set of settings available in the machine/extruder definitions.
|
||||
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
|
||||
# changes of the settings.
|
||||
SettingVersion = 24
|
||||
SettingVersion = 25
|
||||
|
||||
Created = False
|
||||
|
||||
|
|
@ -187,6 +188,7 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._single_instance = None
|
||||
self._open_project_mode: Optional[str] = None
|
||||
self._read_operation_is_project_file: Optional[bool] = None
|
||||
|
||||
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
|
||||
|
||||
|
|
@ -1308,6 +1310,7 @@ class CuraApplication(QtApplication):
|
|||
qmlRegisterType(AddPrinterPagesModel, "Cura", 1, 0, "AddPrinterPagesModel")
|
||||
qmlRegisterType(TextManager, "Cura", 1, 0, "TextManager")
|
||||
qmlRegisterType(RecommendedMode, "Cura", 1, 0, "RecommendedMode")
|
||||
qmlRegisterType(OpenSourceDependenciesModel, "Cura", 1, 0, "OpenSourceDependenciesModel")
|
||||
|
||||
self.processEvents()
|
||||
qmlRegisterType(NetworkMJPGImage, "Cura", 1, 0, "NetworkMJPGImage")
|
||||
|
|
@ -2013,18 +2016,18 @@ class CuraApplication(QtApplication):
|
|||
self.deleteAll()
|
||||
break
|
||||
|
||||
is_project_file = self.checkIsValidProjectFile(file)
|
||||
self._read_operation_is_project_file = self.checkIsValidProjectFile(file)
|
||||
|
||||
if self._open_project_mode is None:
|
||||
self._open_project_mode = self.getPreferences().getValue("cura/choice_on_open_project")
|
||||
|
||||
if is_project_file and self._open_project_mode == "open_as_project":
|
||||
if self._read_operation_is_project_file and self._open_project_mode == "open_as_project":
|
||||
# open as project immediately without presenting a dialog
|
||||
workspace_handler = self.getWorkspaceFileHandler()
|
||||
workspace_handler.readLocalFile(file, add_to_recent_files_hint = add_to_recent_files)
|
||||
return
|
||||
|
||||
if is_project_file and self._open_project_mode == "always_ask":
|
||||
if self._read_operation_is_project_file and self._open_project_mode == "always_ask":
|
||||
# present a dialog asking to open as project or import models
|
||||
self.callLater(self.openProjectFile.emit, file, add_to_recent_files)
|
||||
return
|
||||
|
|
@ -2162,7 +2165,7 @@ class CuraApplication(QtApplication):
|
|||
nodes_to_arrange.append(node)
|
||||
# If the file is a project,and models are to be loaded from a that project,
|
||||
# models inside file should be arranged in buildplate.
|
||||
elif self._open_project_mode == "open_as_model":
|
||||
elif self._read_operation_is_project_file and self._open_project_mode == "open_as_model":
|
||||
nodes_to_arrange.append(node)
|
||||
|
||||
# This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy
|
||||
|
|
|
|||
|
|
@ -72,6 +72,13 @@ class ActiveIntentQualitiesModel(ListModel):
|
|||
new_items.append(intent)
|
||||
added_quality_type_set.add(intent["quality_type"])
|
||||
|
||||
# If there aren't any possibilities when the Intent is kept the same, attempt to set it 'back' to default.
|
||||
current_quality_type = global_stack.quality.getMetaDataEntry("quality_type")
|
||||
if len(new_items) == 0 and self._intent_category != "default" and current_quality_type != "not_supported":
|
||||
IntentManager.getInstance().selectIntent("default", current_quality_type)
|
||||
self._update()
|
||||
return
|
||||
|
||||
new_items = sorted(new_items, key=lambda x: x["layer_height"])
|
||||
self.setItems(new_items)
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ class MultiplyObjectsJob(Job):
|
|||
arranger = Nest2DArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, factor=1000)
|
||||
|
||||
group_operation, not_fit_count = arranger.createGroupOperationForArrange(add_new_nodes_in_scene=True)
|
||||
found_solution_for_all = not_fit_count == 0
|
||||
|
||||
if nodes_to_add_without_arrange:
|
||||
for nested_node in nodes_to_add_without_arrange:
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ class AuthorizationRequestHandler(BaseHTTPRequestHandler):
|
|||
def _sendHeaders(self, status: "ResponseStatus", content_type: str, redirect_uri: str = None) -> None:
|
||||
self.send_response(status.code, status.message)
|
||||
self.send_header("Content-type", content_type)
|
||||
self.send_header("Strict-Transport-Security", "max-age=900")
|
||||
if redirect_uri:
|
||||
self.send_header("Location", redirect_uri)
|
||||
self.end_headers()
|
||||
|
|
|
|||
|
|
@ -402,6 +402,9 @@ class CuraContainerStack(ContainerStack):
|
|||
|
||||
return super().getProperty(key, property_name, context)
|
||||
|
||||
def getValue(self, key: str, context = None) -> Any:
|
||||
return self.getProperty(key, "value", context)
|
||||
|
||||
|
||||
class _ContainerIndexes:
|
||||
"""Private helper class to keep track of container positions and their types."""
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from UM.Scene.Selection import Selection
|
|||
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
|
||||
from cura.Machines.ContainerTree import ContainerTree
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
|
||||
from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING, Union
|
||||
|
||||
|
|
@ -304,6 +305,11 @@ class ExtruderManager(QObject):
|
|||
Logger.log("e", "Unable to find one or more of the extruders in %s", used_extruder_stack_ids)
|
||||
return []
|
||||
|
||||
def getFirstUsedExtruderStack(self)-> ExtruderStack:
|
||||
used_extruders = self.getUsedExtruderStacks()
|
||||
sorted_extruders = sorted(used_extruders, key=lambda extruder: extruder.getValue("extruder_nr"))
|
||||
return sorted_extruders[0]
|
||||
|
||||
def getInitialExtruderNr(self) -> int:
|
||||
"""Get the extruder that the print will start with.
|
||||
|
||||
|
|
@ -320,8 +326,7 @@ class ExtruderManager(QObject):
|
|||
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
|
||||
return self.getFirstUsedExtruderStack().getValue("extruder_nr")
|
||||
else:
|
||||
return skirt_brim_extruder_nr
|
||||
if adhesion_type == "raft":
|
||||
|
|
@ -332,7 +337,7 @@ class ExtruderManager(QObject):
|
|||
return global_stack.getProperty("support_infill_extruder_nr", "value")
|
||||
|
||||
# REALLY no adhesion? Use the first used extruder.
|
||||
return self.getUsedExtruderStacks()[0].getProperty("extruder_nr", "value")
|
||||
return self.getFirstUsedExtruderStack().getValue("extruder_nr")
|
||||
|
||||
def removeMachineExtruders(self, machine_id: str) -> None:
|
||||
"""Removes the container stack and user profile for the extruders for a specific machine.
|
||||
|
|
|
|||
|
|
@ -398,7 +398,8 @@ class MachineManager(QObject):
|
|||
self.setVariantByName(extruder.getMetaDataEntry("position"), machine_node.preferred_variant_name)
|
||||
variant_node = machine_node.variants.get(machine_node.preferred_variant_name)
|
||||
|
||||
material_node = variant_node.materials.get(extruder.material.getMetaDataEntry("base_file")) if variant_node else None
|
||||
material_node = variant_node.materials.get(
|
||||
extruder.material.getMetaDataEntry("base_file")) if variant_node else None
|
||||
if material_node is None:
|
||||
Logger.log("w", "An extruder has an unknown material, switching it to the preferred material")
|
||||
if not self.setMaterialById(extruder.getMetaDataEntry("position"), machine_node.preferred_material):
|
||||
|
|
|
|||
23
cura/UI/OpenSourceDependenciesModel.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import List
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtProperty
|
||||
|
||||
from cura import CuraVersion
|
||||
from .OpenSourceDependency import OpenSourceDependency
|
||||
|
||||
|
||||
class OpenSourceDependenciesModel(QObject):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._dependencies = []
|
||||
|
||||
for name, data in CuraVersion.DependenciesDescriptions.items():
|
||||
self._dependencies.append(OpenSourceDependency(name, data))
|
||||
|
||||
@pyqtProperty(list, constant=True)
|
||||
def dependencies(self) -> List[OpenSourceDependency]:
|
||||
return self._dependencies
|
||||
40
cura/UI/OpenSourceDependency.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtProperty, pyqtEnum
|
||||
|
||||
|
||||
class OpenSourceDependency(QObject):
|
||||
|
||||
def __init__(self, name, data):
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self._version = data['version'] if data['version'] is not None else ''
|
||||
self._summary = data['summary'] if data['summary'] is not None else ''
|
||||
self._license = data['license'] if data['license'] is not None and len(data['license']) > 0 else name
|
||||
self._license_full = data['license_full'] if 'license_full' in data else ''
|
||||
self._sources_url = data['sources_url'] if 'sources_url' in data else ''
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def version(self):
|
||||
return self._version
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def summary(self):
|
||||
return self._summary
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def license(self):
|
||||
return self._license
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def license_full(self):
|
||||
return self._license_full
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def sources_url(self):
|
||||
return self._sources_url
|
||||
|
|
@ -449,6 +449,6 @@ class PrintInformation(QObject):
|
|||
"""If this is a sort of output 'device' (like local or online file storage, rather than a printer),
|
||||
the user could have altered the file-name, and thus the project name should be altered as well."""
|
||||
if isinstance(output_device, ProjectOutputDevice):
|
||||
new_name = output_device.getLastOutputName()
|
||||
new_name = output_device.popLastOutputName()
|
||||
if new_name is not None:
|
||||
self.setJobName(os.path.splitext(os.path.basename(new_name))[0])
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@ class WelcomePagesModel(ListModel):
|
|||
def _getBuiltinWelcomePagePath(page_filename: str) -> QUrl:
|
||||
"""Convenience function to get QUrl path to pages that's located in "resources/qml/WelcomePages"."""
|
||||
from cura.CuraApplication import CuraApplication
|
||||
return QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "WelcomePages", page_filename))
|
||||
return QUrl.fromLocalFile(
|
||||
Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "WelcomePages", page_filename))
|
||||
|
||||
# FIXME: HACKs for optimization that we don't update the model every time the active machine gets changed.
|
||||
def _onActiveMachineChanged(self) -> None:
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ if __name__ == "__main__":
|
|||
parser.add_argument("--app_name", required = True, type = str, help = "Filename of the .app that will be contained within the dmg/pkg")
|
||||
args = parser.parse_args()
|
||||
|
||||
cura_version = args.cura_conan_version.replace("+","-") # + is not allowed for bundle identifier
|
||||
cura_version = args.cura_conan_version.replace("+", "-") # + is not allowed for bundle identifier
|
||||
|
||||
app_name = f"{args.app_name}.app"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
# Copyright (c) 2022 UltiMaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura's build system is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
!define APP_NAME "{{ app_name }}"
|
||||
!define COMP_NAME "{{ company }}"
|
||||
!define WEB_SITE "{{ web_site }}"
|
||||
!define VERSION "{{ version }}"
|
||||
!define VIVERSION "{{ version_major }}.{{ version_minor }}.{{ version_patch }}.0"
|
||||
!define COPYRIGHT "Copyright (c) {{ year }} {{ company }}"
|
||||
|
|
@ -16,13 +15,11 @@
|
|||
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${APP_NAME}-${VERSION}"
|
||||
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}-${VERSION}"
|
||||
|
||||
!define REG_START_MENU "Start Menu Folder"
|
||||
!define REG_START_MENU "Start Menu Shortcut"
|
||||
|
||||
;Require administrator access
|
||||
RequestExecutionLevel admin
|
||||
|
||||
var SM_Folder
|
||||
|
||||
######################################################################
|
||||
|
||||
VIProductVersion "${VIVERSION}"
|
||||
|
|
@ -64,11 +61,9 @@ InstallDir "$PROGRAMFILES64\${APP_NAME}"
|
|||
|
||||
!ifdef REG_START_MENU
|
||||
!define MUI_STARTMENUPAGE_NODISABLE
|
||||
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "UltiMaker Cura"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}"
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder
|
||||
!endif
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
|
@ -107,27 +102,11 @@ SetOutPath "$INSTDIR"
|
|||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
!ifdef REG_START_MENU
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
CreateDirectory "$SMPROGRAMS\$SM_Folder"
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
!ifdef WEB_SITE
|
||||
WriteIniStr "$INSTDIR\UltiMaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\$SM_Folder\UltiMaker Cura website.lnk" "$INSTDIR\UltiMaker Cura website.url"
|
||||
!endif
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
CreateShortCut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
|
||||
!endif
|
||||
|
||||
!ifndef REG_START_MENU
|
||||
CreateDirectory "$SMPROGRAMS\{{ app_name }}"
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
!ifdef WEB_SITE
|
||||
WriteIniStr "$INSTDIR\UltiMaker Cura website.url" "InternetShortcut" "URL" "${WEB_SITE}"
|
||||
CreateShortCut "$SMPROGRAMS\{{ app_name }}\UltiMaker Cura website.lnk" "$INSTDIR\UltiMaker Cura website.url"
|
||||
!endif
|
||||
CreateShortCut "$SMPROGRAMS\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}"
|
||||
!endif
|
||||
|
||||
WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
|
||||
|
|
@ -138,9 +117,6 @@ WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_
|
|||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}"
|
||||
|
||||
!ifdef WEB_SITE
|
||||
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "${WEB_SITE}"
|
||||
!endif
|
||||
SectionEnd
|
||||
|
||||
######################################################################
|
||||
|
|
@ -177,29 +153,17 @@ RmDir "$INSTDIR\share\uranium"
|
|||
RmDir "$INSTDIR\share"
|
||||
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
!ifdef WEB_SITE
|
||||
Delete "$INSTDIR\${APP_NAME} website.url"
|
||||
!endif
|
||||
|
||||
RmDir /r /REBOOTOK "$INSTDIR"
|
||||
|
||||
!ifdef REG_START_MENU
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder
|
||||
Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk"
|
||||
!ifdef WEB_SITE
|
||||
Delete "$SMPROGRAMS\$SM_Folder\UltiMaker Cura website.lnk"
|
||||
!endif
|
||||
RmDir "$SMPROGRAMS\$SM_Folder"
|
||||
Delete "$SMPROGRAMS\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\Uninstall ${APP_NAME}.lnk"
|
||||
!endif
|
||||
|
||||
!ifndef REG_START_MENU
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\Uninstall ${APP_NAME}.lnk"
|
||||
!ifdef WEB_SITE
|
||||
Delete "$SMPROGRAMS\{{ app_name }}\UltiMaker Cura website.lnk"
|
||||
!endif
|
||||
RmDir "$SMPROGRAMS\{{ app_name }}"
|
||||
Delete "$SMPROGRAMS\${APP_NAME}.lnk"
|
||||
Delete "$SMPROGRAMS\Uninstall ${APP_NAME}.lnk"
|
||||
!endif
|
||||
|
||||
!insertmacro APP_UNASSOCIATE "stl" "Cura.model"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2022 UltiMaker
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
|
||||
|
|
@ -51,7 +51,6 @@ def generate_nsi(source_path: str, dist_path: str, filename: str, version: str):
|
|||
version_minor = str(parsed_version.minor),
|
||||
version_patch = str(parsed_version.patch),
|
||||
company = "UltiMaker",
|
||||
web_site = "https://ultimaker.com",
|
||||
year = datetime.now().year,
|
||||
cura_license_file = str(source_loc.joinpath("packaging", "cura_license.txt")),
|
||||
compression_method = "LZMA", # ZLIB, BZIP2 or LZMA
|
||||
|
|
@ -78,7 +77,7 @@ if __name__ == "__main__":
|
|||
parser = argparse.ArgumentParser(description = "Create Windows exe installer of Cura.")
|
||||
parser.add_argument("--source_path", type=str, help="Path to Conan install Cura folder.")
|
||||
parser.add_argument("--dist_path", type=str, help="Path to Pyinstaller dist folder")
|
||||
parser.add_argument("--filename", type = str, help = "Filename of the exe (e.g. 'UltiMaker-Cura-5.1.0-beta-Windows-X64.exe')")
|
||||
parser.add_argument("--filename", type=str, help="Filename of the exe (e.g. 'UltiMaker-Cura-5.1.0-beta-Windows-X64.exe')")
|
||||
parser.add_argument("--version", type=str, help="The full cura version, e.g. 5.9.0-beta.1+24132")
|
||||
args = parser.parse_args()
|
||||
generate_nsi(args.source_path, args.dist_path, args.filename, args.version)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2022 UltiMaker
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
|
||||
|
|
@ -40,7 +40,6 @@ def generate_wxs(source_path: Path, dist_path: Path, filename: Path, app_name: s
|
|||
version_minor=str(parsed_version.minor),
|
||||
version_patch=str(parsed_version.patch),
|
||||
company="UltiMaker",
|
||||
web_site="https://ultimaker.com",
|
||||
year=datetime.now().year,
|
||||
upgrade_code=str(uuid.uuid5(uuid.NAMESPACE_DNS, app_name)),
|
||||
cura_license_file=str(source_loc.joinpath("packaging", "msi", "cura_license.rtf")),
|
||||
|
|
|
|||
273
plugins/3DConnexion/NavlibClient.py
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
# Copyright (c) 2025 3Dconnexion, UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Math.AxisAlignedBox import AxisAlignedBox
|
||||
from cura.PickingPass import PickingPass
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Scene import Scene
|
||||
from UM.Resources import Resources
|
||||
from UM.Tool import Tool
|
||||
from UM.View.Renderer import Renderer
|
||||
from .OverlayNode import OverlayNode
|
||||
import pynavlib.pynavlib_interface as pynav
|
||||
|
||||
|
||||
class NavlibClient(pynav.NavlibNavigationModel, Tool):
|
||||
|
||||
def __init__(self, scene: Scene, renderer: Renderer) -> None:
|
||||
pynav.NavlibNavigationModel.__init__(self, False, pynav.NavlibOptions.RowMajorOrder)
|
||||
Tool.__init__(self)
|
||||
self._scene = scene
|
||||
self._renderer = renderer
|
||||
self._pointer_pick = None
|
||||
self._was_pick = False
|
||||
self._hit_selection_only = False
|
||||
self._picking_pass = None
|
||||
self._pivot_node = OverlayNode(node=SceneNode(), image_path=Resources.getPath(Resources.Images, "cor.png"), size=2.5)
|
||||
self.put_profile_hint("UltiMaker Cura")
|
||||
self.enable_navigation(True)
|
||||
|
||||
def pick(self, x: float, y: float, check_selection: bool = False, radius: float = 0.) -> Optional[Vector]:
|
||||
|
||||
if self._picking_pass is None or radius < 0.:
|
||||
return None
|
||||
|
||||
step = 0.
|
||||
if radius == 0.:
|
||||
grid_resolution = 0
|
||||
else:
|
||||
grid_resolution = 5
|
||||
step = (2. * radius) / float(grid_resolution)
|
||||
|
||||
min_depth = 99999.
|
||||
result_position = None
|
||||
|
||||
for i in range(grid_resolution + 1):
|
||||
for j in range(grid_resolution + 1):
|
||||
|
||||
coord_x = (x - radius) + i * step
|
||||
coord_y = (y - radius) + j * step
|
||||
|
||||
picked_depth = self._picking_pass.getPickedDepth(coord_x, coord_y)
|
||||
max_depth = 16777.215
|
||||
|
||||
if 0. < picked_depth < max_depth:
|
||||
|
||||
valid_hit = True
|
||||
if check_selection:
|
||||
selection_pass = self._renderer.getRenderPass("selection")
|
||||
picked_object_id = selection_pass.getIdAtPosition(coord_x, coord_y)
|
||||
picked_object = self._scene.findObject(picked_object_id)
|
||||
|
||||
from UM.Scene.Selection import Selection
|
||||
valid_hit = Selection.isSelected(picked_object)
|
||||
|
||||
if not valid_hit and grid_resolution > 0.:
|
||||
continue
|
||||
elif not valid_hit and grid_resolution == 0.:
|
||||
return None
|
||||
|
||||
if picked_depth < min_depth:
|
||||
min_depth = picked_depth
|
||||
result_position = self._picking_pass.getPickedPosition(coord_x, coord_y)
|
||||
|
||||
return result_position
|
||||
|
||||
def get_pointer_position(self) -> "pynav.NavlibVector":
|
||||
|
||||
from UM.Qt.QtApplication import QtApplication
|
||||
main_window = QtApplication.getInstance().getMainWindow()
|
||||
|
||||
x_n = 2. * main_window._mouse_x / main_window.width() - 1.
|
||||
y_n = 2. * main_window._mouse_y / main_window.height() - 1.
|
||||
|
||||
if self.get_is_view_perspective():
|
||||
self._was_pick = True
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
wrapped_pick = call_on_qt_thread(self.pick)
|
||||
|
||||
self._pointer_pick = wrapped_pick(x_n, y_n)
|
||||
|
||||
return pynav.NavlibVector(0., 0., 0.)
|
||||
else:
|
||||
ray = self._scene.getActiveCamera().getRay(x_n, y_n)
|
||||
pointer_position = ray.origin + ray.direction
|
||||
|
||||
return pynav.NavlibVector(pointer_position.x, pointer_position.y, pointer_position.z)
|
||||
|
||||
def get_view_extents(self) -> "pynav.NavlibBox":
|
||||
|
||||
view_width = self._scene.getActiveCamera().getViewportWidth()
|
||||
view_height = self._scene.getActiveCamera().getViewportHeight()
|
||||
horizontal_zoom = view_width * self._scene.getActiveCamera().getZoomFactor()
|
||||
vertical_zoom = view_height * self._scene.getActiveCamera().getZoomFactor()
|
||||
|
||||
pt_min = pynav.NavlibVector(-view_width / 2 - horizontal_zoom, -view_height / 2 - vertical_zoom, -9001)
|
||||
pt_max = pynav.NavlibVector(view_width / 2 + horizontal_zoom, view_height / 2 + vertical_zoom, 9001)
|
||||
|
||||
return pynav.NavlibBox(pt_min, pt_max)
|
||||
|
||||
def get_view_frustum(self) -> "pynav.NavlibFrustum":
|
||||
|
||||
projection_matrix = self._scene.getActiveCamera().getProjectionMatrix()
|
||||
half_height = 2. / projection_matrix.getData()[1,1]
|
||||
half_width = half_height * (projection_matrix.getData()[1,1] / projection_matrix.getData()[0,0])
|
||||
|
||||
return pynav.NavlibFrustum(-half_width, half_width, -half_height, half_height, 1., 5000.)
|
||||
|
||||
def get_is_view_perspective(self) -> bool:
|
||||
return self._scene.getActiveCamera().isPerspective()
|
||||
|
||||
def get_selection_extents(self) -> "pynav.NavlibBox":
|
||||
|
||||
from UM.Scene.Selection import Selection
|
||||
bounding_box = Selection.getBoundingBox()
|
||||
|
||||
if(bounding_box is not None) :
|
||||
pt_min = pynav.NavlibVector(bounding_box.minimum.x, bounding_box.minimum.y, bounding_box.minimum.z)
|
||||
pt_max = pynav.NavlibVector(bounding_box.maximum.x, bounding_box.maximum.y, bounding_box.maximum.z)
|
||||
return pynav.NavlibBox(pt_min, pt_max)
|
||||
|
||||
def get_selection_transform(self) -> "pynav.NavlibMatrix":
|
||||
return pynav.NavlibMatrix()
|
||||
|
||||
def get_is_selection_empty(self) -> bool:
|
||||
from UM.Scene.Selection import Selection
|
||||
return not Selection.hasSelection()
|
||||
|
||||
def get_pivot_visible(self) -> bool:
|
||||
return False
|
||||
|
||||
def get_camera_matrix(self) -> "pynav.NavlibMatrix":
|
||||
|
||||
transformation = self._scene.getActiveCamera().getLocalTransformation()
|
||||
|
||||
return pynav.NavlibMatrix([[transformation.at(0, 0), transformation.at(0, 1), transformation.at(0, 2), transformation.at(0, 3)],
|
||||
[transformation.at(1, 0), transformation.at(1, 1), transformation.at(1, 2), transformation.at(1, 3)],
|
||||
[transformation.at(2, 0), transformation.at(2, 1), transformation.at(2, 2), transformation.at(2, 3)],
|
||||
[transformation.at(3, 0), transformation.at(3, 1), transformation.at(3, 2), transformation.at(3, 3)]])
|
||||
|
||||
def get_coordinate_system(self) -> "pynav.NavlibMatrix":
|
||||
return pynav.NavlibMatrix()
|
||||
|
||||
def get_front_view(self) -> "pynav.NavlibMatrix":
|
||||
return pynav.NavlibMatrix()
|
||||
|
||||
def get_model_extents(self) -> "pynav.NavlibBox":
|
||||
|
||||
result_bbox = AxisAlignedBox()
|
||||
build_volume_bbox = None
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
node.setCalculateBoundingBox(True)
|
||||
if node.__class__.__qualname__ == "CuraSceneNode" :
|
||||
result_bbox = result_bbox + node.getBoundingBox()
|
||||
elif node.__class__.__qualname__ == "BuildVolume":
|
||||
build_volume_bbox = node.getBoundingBox()
|
||||
|
||||
if not result_bbox.isValid():
|
||||
result_bbox = build_volume_bbox
|
||||
|
||||
if result_bbox is not None:
|
||||
pt_min = pynav.NavlibVector(result_bbox.minimum.x, result_bbox.minimum.y, result_bbox.minimum.z)
|
||||
pt_max = pynav.NavlibVector(result_bbox.maximum.x, result_bbox.maximum.y, result_bbox.maximum.z)
|
||||
self._scene_center = result_bbox.center
|
||||
self._scene_radius = (result_bbox.maximum - self._scene_center).length()
|
||||
return pynav.NavlibBox(pt_min, pt_max)
|
||||
|
||||
def get_pivot_position(self) -> "pynav.NavlibVector":
|
||||
return pynav.NavlibVector()
|
||||
|
||||
def get_hit_look_at(self) -> "pynav.NavlibVector":
|
||||
|
||||
if self._was_pick and self._pointer_pick is not None:
|
||||
return pynav.NavlibVector(self._pointer_pick.x, self._pointer_pick.y, self._pointer_pick.z)
|
||||
elif self._was_pick and self._pointer_pick is None:
|
||||
return None
|
||||
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
wrapped_pick = call_on_qt_thread(self.pick)
|
||||
picked_position = wrapped_pick(0, 0, self._hit_selection_only, 0.5)
|
||||
|
||||
if picked_position is not None:
|
||||
return pynav.NavlibVector(picked_position.x, picked_position.y, picked_position.z)
|
||||
|
||||
def get_units_to_meters(self) -> float:
|
||||
return 0.05
|
||||
|
||||
def is_user_pivot(self) -> bool:
|
||||
return False
|
||||
|
||||
def set_camera_matrix(self, matrix : "pynav.NavlibMatrix") -> None:
|
||||
|
||||
# !!!!!!
|
||||
# Hit testing in Orthographic view is not reliable
|
||||
# Picking starts in camera position, not on near plane
|
||||
# which results in wrong depth values (visible geometry
|
||||
# cannot be picked properly) - Workaround needed (camera position offset)
|
||||
# !!!!!!
|
||||
if not self.get_is_view_perspective():
|
||||
affine = matrix._matrix
|
||||
direction = Vector(-affine[0][2], -affine[1][2], -affine[2][2])
|
||||
distance = self._scene_center - Vector(affine[0][3], affine[1][3], affine[2][3])
|
||||
|
||||
cos_value = direction.dot(distance.normalized())
|
||||
|
||||
offset = 0.
|
||||
|
||||
if (distance.length() < self._scene_radius) and (cos_value > 0.):
|
||||
offset = self._scene_radius
|
||||
elif (distance.length() < self._scene_radius) and (cos_value < 0.):
|
||||
offset = 2. * self._scene_radius
|
||||
elif (distance.length() > self._scene_radius) and (cos_value < 0.):
|
||||
offset = 2. * distance.length()
|
||||
|
||||
matrix._matrix[0][3] = matrix._matrix[0][3] - offset * direction.x
|
||||
matrix._matrix[1][3] = matrix._matrix[1][3] - offset * direction.y
|
||||
matrix._matrix[2][3] = matrix._matrix[2][3] - offset * direction.z
|
||||
|
||||
transformation = Matrix(data = matrix._matrix)
|
||||
self._scene.getActiveCamera().setTransformation(transformation)
|
||||
|
||||
active_camera = self._scene.getActiveCamera()
|
||||
if active_camera.isPerspective():
|
||||
camera_position = active_camera.getWorldPosition()
|
||||
dist = (camera_position - self._pivot_node.getWorldPosition()).length()
|
||||
scale = dist / 400.
|
||||
else:
|
||||
view_width = active_camera.getViewportWidth()
|
||||
current_size = view_width + (2. * active_camera.getZoomFactor() * view_width)
|
||||
scale = current_size / view_width * 5.
|
||||
|
||||
self._pivot_node.scale(scale)
|
||||
|
||||
def set_view_extents(self, extents: "pynav.NavlibBox") -> None:
|
||||
view_width = self._scene.getActiveCamera().getViewportWidth()
|
||||
new_zoom = (extents._min._x + view_width / 2.) / - view_width
|
||||
self._scene.getActiveCamera().setZoomFactor(new_zoom)
|
||||
|
||||
def set_hit_selection_only(self, onlySelection : bool) -> None:
|
||||
self._hit_selection_only = onlySelection
|
||||
|
||||
def set_motion_flag(self, motion : bool) -> None:
|
||||
if motion:
|
||||
width = self._scene.getActiveCamera().getViewportWidth()
|
||||
height = self._scene.getActiveCamera().getViewportHeight()
|
||||
self._picking_pass = PickingPass(width, height)
|
||||
self._renderer.addRenderPass(self._picking_pass)
|
||||
else:
|
||||
self._was_pick = False
|
||||
self._renderer.removeRenderPass(self._picking_pass)
|
||||
|
||||
def set_pivot_position(self, position) -> None:
|
||||
self._pivot_node._target_node.setPosition(position=Vector(position._x, position._y, position._z), transform_space = SceneNode.TransformSpace.World)
|
||||
|
||||
def set_pivot_visible(self, visible) -> None:
|
||||
if visible:
|
||||
self._scene.getRoot().addChild(self._pivot_node)
|
||||
else:
|
||||
self._scene.getRoot().removeChild(self._pivot_node)
|
||||
68
plugins/3DConnexion/OverlayNode.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Copyright (c) 2025 3Dconnexion, UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder # To create the overlay quad
|
||||
from UM.Resources import Resources # To find shader locations
|
||||
from UM.Math.Matrix import Matrix
|
||||
from UM.Application import Application
|
||||
|
||||
try:
|
||||
from PyQt6.QtGui import QImage
|
||||
except:
|
||||
from PyQt5.QtGui import QImage
|
||||
|
||||
class OverlayNode(SceneNode):
|
||||
def __init__(self, node, image_path, size, parent=None):
|
||||
super().__init__(parent)
|
||||
self._target_node = node
|
||||
self.setCalculateBoundingBox(False)
|
||||
|
||||
self._overlay_mesh = self._createOverlayQuad(size)
|
||||
self._drawed_mesh = self._overlay_mesh
|
||||
self._shader = None
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._scale = 1.
|
||||
self._image_path = image_path
|
||||
|
||||
def scale(self, factor):
|
||||
scale_matrix = Matrix()
|
||||
scale_matrix.setByScaleFactor(factor)
|
||||
self._drawed_mesh = self._overlay_mesh.getTransformed(scale_matrix)
|
||||
|
||||
def _createOverlayQuad(self, size):
|
||||
mb = MeshBuilder()
|
||||
mb.addFaceByPoints(-size / 2, -size / 2, 0, -size / 2, size / 2, 0, size / 2, -size / 2, 0)
|
||||
mb.addFaceByPoints(size / 2, size / 2, 0, -size / 2, size / 2, 0, size / 2, -size / 2, 0)
|
||||
|
||||
# Set UV coordinates so a texture can be created
|
||||
mb.setVertexUVCoordinates(0, 0, 1)
|
||||
mb.setVertexUVCoordinates(1, 0, 0)
|
||||
mb.setVertexUVCoordinates(4, 0, 0)
|
||||
mb.setVertexUVCoordinates(2, 1, 1)
|
||||
mb.setVertexUVCoordinates(5, 1, 1)
|
||||
mb.setVertexUVCoordinates(3, 1, 0)
|
||||
|
||||
return mb.build()
|
||||
|
||||
def render(self, renderer):
|
||||
|
||||
if not self._shader:
|
||||
# We now misuse the platform shader, as it actually supports textures
|
||||
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "platform.shader"))
|
||||
# Set the opacity to 0, so that the template is in full control.
|
||||
self._shader.setUniformValue("u_opacity", 0)
|
||||
self._texture = OpenGL.getInstance().createTexture()
|
||||
texture_image = QImage(self._image_path)
|
||||
self._texture.setImage(texture_image)
|
||||
self._shader.setTexture(0, self._texture)
|
||||
|
||||
node_position = self._target_node.getWorldPosition()
|
||||
position_matrix = Matrix()
|
||||
position_matrix.setByTranslation(node_position)
|
||||
camera_orientation = self._scene.getActiveCamera().getOrientation().toMatrix()
|
||||
|
||||
renderer.queueNode(self._scene.getRoot(), shader=self._shader, mesh=self._drawed_mesh.getTransformed(position_matrix.multiply(camera_orientation)), overlay=True)
|
||||
|
||||
return True # This node does it's own rendering.
|
||||
26
plugins/3DConnexion/__init__.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from UM.Logger import Logger
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from UM.Application import Application
|
||||
|
||||
|
||||
def getMetaData() -> Dict[str, Any]:
|
||||
return {
|
||||
"tool": {
|
||||
"visible": False
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def register(app: "Application") -> Dict[str, Any]:
|
||||
try:
|
||||
from .NavlibClient import NavlibClient
|
||||
return { "tool": NavlibClient(app.getController().getScene(), app.getRenderer()) }
|
||||
except BaseException as exception:
|
||||
Logger.warning(f"Unable to load 3Dconnexion library: {exception}")
|
||||
return { }
|
||||
8
plugins/3DConnexion/plugin.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "3DConnexion mouses",
|
||||
"author": "3DConnexion",
|
||||
"version": "1.0.0",
|
||||
"description": "Allows working with 3D mouses inside Cura.",
|
||||
"api": 8,
|
||||
"i18n-catalog": "cura"
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ class ThreeMFReader(MeshReader):
|
|||
return temp_mat
|
||||
|
||||
@staticmethod
|
||||
def _convertSavitarNodeToUMNode(savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]:
|
||||
def _convertSavitarNodeToUMNode(savitar_node: Savitar.SceneNode, file_name: str = "", archive: zipfile.ZipFile = None) -> Optional[SceneNode]:
|
||||
"""Convenience function that converts a SceneNode object (as obtained from libSavitar) to a scene node.
|
||||
|
||||
:returns: Scene node.
|
||||
|
|
@ -115,6 +115,10 @@ class ThreeMFReader(MeshReader):
|
|||
|
||||
active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
|
||||
|
||||
component_path = savitar_node.getComponentPath()
|
||||
if component_path != "" and archive is not None:
|
||||
savitar_node.parseComponentData(archive.open(component_path.lstrip("/")).read())
|
||||
|
||||
um_node = CuraSceneNode() # This adds a SettingOverrideDecorator
|
||||
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
|
||||
try:
|
||||
|
|
@ -143,7 +147,7 @@ class ThreeMFReader(MeshReader):
|
|||
um_node.setMeshData(mesh_data)
|
||||
|
||||
for child in savitar_node.getChildren():
|
||||
child_node = ThreeMFReader._convertSavitarNodeToUMNode(child)
|
||||
child_node = ThreeMFReader._convertSavitarNodeToUMNode(child, archive=archive)
|
||||
if child_node:
|
||||
um_node.addChild(child_node)
|
||||
|
||||
|
|
@ -232,7 +236,7 @@ class ThreeMFReader(MeshReader):
|
|||
CuraApplication.getInstance().getController().getScene().setMetaDataEntry(key, value)
|
||||
|
||||
for node in scene_3mf.getSceneNodes():
|
||||
um_node = ThreeMFReader._convertSavitarNodeToUMNode(node, file_name)
|
||||
um_node = ThreeMFReader._convertSavitarNodeToUMNode(node, file_name, archive)
|
||||
if um_node is None:
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def getMetaData() -> Dict:
|
|||
if "3MFReader.ThreeMFReader" in sys.modules:
|
||||
metaData["mesh_reader"] = [
|
||||
{
|
||||
"extension": "3mf",
|
||||
"extension": workspace_extension,
|
||||
"description": catalog.i18nc("@item:inlistbox", "3MF File")
|
||||
}
|
||||
]
|
||||
|
|
|
|||
176
plugins/3MFWriter/BambuLabVariant.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from io import StringIO
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
|
||||
from PyQt6.QtCore import Qt, QBuffer
|
||||
from PyQt6.QtGui import QImage
|
||||
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from typing import cast
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
|
||||
from .ThreeMFVariant import ThreeMFVariant
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
# Path constants
|
||||
METADATA_PATH = "Metadata"
|
||||
THUMBNAIL_PATH_MULTIPLATE = f"{METADATA_PATH}/plate_1.png"
|
||||
THUMBNAIL_PATH_MULTIPLATE_SMALL = f"{METADATA_PATH}/plate_1_small.png"
|
||||
GCODE_PATH = f"{METADATA_PATH}/plate_1.gcode"
|
||||
GCODE_MD5_PATH = f"{GCODE_PATH}.md5"
|
||||
MODEL_SETTINGS_PATH = f"{METADATA_PATH}/model_settings.config"
|
||||
PLATE_DESC_PATH = f"{METADATA_PATH}/plate_1.json"
|
||||
SLICE_INFO_PATH = f"{METADATA_PATH}/slice_info.config"
|
||||
PROJECT_SETTINGS_PATH = f"{METADATA_PATH}/project_settings.config"
|
||||
|
||||
class BambuLabVariant(ThreeMFVariant):
|
||||
"""BambuLab specific implementation of the 3MF format."""
|
||||
|
||||
@property
|
||||
def mime_type(self) -> str:
|
||||
return "application/vnd.bambulab-package.3dmanufacturing-3dmodel+xml"
|
||||
|
||||
def process_thumbnail(self, snapshot: QImage, thumbnail_buffer: QBuffer,
|
||||
archive: zipfile.ZipFile, relations_element: ET.Element) -> None:
|
||||
"""Process the thumbnail for BambuLab variant."""
|
||||
# Write thumbnail
|
||||
archive.writestr(zipfile.ZipInfo(THUMBNAIL_PATH_MULTIPLATE), thumbnail_buffer.data())
|
||||
|
||||
# Add relations elements for thumbnails
|
||||
ET.SubElement(relations_element, "Relationship",
|
||||
Target="/" + THUMBNAIL_PATH_MULTIPLATE, Id="rel-2",
|
||||
pe="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
|
||||
|
||||
ET.SubElement(relations_element, "Relationship",
|
||||
Target="/" + THUMBNAIL_PATH_MULTIPLATE, Id="rel-4",
|
||||
Type="http://schemas.bambulab.com/package/2021/cover-thumbnail-middle")
|
||||
|
||||
# Create and save small thumbnail
|
||||
small_snapshot = snapshot.scaled(128, 128, transformMode=Qt.TransformationMode.SmoothTransformation)
|
||||
small_thumbnail_buffer = QBuffer()
|
||||
small_thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite)
|
||||
small_snapshot.save(small_thumbnail_buffer, "PNG")
|
||||
|
||||
# Write small thumbnail
|
||||
archive.writestr(zipfile.ZipInfo(THUMBNAIL_PATH_MULTIPLATE_SMALL), small_thumbnail_buffer.data())
|
||||
|
||||
# Add relation for small thumbnail
|
||||
ET.SubElement(relations_element, "Relationship",
|
||||
Target="/" + THUMBNAIL_PATH_MULTIPLATE_SMALL, Id="rel-5",
|
||||
Type="http://schemas.bambulab.com/package/2021/cover-thumbnail-small")
|
||||
|
||||
def add_extra_files(self, archive: zipfile.ZipFile, metadata_relations_element: ET.Element) -> None:
|
||||
"""Add BambuLab specific files to the archive."""
|
||||
self._storeGCode(archive, metadata_relations_element)
|
||||
self._storeModelSettings(archive)
|
||||
self._storePlateDesc(archive)
|
||||
self._storeSliceInfo(archive)
|
||||
self._storeProjectSettings(archive)
|
||||
|
||||
def _storeGCode(self, archive: zipfile.ZipFile, metadata_relations_element: ET.Element):
|
||||
"""Store GCode data in the archive."""
|
||||
gcode_textio = StringIO()
|
||||
gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
|
||||
success = gcode_writer.write(gcode_textio, None)
|
||||
|
||||
if not success:
|
||||
error_msg = catalog.i18nc("@info:error", "Can't write GCode to 3MF file")
|
||||
self._writer.setInformation(error_msg)
|
||||
Logger.error(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
gcode_data = gcode_textio.getvalue().encode("UTF-8")
|
||||
archive.writestr(zipfile.ZipInfo(GCODE_PATH), gcode_data)
|
||||
|
||||
gcode_relation_element = ET.SubElement(metadata_relations_element, "Relationship",
|
||||
Target=f"/{GCODE_PATH}", Id="rel-1",
|
||||
Type="http://schemas.bambulab.com/package/2021/gcode")
|
||||
|
||||
# Calculate and store the MD5 sum of the gcode data
|
||||
md5_hash = hashlib.md5(gcode_data).hexdigest()
|
||||
archive.writestr(zipfile.ZipInfo(GCODE_MD5_PATH), md5_hash.encode("UTF-8"))
|
||||
|
||||
def _storeModelSettings(self, archive: zipfile.ZipFile):
|
||||
"""Store model settings in the archive."""
|
||||
config = ET.Element("config")
|
||||
plate = ET.SubElement(config, "plate")
|
||||
ET.SubElement(plate, "metadata", key="plater_id", value="1")
|
||||
ET.SubElement(plate, "metadata", key="plater_name", value="")
|
||||
ET.SubElement(plate, "metadata", key="locked", value="false")
|
||||
ET.SubElement(plate, "metadata", key="filament_map_mode", value="Auto For Flush")
|
||||
extruders_count = len(CuraApplication.getInstance().getExtruderManager().extruderIds)
|
||||
ET.SubElement(plate, "metadata", key="filament_maps", value=" ".join("1" for _ in range(extruders_count)))
|
||||
ET.SubElement(plate, "metadata", key="gcode_file", value=GCODE_PATH)
|
||||
ET.SubElement(plate, "metadata", key="thumbnail_file", value=THUMBNAIL_PATH_MULTIPLATE)
|
||||
ET.SubElement(plate, "metadata", key="pattern_bbox_file", value=PLATE_DESC_PATH)
|
||||
|
||||
self._writer._storeElementTree(archive, MODEL_SETTINGS_PATH, config)
|
||||
|
||||
def _storePlateDesc(self, archive: zipfile.ZipFile):
|
||||
"""Store plate description in the archive."""
|
||||
plate_desc = {}
|
||||
|
||||
filament_ids = []
|
||||
filament_colors = []
|
||||
|
||||
for extruder in CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks():
|
||||
filament_ids.append(extruder.getValue("extruder_nr"))
|
||||
filament_colors.append(self._writer._getMaterialColor(extruder))
|
||||
|
||||
plate_desc["filament_ids"] = filament_ids
|
||||
plate_desc["filament_colors"] = filament_colors
|
||||
plate_desc["first_extruder"] = CuraApplication.getInstance().getExtruderManager().getInitialExtruderNr()
|
||||
plate_desc["is_seq_print"] = Application.getInstance().getGlobalContainerStack().getValue("print_sequence") == "one_at_a_time"
|
||||
plate_desc["nozzle_diameter"] = CuraApplication.getInstance().getExtruderManager().getActiveExtruderStack().getValue("machine_nozzle_size")
|
||||
plate_desc["version"] = 2
|
||||
|
||||
file = zipfile.ZipInfo(PLATE_DESC_PATH)
|
||||
file.compress_type = zipfile.ZIP_DEFLATED
|
||||
archive.writestr(file, json.dumps(plate_desc).encode("UTF-8"))
|
||||
|
||||
def _storeSliceInfo(self, archive: zipfile.ZipFile):
|
||||
"""Store slice information in the archive."""
|
||||
config = ET.Element("config")
|
||||
|
||||
header = ET.SubElement(config, "header")
|
||||
ET.SubElement(header, "header_item", key="X-BBL-Client-Type", value="slicer")
|
||||
ET.SubElement(header, "header_item", key="X-BBL-Client-Version", value="02.00.01.50")
|
||||
|
||||
plate = ET.SubElement(config, "plate")
|
||||
ET.SubElement(plate, "metadata", key="index", value="1")
|
||||
ET.SubElement(plate,
|
||||
"metadata",
|
||||
key="nozzle_diameters",
|
||||
value=str(CuraApplication.getInstance().getExtruderManager().getActiveExtruderStack().getValue("machine_nozzle_size")))
|
||||
|
||||
print_information = CuraApplication.getInstance().getPrintInformation()
|
||||
for index, extruder in enumerate(Application.getInstance().getGlobalContainerStack().extruderList):
|
||||
used_m = print_information.materialLengths[index]
|
||||
used_g = print_information.materialWeights[index]
|
||||
if used_m > 0.0 and used_g > 0.0:
|
||||
ET.SubElement(plate,
|
||||
"filament",
|
||||
id=str(extruder.getValue("extruder_nr") + 1),
|
||||
tray_info_idx="GFA00",
|
||||
type=extruder.material.getMetaDataEntry("material", ""),
|
||||
color=self._writer._getMaterialColor(extruder),
|
||||
used_m=str(used_m),
|
||||
used_g=str(used_g))
|
||||
|
||||
self._writer._storeElementTree(archive, SLICE_INFO_PATH, config)
|
||||
|
||||
def _storeProjectSettings(self, archive: zipfile.ZipFile):
|
||||
api = CuraApplication.getInstance().getCuraAPI()
|
||||
file = zipfile.ZipInfo(PROJECT_SETTINGS_PATH)
|
||||
json_string = json.dumps(api.interface.settings.getAllGlobalSettings(), separators=(", ", ": "), indent=4)
|
||||
archive.writestr(file, json_string.encode("UTF-8"))
|
||||
33
plugins/3MFWriter/Cura3mfVariant.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
|
||||
from PyQt6.QtCore import QBuffer
|
||||
from PyQt6.QtGui import QImage
|
||||
|
||||
from .ThreeMFVariant import ThreeMFVariant
|
||||
|
||||
# Standard 3MF paths
|
||||
METADATA_PATH = "Metadata"
|
||||
THUMBNAIL_PATH = f"{METADATA_PATH}/thumbnail.png"
|
||||
|
||||
class Cura3mfVariant(ThreeMFVariant):
|
||||
"""Default implementation of the 3MF format."""
|
||||
|
||||
@property
|
||||
def mime_type(self) -> str:
|
||||
return "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"
|
||||
|
||||
def process_thumbnail(self, snapshot: QImage, thumbnail_buffer: QBuffer,
|
||||
archive: zipfile.ZipFile, relations_element: ET.Element) -> None:
|
||||
"""Process the thumbnail for default 3MF variant."""
|
||||
thumbnail_file = zipfile.ZipInfo(THUMBNAIL_PATH)
|
||||
# Don't try to compress snapshot file, because the PNG is pretty much as compact as it will get
|
||||
archive.writestr(thumbnail_file, thumbnail_buffer.data())
|
||||
|
||||
# Add thumbnail relation to _rels/.rels file
|
||||
ET.SubElement(relations_element, "Relationship",
|
||||
Target="/" + THUMBNAIL_PATH, Id="rel1",
|
||||
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
|
||||
74
plugins/3MFWriter/ThreeMFVariant.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
|
||||
from PyQt6.QtGui import QImage
|
||||
from PyQt6.QtCore import QBuffer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .ThreeMFWriter import ThreeMFWriter
|
||||
|
||||
class ThreeMFVariant(ABC):
|
||||
"""Base class for 3MF format variants.
|
||||
|
||||
Different vendors may have their own extensions to the 3MF format,
|
||||
such as BambuLab's 3MF variant. This class provides an interface
|
||||
for implementing these variants.
|
||||
"""
|
||||
|
||||
def __init__(self, writer: 'ThreeMFWriter'):
|
||||
"""
|
||||
:param writer: The ThreeMFWriter instance that will use this variant
|
||||
"""
|
||||
self._writer = writer
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def mime_type(self) -> str:
|
||||
"""The MIME type for this 3MF variant."""
|
||||
pass
|
||||
|
||||
def handles_mime_type(self, mime_type: str) -> bool:
|
||||
"""Check if this variant handles the given MIME type.
|
||||
|
||||
:param mime_type: The MIME type to check
|
||||
:return: True if this variant handles the MIME type, False otherwise
|
||||
"""
|
||||
return mime_type == self.mime_type
|
||||
|
||||
def prepare_content_types(self, content_types: ET.Element) -> None:
|
||||
"""Prepare the content types XML element for this variant.
|
||||
|
||||
:param content_types: The content types XML element
|
||||
"""
|
||||
pass
|
||||
|
||||
def prepare_relations(self, relations_element: ET.Element) -> None:
|
||||
"""Prepare the relations XML element for this variant.
|
||||
|
||||
:param relations_element: The relations XML element
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_thumbnail(self, snapshot: QImage, thumbnail_buffer: QBuffer,
|
||||
archive: zipfile.ZipFile, relations_element: ET.Element) -> None:
|
||||
"""Process the thumbnail for this variant.
|
||||
|
||||
:param snapshot: The snapshot image
|
||||
:param thumbnail_buffer: Buffer containing the thumbnail data
|
||||
:param archive: The zip archive to write to
|
||||
:param relations_element: The relations XML element
|
||||
"""
|
||||
pass
|
||||
|
||||
def add_extra_files(self, archive: zipfile.ZipFile, metadata_relations_element: ET.Element) -> None:
|
||||
"""Add any extra files required by this variant to the archive.
|
||||
|
||||
:param archive: The zip archive to write to
|
||||
:param metadata_relations_element: The metadata relations XML element
|
||||
"""
|
||||
pass
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
# Copyright (c) 2015-2022 Ultimaker B.V.
|
||||
# Copyright (c) 2015-2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import json
|
||||
import re
|
||||
import threading
|
||||
|
||||
from typing import Optional, cast, List, Dict, Pattern, Set
|
||||
from typing import Optional, cast, List, Dict, Set
|
||||
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
from UM.Mesh.MeshWriter import MeshWriter
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Logger import Logger
|
||||
|
|
@ -19,7 +21,9 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
|
|||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.CuraPackageManager import CuraPackageManager
|
||||
from cura.Machines.Models.ExtrudersModel import ExtrudersModel
|
||||
from cura.Settings import CuraContainerStack
|
||||
from cura.Settings.ExtruderStack import ExtruderStack
|
||||
from cura.Utils.Threading import call_on_qt_thread
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Snapshot import Snapshot
|
||||
|
|
@ -45,11 +49,13 @@ import UM.Application
|
|||
|
||||
from .SettingsExportModel import SettingsExportModel
|
||||
from .SettingsExportGroup import SettingsExportGroup
|
||||
from .ThreeMFVariant import ThreeMFVariant
|
||||
from .Cura3mfVariant import Cura3mfVariant
|
||||
from .BambuLabVariant import BambuLabVariant
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
THUMBNAIL_PATH = "Metadata/thumbnail.png"
|
||||
MODEL_PATH = "3D/3dmodel.model"
|
||||
PACKAGE_METADATA_PATH = "Cura/packages.json"
|
||||
|
||||
|
|
@ -68,6 +74,12 @@ class ThreeMFWriter(MeshWriter):
|
|||
self._store_archive = False
|
||||
self._lock = threading.Lock()
|
||||
|
||||
# Register available variants
|
||||
self._variants = {
|
||||
Cura3mfVariant(self).mime_type: Cura3mfVariant,
|
||||
BambuLabVariant(self).mime_type: BambuLabVariant
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _convertMatrixToString(matrix):
|
||||
result = ""
|
||||
|
|
@ -201,26 +213,48 @@ class ThreeMFWriter(MeshWriter):
|
|||
|
||||
painter.end()
|
||||
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode, export_settings_model = None) -> bool:
|
||||
def _getVariant(self, mime_type: str) -> ThreeMFVariant:
|
||||
"""Get the appropriate variant for the given MIME type.
|
||||
|
||||
:param mime_type: The MIME type to get the variant for
|
||||
:return: An instance of the variant for the given MIME type
|
||||
"""
|
||||
variant_class = self._variants.get(mime_type, Cura3mfVariant)
|
||||
return variant_class(self)
|
||||
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode, export_settings_model = None, **kwargs) -> bool:
|
||||
self._archive = None # Reset archive
|
||||
archive = zipfile.ZipFile(stream, "w", compression = zipfile.ZIP_DEFLATED)
|
||||
|
||||
# Determine which variant to use based on mime type in kwargs
|
||||
mime_type = kwargs.get("mime_type", Cura3mfVariant(self).mime_type)
|
||||
variant = self._getVariant(mime_type)
|
||||
|
||||
try:
|
||||
model_file = zipfile.ZipInfo(MODEL_PATH)
|
||||
# Because zipfile is stupid and ignores archive-level compression settings when writing with ZipInfo.
|
||||
model_file.compress_type = zipfile.ZIP_DEFLATED
|
||||
|
||||
# Create content types file
|
||||
content_types_file = zipfile.ZipInfo("[Content_Types].xml")
|
||||
content_types_file.compress_type = zipfile.ZIP_DEFLATED
|
||||
content_types = ET.Element("Types", xmlns = self._namespaces["content-types"])
|
||||
rels_type = ET.SubElement(content_types, "Default", Extension = "rels", ContentType = "application/vnd.openxmlformats-package.relationships+xml")
|
||||
model_type = ET.SubElement(content_types, "Default", Extension = "model", ContentType = "application/vnd.ms-package.3dmanufacturing-3dmodel+xml")
|
||||
|
||||
# Create _rels/.rels file
|
||||
relations_file = zipfile.ZipInfo("_rels/.rels")
|
||||
relations_file.compress_type = zipfile.ZIP_DEFLATED
|
||||
relations_element = ET.Element("Relationships", xmlns = self._namespaces["relationships"])
|
||||
model_relation_element = ET.SubElement(relations_element, "Relationship", Target = "/" + MODEL_PATH, Id = "rel0", Type = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")
|
||||
relations_element = self._makeRelationsTree()
|
||||
model_relation_element = ET.SubElement(relations_element, "Relationship", Target="/" + MODEL_PATH,
|
||||
Id="rel0",
|
||||
Type="http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel")
|
||||
|
||||
# Create Metadata/_rels/model_settings.config.rels
|
||||
metadata_relations_element = self._makeRelationsTree()
|
||||
|
||||
# Let the variant add its specific files
|
||||
variant.add_extra_files(archive, metadata_relations_element)
|
||||
|
||||
# Let the variant prepare content types and relations
|
||||
variant.prepare_content_types(content_types)
|
||||
variant.prepare_relations(relations_element)
|
||||
|
||||
# Attempt to add a thumbnail
|
||||
snapshot = self._createSnapshot()
|
||||
|
|
@ -233,16 +267,11 @@ class ThreeMFWriter(MeshWriter):
|
|||
thumbnail_buffer.open(QBuffer.OpenModeFlag.ReadWrite)
|
||||
snapshot.save(thumbnail_buffer, "PNG")
|
||||
|
||||
thumbnail_file = zipfile.ZipInfo(THUMBNAIL_PATH)
|
||||
# Don't try to compress snapshot file, because the PNG is pretty much as compact as it will get
|
||||
archive.writestr(thumbnail_file, thumbnail_buffer.data())
|
||||
|
||||
# Add PNG to content types file
|
||||
thumbnail_type = ET.SubElement(content_types, "Default", Extension="png", ContentType="image/png")
|
||||
# Add thumbnail relation to _rels/.rels file
|
||||
thumbnail_relation_element = ET.SubElement(relations_element, "Relationship",
|
||||
Target="/" + THUMBNAIL_PATH, Id="rel1",
|
||||
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail")
|
||||
|
||||
# Let the variant process the thumbnail
|
||||
variant.process_thumbnail(snapshot, thumbnail_buffer, archive, relations_element)
|
||||
|
||||
# Write material metadata
|
||||
packages_metadata = self._getMaterialPackageMetadata() + self._getPluginPackageMetadata()
|
||||
|
|
@ -305,8 +334,10 @@ class ThreeMFWriter(MeshWriter):
|
|||
scene_string = parser.sceneToString(savitar_scene)
|
||||
|
||||
archive.writestr(model_file, scene_string)
|
||||
archive.writestr(content_types_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(content_types))
|
||||
archive.writestr(relations_file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(relations_element))
|
||||
self._storeElementTree(archive, "[Content_Types].xml", content_types)
|
||||
self._storeElementTree(archive, "_rels/.rels", relations_element)
|
||||
if len(metadata_relations_element) > 0:
|
||||
self._storeElementTree(archive, "Metadata/_rels/model_settings.config.rels", metadata_relations_element)
|
||||
except Exception as error:
|
||||
Logger.logException("e", "Error writing zip file")
|
||||
self.setInformation(str(error))
|
||||
|
|
@ -319,6 +350,25 @@ class ThreeMFWriter(MeshWriter):
|
|||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _storeElementTree(archive: zipfile.ZipFile, file_path: str, root_element: ET.Element):
|
||||
file = zipfile.ZipInfo(file_path)
|
||||
file.compress_type = zipfile.ZIP_DEFLATED
|
||||
archive.writestr(file, b'<?xml version="1.0" encoding="UTF-8"?> \n' + ET.tostring(root_element))
|
||||
|
||||
def _makeRelationsTree(self):
|
||||
return ET.Element("Relationships", xmlns=self._namespaces["relationships"])
|
||||
|
||||
@staticmethod
|
||||
def _getMaterialColor(extruder: "ExtruderStack") -> str:
|
||||
position = int(extruder.getMetaDataEntry("position", default="0"))
|
||||
try:
|
||||
default_color = ExtrudersModel.defaultColors[position]
|
||||
except IndexError:
|
||||
default_color = "#e0e000"
|
||||
color_code = extruder.material.getMetaDataEntry("color_code", default=default_color)
|
||||
return color_code.upper()
|
||||
|
||||
@staticmethod
|
||||
def _storeMetadataJson(metadata: Dict[str, List[Dict[str, str]]], archive: zipfile.ZipFile, path: str) -> None:
|
||||
"""Stores metadata inside archive path as json file"""
|
||||
|
|
|
|||
|
|
@ -28,11 +28,17 @@ def getMetaData():
|
|||
metaData["mesh_writer"] = {
|
||||
"output": [
|
||||
{
|
||||
"extension": "3mf",
|
||||
"extension": workspace_extension,
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "3MF file"),
|
||||
"mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
|
||||
"mode": ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode
|
||||
},
|
||||
{
|
||||
"extension": f"gcode.{workspace_extension}",
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "BambuLab 3MF file"),
|
||||
"mime_type": "application/vnd.bambulab-package.3dmanufacturing-3dmodel+xml",
|
||||
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
|
||||
}
|
||||
]
|
||||
}
|
||||
metaData["workspace_writer"] = {
|
||||
|
|
@ -44,7 +50,7 @@ def getMetaData():
|
|||
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
|
||||
},
|
||||
{
|
||||
"extension": "3mf",
|
||||
"extension": workspace_extension,
|
||||
"description": i18n_catalog.i18nc("@item:inlistbox", "Universal Cura Project"),
|
||||
"mime_type": "application/x-ucp",
|
||||
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2020 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
import json
|
||||
import threading
|
||||
|
|
@ -13,11 +13,14 @@ from UM.Message import Message
|
|||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
|
||||
from UM.i18n import i18nCatalog
|
||||
from cura.ApplicationMetadata import CuraSDKVersion
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
|
||||
import cura.UltimakerCloud.UltimakerCloudConstants as UltimakerCloudConstants
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
PACKAGES_URL = f"{UltimakerCloudConstants.CuraCloudAPIRoot}/cura-packages/v{UltimakerCloudConstants.CuraCloudAPIVersion}/cura/v{CuraSDKVersion}/packages"
|
||||
|
||||
class CreateBackupJob(Job):
|
||||
"""Creates backup zip, requests upload url and uploads the backup file to cloud storage."""
|
||||
|
|
@ -40,23 +43,54 @@ class CreateBackupJob(Job):
|
|||
self._job_done = threading.Event()
|
||||
"""Set when the job completes. Does not indicate success."""
|
||||
self.backup_upload_error_message = ""
|
||||
"""After the job completes, an empty string indicates success. Othrerwise, the value is a translated message."""
|
||||
"""After the job completes, an empty string indicates success. Otherwise, the value is a translated message."""
|
||||
|
||||
def _setPluginFetchErrorMessage(self, error_msg: str) -> None:
|
||||
Logger.error(f"Fetching plugins for backup resulted in error: {error_msg}")
|
||||
self.backup_upload_error_message = "Couldn't update currently available plugins, backup stopped."
|
||||
self._upload_message.hide()
|
||||
self._job_done.set()
|
||||
|
||||
def run(self) -> None:
|
||||
upload_message = Message(catalog.i18nc("@info:backup_status", "Creating your backup..."),
|
||||
self._upload_message = Message(catalog.i18nc("@info:backup_status", "Fetch re-downloadable package-ids..."),
|
||||
title = self.MESSAGE_TITLE,
|
||||
progress = -1)
|
||||
upload_message.show()
|
||||
self._upload_message.show()
|
||||
CuraApplication.getInstance().processEvents()
|
||||
|
||||
if CuraApplication.getInstance().getCuraAPI().backups.shouldReinstallDownloadablePlugins():
|
||||
request_url = f"{PACKAGES_URL}?package_type=plugin"
|
||||
scope = JsonDecoratorScope(UltimakerCloudScope(CuraApplication.getInstance()))
|
||||
HttpRequestManager.getInstance().get(
|
||||
request_url,
|
||||
scope=scope,
|
||||
callback=self._continueRun,
|
||||
error_callback=lambda reply, error: self._setPluginFetchErrorMessage(str(error)),
|
||||
)
|
||||
else:
|
||||
self._continueRun()
|
||||
|
||||
def _continueRun(self, reply: "QNetworkReply" = None) -> None:
|
||||
if reply is not None:
|
||||
response_data = HttpRequestManager.readJSON(reply)
|
||||
if "data" not in response_data:
|
||||
self._setPluginFetchErrorMessage(f"Missing 'data' from response. Keys in response: {response_data.keys()}")
|
||||
return
|
||||
available_remote_plugins = frozenset({v["package_id"] for v in response_data["data"]})
|
||||
else:
|
||||
available_remote_plugins = frozenset()
|
||||
|
||||
self._upload_message.setText(catalog.i18nc("@info:backup_status", "Creating your backup..."))
|
||||
CuraApplication.getInstance().processEvents()
|
||||
cura_api = CuraApplication.getInstance().getCuraAPI()
|
||||
self._backup_zip, backup_meta_data = cura_api.backups.createBackup()
|
||||
self._backup_zip, backup_meta_data = cura_api.backups.createBackup(available_remote_plugins)
|
||||
|
||||
if not self._backup_zip or not backup_meta_data:
|
||||
self.backup_upload_error_message = catalog.i18nc("@info:backup_status", "There was an error while creating your backup.")
|
||||
upload_message.hide()
|
||||
self._upload_message.hide()
|
||||
return
|
||||
|
||||
upload_message.setText(catalog.i18nc("@info:backup_status", "Uploading your backup..."))
|
||||
self._upload_message.setText(catalog.i18nc("@info:backup_status", "Uploading your backup..."))
|
||||
CuraApplication.getInstance().processEvents()
|
||||
|
||||
# Create an upload entry for the backup.
|
||||
|
|
@ -64,13 +98,18 @@ class CreateBackupJob(Job):
|
|||
backup_meta_data["description"] = "{}.backup.{}.cura.zip".format(timestamp, backup_meta_data["cura_release"])
|
||||
self._requestUploadSlot(backup_meta_data, len(self._backup_zip))
|
||||
|
||||
self._job_done.wait()
|
||||
# Note: One 'process events' call wasn't enough with the changed situation somehow.
|
||||
for _ in range(5000):
|
||||
CuraApplication.getInstance().processEvents()
|
||||
if self._job_done.wait(0.02):
|
||||
break
|
||||
|
||||
if self.backup_upload_error_message == "":
|
||||
upload_message.setText(catalog.i18nc("@info:backup_status", "Your backup has finished uploading."))
|
||||
upload_message.setProgress(None) # Hide progress bar
|
||||
self._upload_message.setText(catalog.i18nc("@info:backup_status", "Your backup has finished uploading."))
|
||||
self._upload_message.setProgress(None) # Hide progress bar
|
||||
else:
|
||||
# some error occurred. This error is presented to the user by DrivePluginExtension
|
||||
upload_message.hide()
|
||||
self._upload_message.hide()
|
||||
|
||||
def _requestUploadSlot(self, backup_metadata: Dict[str, Any], backup_size: int) -> None:
|
||||
"""Request a backup upload slot from the API.
|
||||
|
|
@ -83,7 +122,6 @@ class CreateBackupJob(Job):
|
|||
"metadata": backup_metadata
|
||||
}
|
||||
}).encode()
|
||||
|
||||
HttpRequestManager.getInstance().put(
|
||||
self._api_backup_url,
|
||||
data = payload,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Copyright (c) 2021 Ultimaker B.V.
|
||||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import Optional, Any, Dict
|
||||
|
|
@ -12,9 +13,16 @@ from PyQt6.QtNetwork import QNetworkReply, QNetworkRequest
|
|||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.PackageManager import catalog
|
||||
from UM.Resources import Resources
|
||||
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from UM.Version import Version
|
||||
|
||||
from cura.ApplicationMetadata import CuraSDKVersion
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
|
||||
import cura.UltimakerCloud.UltimakerCloudConstants as UltimakerCloudConstants
|
||||
|
||||
PACKAGES_URL_TEMPLATE = f"{UltimakerCloudConstants.CuraCloudAPIRoot}/cura-packages/v{UltimakerCloudConstants.CuraCloudAPIVersion}/cura/v{{0}}/packages/{{1}}/download"
|
||||
|
||||
class RestoreBackupJob(Job):
|
||||
"""Downloads a backup and overwrites local configuration with the backup.
|
||||
|
|
@ -38,7 +46,6 @@ class RestoreBackupJob(Job):
|
|||
self.restore_backup_error_message = ""
|
||||
|
||||
def run(self) -> None:
|
||||
|
||||
url = self._backup.get("download_url")
|
||||
assert url is not None
|
||||
|
||||
|
|
@ -48,7 +55,11 @@ class RestoreBackupJob(Job):
|
|||
error_callback = self._onRestoreRequestCompleted
|
||||
)
|
||||
|
||||
self._job_done.wait() # A job is considered finished when the run function completes
|
||||
# Note: Just to be sure, use the same structure here as in CreateBackupJob.
|
||||
for _ in range(5000):
|
||||
CuraApplication.getInstance().processEvents()
|
||||
if self._job_done.wait(0.02):
|
||||
break
|
||||
|
||||
def _onRestoreRequestCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None:
|
||||
if not HttpRequestManager.replyIndicatesSuccess(reply, error):
|
||||
|
|
@ -60,8 +71,8 @@ class RestoreBackupJob(Job):
|
|||
|
||||
# We store the file in a temporary path fist to ensure integrity.
|
||||
try:
|
||||
temporary_backup_file = NamedTemporaryFile(delete = False)
|
||||
with open(temporary_backup_file.name, "wb") as write_backup:
|
||||
self._temporary_backup_file = NamedTemporaryFile(delete_on_close = False)
|
||||
with open(self._temporary_backup_file.name, "wb") as write_backup:
|
||||
app = CuraApplication.getInstance()
|
||||
bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
|
||||
while bytes_read:
|
||||
|
|
@ -69,23 +80,98 @@ class RestoreBackupJob(Job):
|
|||
bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE)
|
||||
app.processEvents()
|
||||
except EnvironmentError as e:
|
||||
Logger.log("e", f"Unable to save backed up files due to computer limitations: {str(e)}")
|
||||
Logger.error(f"Unable to save backed up files due to computer limitations: {str(e)}")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
return
|
||||
|
||||
if not self._verifyMd5Hash(temporary_backup_file.name, self._backup.get("md5_hash", "")):
|
||||
if not self._verifyMd5Hash(self._temporary_backup_file.name, self._backup.get("md5_hash", "")):
|
||||
# Don't restore the backup if the MD5 hashes do not match.
|
||||
# This can happen if the download was interrupted.
|
||||
Logger.log("w", "Remote and local MD5 hashes do not match, not restoring backup.")
|
||||
Logger.error("Remote and local MD5 hashes do not match, not restoring backup.")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
return
|
||||
|
||||
# Tell Cura to place the backup back in the user data folder.
|
||||
with open(temporary_backup_file.name, "rb") as read_backup:
|
||||
metadata = self._backup.get("metadata", {})
|
||||
with open(self._temporary_backup_file.name, "rb") as read_backup:
|
||||
cura_api = CuraApplication.getInstance().getCuraAPI()
|
||||
cura_api.backups.restoreBackup(read_backup.read(), self._backup.get("metadata", {}))
|
||||
cura_api.backups.restoreBackup(read_backup.read(), metadata, auto_close=False)
|
||||
|
||||
self._job_done.set()
|
||||
# Read packages data-file, to get the 'to_install' plugin-ids.
|
||||
version_to_restore = Version(metadata.get("cura_release", "dev"))
|
||||
version_str = f"{version_to_restore.getMajor()}.{version_to_restore.getMinor()}"
|
||||
packages_path = os.path.abspath(os.path.join(os.path.abspath(
|
||||
Resources.getConfigStoragePath()), "..", version_str, "packages.json"))
|
||||
if not os.path.exists(packages_path):
|
||||
Logger.error(f"Can't find path '{packages_path}' to tell what packages should be redownloaded.")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
return
|
||||
|
||||
to_install = {}
|
||||
try:
|
||||
with open(packages_path, "r") as packages_file:
|
||||
packages_json = json.load(packages_file)
|
||||
if "to_install" in packages_json:
|
||||
for package_data in packages_json["to_install"].values():
|
||||
if "package_info" not in package_data:
|
||||
continue
|
||||
package_info = package_data["package_info"]
|
||||
if "package_id" in package_info and "sdk_version_semver" in package_info:
|
||||
to_install[package_info["package_id"]] = package_info["sdk_version_semver"]
|
||||
except IOError as ex:
|
||||
Logger.error(f"Couldn't open '{packages_path}' because '{str(ex)}' to get packages to re-install.")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
return
|
||||
|
||||
if len(to_install) < 1:
|
||||
Logger.info("No packages to reinstall, early out.")
|
||||
self._job_done.set()
|
||||
return
|
||||
|
||||
# Download all re-installable plugins packages, so they can be put back on start-up.
|
||||
redownload_errors = []
|
||||
def packageDownloadCallback(package_id: str, msg: "QNetworkReply", err: "QNetworkReply.NetworkError" = None) -> None:
|
||||
if err is not None or HttpRequestManager.safeHttpStatus(msg) != 200:
|
||||
redownload_errors.append(err)
|
||||
del to_install[package_id]
|
||||
|
||||
try:
|
||||
with NamedTemporaryFile(mode="wb", suffix=".curapackage", delete=False) as temp_file:
|
||||
bytes_read = msg.read(self.DISK_WRITE_BUFFER_SIZE)
|
||||
while bytes_read:
|
||||
temp_file.write(bytes_read)
|
||||
bytes_read = msg.read(self.DISK_WRITE_BUFFER_SIZE)
|
||||
CuraApplication.getInstance().processEvents()
|
||||
temp_file.close()
|
||||
if not CuraApplication.getInstance().getPackageManager().installPackage(temp_file.name):
|
||||
redownload_errors.append(f"Couldn't install package '{package_id}'.")
|
||||
except IOError as ex:
|
||||
redownload_errors.append(f"Couldn't process package '{package_id}' because '{ex}'.")
|
||||
|
||||
if len(to_install) < 1:
|
||||
if len(redownload_errors) == 0:
|
||||
Logger.info("All packages redownloaded!")
|
||||
self._job_done.set()
|
||||
else:
|
||||
msgs = "\n - ".join(redownload_errors)
|
||||
Logger.error(f"Couldn't re-install at least one package(s) because: {msgs}")
|
||||
self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE
|
||||
self._job_done.set()
|
||||
|
||||
self._package_download_scope = UltimakerCloudScope(CuraApplication.getInstance())
|
||||
for package_id, package_api_version in to_install.items():
|
||||
def handlePackageId(package_id: str = package_id):
|
||||
HttpRequestManager.getInstance().get(
|
||||
PACKAGES_URL_TEMPLATE.format(package_api_version, package_id),
|
||||
scope=self._package_download_scope,
|
||||
callback=lambda msg: packageDownloadCallback(package_id, msg),
|
||||
error_callback=lambda msg, err: packageDownloadCallback(package_id, msg, err)
|
||||
)
|
||||
handlePackageId(package_id)
|
||||
|
||||
@staticmethod
|
||||
def _verifyMd5Hash(file_path: str, known_hash: str) -> bool:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class GcodeStartEndFormatter:
|
|||
# will be used. Alternatively, if the expression is formatted as "{[expression], [extruder_nr]}",
|
||||
# then the expression will be evaluated with the extruder stack of the specified extruder_nr.
|
||||
|
||||
_instruction_regex = re.compile(r"{(?P<condition>if|else|elif|endif)?\s*(?P<expression>.*?)\s*(?:,\s*(?P<extruder_nr_expr>.*))?\s*}(?P<end_of_line>\n?)")
|
||||
_instruction_regex = re.compile(r"{(?P<condition>if|else|elif|endif)?\s*(?P<expression>[^{}]*?)\s*(?:,\s*(?P<extruder_nr_expr>[^{}]*))?\s*}(?P<end_of_line>\n?)")
|
||||
|
||||
def __init__(self, all_extruder_settings: Dict[str, Dict[str, Any]], default_extruder_nr: int = -1) -> None:
|
||||
super().__init__()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class GCodeGzWriter(MeshWriter):
|
|||
def __init__(self) -> None:
|
||||
super().__init__(add_to_recent_files = False)
|
||||
|
||||
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool:
|
||||
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode, **kwargs) -> bool:
|
||||
"""Writes the gzipped g-code to a stream.
|
||||
|
||||
Note that even though the function accepts a collection of nodes, the
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class GCodeWriter(MeshWriter):
|
|||
|
||||
self._application = Application.getInstance()
|
||||
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode):
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode, **kwargs):
|
||||
"""Writes the g-code for the entire scene to a stream.
|
||||
|
||||
Note that even though the function accepts a collection of nodes, the
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class MakerbotWriter(MeshWriter):
|
|||
|
||||
return None
|
||||
|
||||
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode=MeshWriter.OutputMode.BinaryMode) -> bool:
|
||||
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode=MeshWriter.OutputMode.BinaryMode, **kwargs) -> bool:
|
||||
metadata, file_format = self._getMeta(nodes)
|
||||
if mode != MeshWriter.OutputMode.BinaryMode:
|
||||
Logger.log("e", "MakerbotWriter does not support text mode.")
|
||||
|
|
|
|||
988
plugins/PostProcessingPlugin/scripts/PurgeLinesAndUnload.py
Normal file
|
|
@ -0,0 +1,988 @@
|
|||
# August 2024 - Designed by: GregValiant (Greg Foresi). Straightened out by: Hellaholic
|
||||
#
|
||||
# NOTE: You may have purge lines in your startup, or you may use this script, you should not do both. The script will attempt to comment out existing StartUp purge lines.
|
||||
# 'Add Purge Lines to StartUp' Allows the user to determine where the purge lines are on the build plate, or to not use purge lines if a print extends to the limits of the build surface.
|
||||
# This script will attempt to recognize and comment out purge lines in the StartUp Gcode but they should be removed if using this script.
|
||||
# The setting 'Purge Line Length' is only avaialble for rectangular beds because I was too lazy to calculate the 45° arcs.
|
||||
# 'Move to Start' takes an orthogonal path around the periphery before moving in to the print start location. It eliminates strings across the print area.
|
||||
# 'Adjust Starting E' is a correction in the E location before the skirt/brim starts. The user can make an adjustment so that the skirt / brim / raft starts where it should.
|
||||
# 'Unload' adds code to the Ending Gcode that will unload the filament from the machine. The unlaod distance is broken into chunks to avoid overly long E distances.
|
||||
# Added extra moves to account for Cura adding a "Travel to Prime Tower" move that can cross the middle of the build surface.
|
||||
# Added ability to take 'disallowed areas' into account.
|
||||
|
||||
import math
|
||||
from ..Script import Script
|
||||
from UM.Application import Application
|
||||
from UM.Message import Message
|
||||
import re
|
||||
from UM.Logger import Logger
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Location(str, Enum):
|
||||
LEFT = "left"
|
||||
RIGHT = "right"
|
||||
REAR = "rear"
|
||||
FRONT = "front"
|
||||
|
||||
|
||||
class Position(tuple, Enum):
|
||||
LEFT_FRONT = ("left", "front")
|
||||
RIGHT_FRONT = ("right", "front")
|
||||
LEFT_REAR = ("left", "rear")
|
||||
RIGHT_REAR = ("right", "rear")
|
||||
|
||||
|
||||
class PurgeLinesAndUnload(Script):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
self.extruder = self.global_stack.extruderList
|
||||
self.end_purge_location = None
|
||||
self.speed_travel = None
|
||||
# This will be True when there are more than 4 'machine_disallowed_areas'
|
||||
self.show_warning = False
|
||||
self.disallowed_areas = self.global_stack.getProperty("machine_disallowed_areas", "value")
|
||||
self.extruder = self.global_stack.extruderList
|
||||
self.extruder_count = self.global_stack.getProperty("machine_extruder_count", "value")
|
||||
self.bed_shape = self.global_stack.getProperty("machine_shape", "value")
|
||||
self.origin_at_center = self.global_stack.getProperty("machine_center_is_zero", "value")
|
||||
self.machine_width = self.global_stack.getProperty("machine_width", "value")
|
||||
self.machine_depth = self.global_stack.getProperty("machine_depth", "value")
|
||||
self.machine_left = 1.0
|
||||
self.machine_right = self.machine_width - 1.0
|
||||
self.machine_front = 1.0
|
||||
self.machine_back = self.machine_depth - 1.0
|
||||
self.start_x = None
|
||||
self.start_y = None
|
||||
|
||||
def initialize(self) -> None:
|
||||
super().initialize()
|
||||
# Get the StartUp Gcode from Cura and attempt to catch if it contains purge lines. Message the user if an extrusion is in the startup.
|
||||
startup_gcode = self.global_stack.getProperty("machine_start_gcode", "value")
|
||||
start_lines = startup_gcode.splitlines()
|
||||
for line in start_lines:
|
||||
if "G1" in line and " E" in line and (" X" in line or " Y" in line):
|
||||
Message(title="[Purge Lines and Unload]",
|
||||
text="It appears that there are 'purge lines' in the StartUp Gcode. Using the 'Add Purge Lines' function of this script will comment them out.").show()
|
||||
break
|
||||
# 'is_rectangular' is used to disable half-length purge lines for elliptic beds.
|
||||
self._instance.setProperty("is_rectangular", "value", True if self.global_stack.getProperty("machine_shape", "value") == "rectangular" else False)
|
||||
self._instance.setProperty("move_to_prime_tower", "value", True if self.global_stack.getProperty("machine_extruder_count", "value") > 1 else False)
|
||||
# Set the default E adjustment
|
||||
self._instance.setProperty("adjust_e_loc_to", "value", -abs(round(float(self.extruder[0].getProperty("retraction_amount", "value")), 1)))
|
||||
|
||||
def getSettingDataString(self):
|
||||
return """{
|
||||
"name": "Purge Lines and Unload Filament",
|
||||
"key": "PurgeLinesAndUnload",
|
||||
"metadata": {},
|
||||
"version": 2,
|
||||
"settings":
|
||||
{
|
||||
"add_purge_lines":
|
||||
{
|
||||
"label": "Add Purge Lines to StartUp",
|
||||
"description": "The purge lines can be left, right, front or back. If there are purge lines present in the StartUp Gcode remove them or comment them out before using this script. You don't want to double dip.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": false,
|
||||
"enabled": true
|
||||
},
|
||||
"purge_line_location":
|
||||
{
|
||||
"label": " Purge Line Location",
|
||||
"description": "What edge of the build plate should have the purge lines. If the printer is 'Elliptical' then it is assumed to be an 'Origin At Center' printer and the purge lines are 90° arcs.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"left": "On left edge (Xmin)",
|
||||
"right": "On right edge (Xmax)",
|
||||
"front": "On front edge (Ymin)",
|
||||
"rear": "On back edge (Ymax)"},
|
||||
"default_value": "left",
|
||||
"enabled": "add_purge_lines"
|
||||
},
|
||||
"purge_line_length":
|
||||
{
|
||||
"label": " Purge Line Length",
|
||||
"description": "Select 'Full' for the entire Height or Width of the build plate. Select 'Half' for shorter purge lines. NOTE: This has no effect on elliptic beds.",
|
||||
"type": "enum",
|
||||
"options": {
|
||||
"purge_full": "Full",
|
||||
"purge_half": "Half"},
|
||||
"default_value": "purge_full",
|
||||
"enabled": "add_purge_lines and is_rectangular"
|
||||
},
|
||||
"border_distance":
|
||||
{
|
||||
"label": " Border Distance",
|
||||
"description": "This is the distance from the build plate edge to the first purge line. '0' works for most printers but you might want the lines further inboard. The allowable range is -12 to 12. ⚠️ Negative numbers are allowed for printers that have 'Disallowed Areas'. You must use due caution when using a negative value.",
|
||||
"type": "int",
|
||||
"unit": "mm ",
|
||||
"default_value": 0,
|
||||
"minimum_value": -12,
|
||||
"maximum_value": 12,
|
||||
"enabled": "add_purge_lines"
|
||||
},
|
||||
"prime_blob_enable":
|
||||
{
|
||||
"label": " Start with Prime Blob️",
|
||||
"description": "Enable a stationary purge before starting the purge lines. Available only when purge line location is 'left' or 'front'",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "add_purge_lines and purge_line_location in ['front', 'left']"
|
||||
},
|
||||
"prime_blob_distance":
|
||||
{
|
||||
"label": " Blob Distance️",
|
||||
"description": "How many mm's of filament should be extruded for the blob.",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"unit": "mm ",
|
||||
"enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']"
|
||||
},
|
||||
"prime_blob_loc_x":
|
||||
{
|
||||
"label": " Blob Location X",
|
||||
"description": "The 'X' position to put the prime blob. 'Origin at Center' printers might require a negative value here. Keep in mind that purge lines always start in the left front, or the right rear. Pay attention or the nozzle can sit down into the prime blob.",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"unit": "mm ",
|
||||
"enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']"
|
||||
},
|
||||
"prime_blob_loc_y":
|
||||
{
|
||||
"label": " Blob location Y",
|
||||
"description": "The 'Y' position to put the prime blob. 'Origin at Center' printers might require a negative value here. Keep in mind that purge lines always start in the left front, or the right rear. Pay attention or the nozzle can sit down into the prime blob.",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"unit": "mm ",
|
||||
"enabled": "add_purge_lines and prime_blob_enable and purge_line_location in ['front', 'left']"
|
||||
},
|
||||
"move_to_start":
|
||||
{
|
||||
"label": "Circle around to layer start ⚠️",
|
||||
"description": "Depending on where the 'Layer Start X' and 'Layer Start Y' are for the print, the opening travel move can pass across the print area and leave a string there. This option will generate an orthogonal path that moves the nozzle around the edges of the build plate and then comes in to the Start Point. || ⚠️ || The nozzle can drop to Z0.0 and touch the build plate at each stop in order to 'nail down the string'. The nozzle always raises after the touch-down. It will not drag on the bed.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": true
|
||||
},
|
||||
"move_to_start_min_z":
|
||||
{
|
||||
"label": " Minimum Z height ⚠️",
|
||||
"description": "When moving to the start position, the nozzle can touch down on the build plate at each stop (Z = 0.0). That will stick the string to the build plate at each direction change so it doesn't pull across the print area. Some printers may not respond well to Z=0.0. You may set a minimum Z height here (min is 0.0 and max is 0.50). The string must stick or it defeats the purpose of moving around the periphery.",
|
||||
"type": "float",
|
||||
"default_value": 0.0,
|
||||
"minimum_value": 0.0,
|
||||
"maximum_value": 0.5,
|
||||
"enabled": "move_to_start"
|
||||
},
|
||||
"adjust_starting_e":
|
||||
{
|
||||
"label": "Adjust Starting E location",
|
||||
"description": "If there is a retraction after the purge lines in the Startup Gcode (like the 'Add Purge Lines' script here does) then often the skirt does not start where the nozzle starts. It is because Cura always adds a retraction prior to the print starting which results in a double retraction. Enabling this will allow you to adjust the starting E location and tune it so the skirt/brim/model starts right where it should. To fix a blob enter a positive number. To fix a 'dry start' enter a negative number.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"value": false,
|
||||
"enabled": true
|
||||
},
|
||||
"adjust_e_loc_to":
|
||||
{
|
||||
"label": " Starting E location",
|
||||
"description": "This is usually a negative amount and often equal to the '-Retraction Distance'. This 'G92 E' adjustment changes where the printer 'thinks' the end of the filament is in relation to the nozzle. It replaces the retraction that Cura adds prior to the start of 'LAYER:0'. If retraction is not enabled then this setting has no effect.",
|
||||
"type": "float",
|
||||
"unit": "mm ",
|
||||
"default_value": -6.5,
|
||||
"enabled": "adjust_starting_e"
|
||||
},
|
||||
"enable_unload":
|
||||
{
|
||||
"label": "Unload filament at print end",
|
||||
"description": "Adds an unload script to the Ending Gcode section. It goes in just ahead of the M104 S0. This scripts always unloads the active extruder. If the unload distance is greater than 150mm it will be broken into chunks to avoid tripping the excessive extrusion warning in some firmware.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": true
|
||||
},
|
||||
"unload_distance":
|
||||
{
|
||||
"label": " Unload Distance",
|
||||
"description": "The amount of filament to unload. Bowden printers usually require a significant amount and direct drives not as much.",
|
||||
"type": "int",
|
||||
"default_value": 440,
|
||||
"unit": "mm ",
|
||||
"enabled": "enable_unload"
|
||||
},
|
||||
"unload_quick_purge":
|
||||
{
|
||||
"label": " Quick purge before unload",
|
||||
"description": "When printing something fine that has a lot of retractions in a short space (like lettering or spires) right before the unload, the filament can get hung up in the hot end and unload can fail. A quick purge will soften the end of the filament so it will retract correctly. This 'quick purge' will take place at the last position of the nozzle.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": "enable_unload"
|
||||
},
|
||||
"move_to_prime_tower":
|
||||
{
|
||||
"label": "Hidden setting",
|
||||
"description": "Hidden setting that enables 'move_to_prime_tower' for multi extruder machines.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": false
|
||||
},
|
||||
"is_rectangular":
|
||||
{
|
||||
"label": "Bed is rectangular",
|
||||
"description": "Hidden setting that disables 'purge line length' for elliptical beds.",
|
||||
"type": "bool",
|
||||
"default_value": false,
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}"""
|
||||
|
||||
def execute(self, data):
|
||||
# Exit if the Gcode has already been processed.
|
||||
for num in range(0, len(data)):
|
||||
layer = data[num].split("\n")
|
||||
for line in layer:
|
||||
if ";LAYER:" in line:
|
||||
break
|
||||
elif "PurgeLinesAndUnload" in line:
|
||||
Logger.log("i", "[Add Purge Lines and Unload Filament] has already run on this gcode.")
|
||||
return data
|
||||
# The function also retrieves extruder settings used later in the script
|
||||
# 't0_has_offsets' is used to exit 'Add Purge Lines' and 'Circle around...' because the script is not compatible with machines with the right nozzle as the primary nozzle.
|
||||
self.t0_has_offsets = False
|
||||
self.init_ext_nr = self._get_initial_tool()
|
||||
# Adjust the usable size of the bed per any 'disallowed areas'
|
||||
self._get_build_plate_extents()
|
||||
# The start location changes according to which quadrant the nozzle is in at the beginning
|
||||
self.end_purge_location = self._get_real_start_point(data[1])
|
||||
self.border_distance = self.getSettingValueByKey("border_distance")
|
||||
self.prime_blob_enable = self.getSettingValueByKey("prime_blob_enable")
|
||||
if self.prime_blob_enable:
|
||||
self.prime_blob_distance = self.getSettingValueByKey("prime_blob_distance")
|
||||
else:
|
||||
self.prime_blob_distance = 0
|
||||
# Set the minimum Z to stick the string to the build plate when Move to Start is selected.
|
||||
self.touchdown_z = self.getSettingValueByKey("move_to_start_min_z")
|
||||
|
||||
# Mapping settings to corresponding methods
|
||||
procedures = {
|
||||
"add_purge_lines": self._add_purge_lines,
|
||||
"move_to_prime_tower": self._move_to_prime_tower,
|
||||
"move_to_start": self._move_to_start,
|
||||
"adjust_starting_e": self._adjust_starting_e,
|
||||
"enable_unload": self._unload_filament
|
||||
}
|
||||
# Run selected procedures
|
||||
for setting, method in procedures.items():
|
||||
if self.getSettingValueByKey(setting):
|
||||
method(data)
|
||||
# Format the startup and ending gcodes
|
||||
data[1] = self._format_string(data[1])
|
||||
data[-1] = self._format_string(data[-1])
|
||||
if self.getSettingValueByKey("add_purge_lines"):
|
||||
if self.show_warning:
|
||||
msg_text = ("The printer has ( " + str(len(self.disallowed_areas))
|
||||
+ " ) 'disallowed areas'. That can cause the area available for the purge lines to be small.\nOpen the Gcode file for preview in Cura and check the purge line location to insure it is acceptable.")
|
||||
else:
|
||||
msg_text = "Open the Gcode file for preview in Cura. Make sure the 'Purge Lines' don't run underneath something else and are acceptable."
|
||||
Message(title="[Purge Lines and Unload]", text=msg_text).show()
|
||||
return data
|
||||
|
||||
def _get_real_start_point(self, first_section: str) -> tuple:
|
||||
last_x, last_y = 0.0, 0.0
|
||||
start_quadrant = Position.LEFT_FRONT
|
||||
|
||||
for line in first_section.split("\n"):
|
||||
if line.startswith(";") and not line.startswith(";LAYER_COUNT") or not line:
|
||||
continue
|
||||
|
||||
if line.startswith("G28"):
|
||||
last_x, last_y = 0.0, 0.0
|
||||
elif line[:3] in {"G0 ", "G1 "}:
|
||||
last_x = self.getValue(line, "X") if " X" in line else last_x
|
||||
last_y = self.getValue(line, "Y") if " Y" in line else last_y
|
||||
elif "LAYER_COUNT" in line:
|
||||
break
|
||||
|
||||
midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else (
|
||||
self.machine_width / 2, self.machine_depth / 2)
|
||||
|
||||
if last_x <= midpoint_x and last_y <= midpoint_y:
|
||||
start_quadrant = Position.LEFT_FRONT
|
||||
elif last_x > midpoint_x and last_y <= midpoint_y:
|
||||
start_quadrant = Position.RIGHT_FRONT
|
||||
elif last_x > midpoint_x and last_y > midpoint_y:
|
||||
start_quadrant = Position.RIGHT_REAR
|
||||
elif last_x <= midpoint_x and last_y > midpoint_y:
|
||||
start_quadrant = Position.LEFT_REAR
|
||||
|
||||
return start_quadrant
|
||||
|
||||
"""
|
||||
For some multi-extruder printers.
|
||||
Takes into account a 'Move to Prime Tower' if there is one and adds orthogonal travel moves to get there.
|
||||
'Move to Prime Tower' does not require that the prime tower is enabled,
|
||||
only that 'machine_extruder_start_position_?' is in the definition file.
|
||||
"""
|
||||
|
||||
def _move_to_prime_tower(self, first_section: str) -> str:
|
||||
if self.extruder_count == 1:
|
||||
return first_section
|
||||
adjustment_lines = ""
|
||||
move_to_prime_present = False
|
||||
prime_tower_x = self.global_stack.getProperty("prime_tower_position_x", "value")
|
||||
prime_tower_y = self.global_stack.getProperty("prime_tower_position_y", "value")
|
||||
prime_tower_loc = self._prime_tower_quadrant(prime_tower_x, prime_tower_y)
|
||||
# Shortstop an error if Start Location comes through as None
|
||||
if self.end_purge_location is None:
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
if prime_tower_loc != self.end_purge_location:
|
||||
startup = first_section[1].split("\n")
|
||||
for index, line in enumerate(startup):
|
||||
if ";LAYER_COUNT:" in line:
|
||||
try:
|
||||
if startup[index + 1].startswith("G0"):
|
||||
prime_move = startup[index + 1] + " ; Move to Prime Tower"
|
||||
adjustment_lines = self._move_to_location("Prime Tower", prime_tower_loc)
|
||||
startup[index + 1] = adjustment_lines + prime_move + "\n;---------------------[End of Prime Tower moves]\n" + startup[index]
|
||||
startup.pop(index)
|
||||
first_section[1] = "\n".join(startup)
|
||||
move_to_prime_present = True
|
||||
except IndexError:
|
||||
pass
|
||||
# The start_location changes to the prime tower location in case 'Move to Start' is enabled.
|
||||
if move_to_prime_present:
|
||||
self.end_purge_location = prime_tower_loc
|
||||
return first_section
|
||||
|
||||
# Determine the quadrant that the prime tower rests in so the orthogonal moves can be calculated
|
||||
def _prime_tower_quadrant(self, prime_tower_x: float, prime_tower_y: float) -> tuple:
|
||||
midpoint_x, midpoint_y = (0.0, 0.0) if self.origin_at_center else (
|
||||
self.machine_width / 2, self.machine_depth / 2)
|
||||
|
||||
if prime_tower_x < midpoint_x and prime_tower_y < midpoint_y:
|
||||
return Position.LEFT_FRONT
|
||||
elif prime_tower_x > midpoint_x and prime_tower_y < midpoint_y:
|
||||
return Position.RIGHT_FRONT
|
||||
elif prime_tower_x > midpoint_x and prime_tower_y > midpoint_y:
|
||||
return Position.RIGHT_REAR
|
||||
elif prime_tower_x < midpoint_x and prime_tower_y > midpoint_y:
|
||||
return Position.LEFT_REAR
|
||||
else:
|
||||
return Position.LEFT_FRONT # return Default in case of no match
|
||||
|
||||
def _move_to_location(self, location_name: str, location: tuple) -> str:
|
||||
"""
|
||||
Compare the input tuple (B) with the end purge location (A) and describe the move from A to B.
|
||||
Parameters:
|
||||
location_name (str): A descriptive name for the target location.
|
||||
location (tuple): The target tuple (e.g., ("right", "front")).
|
||||
Returns:
|
||||
str: G-code for the move from A to B or an empty string if no move is required.
|
||||
"""
|
||||
# Validate input
|
||||
if len(self.end_purge_location) != 2 or len(location) != 2:
|
||||
raise ValueError("Both locations must be tuples of length 2.")
|
||||
|
||||
# Extract components
|
||||
start_side, start_depth = self.end_purge_location
|
||||
target_side, target_depth = location
|
||||
# Start of the moves and a comment to highlight the move
|
||||
moves = [f";MESH:NONMESH---------[Circle around to {location_name}] Start from: {str(start_side)} {str(start_depth)} Go to: {target_side} {target_depth}\nG0 F600 Z2 ; Move up\n"]
|
||||
|
||||
# Helper function to add G-code for moves
|
||||
def add_move(axis: str, position: float) -> None:
|
||||
moves.append(
|
||||
f"G0 F{self.speed_travel} {axis}{position} ; Start move\n"
|
||||
f"G0 F600 Z{self.touchdown_z} ; Nail down the string\n"
|
||||
f"G0 F600 Z2 ; Move up\n"
|
||||
)
|
||||
|
||||
# Move to a corner
|
||||
if start_side == Location.LEFT:
|
||||
moves.append(f"G0 F{self.speed_travel} X{self.machine_left + 6} ; Init move\n")
|
||||
elif start_side == Location.RIGHT:
|
||||
moves.append(f"G0 F{self.speed_travel} X{self.machine_right - 6} ; Init move\n")
|
||||
if start_depth == Location.FRONT:
|
||||
add_move("Y", self.machine_front + 6)
|
||||
elif start_depth == Location.REAR:
|
||||
add_move("Y", self.machine_back - 6)
|
||||
# Compare sides
|
||||
if start_side != target_side:
|
||||
if target_side == Location.RIGHT:
|
||||
add_move("X", self.machine_right)
|
||||
else:
|
||||
add_move("X", self.machine_left)
|
||||
# Compare positions
|
||||
if start_depth != target_depth:
|
||||
if target_depth == Location.REAR:
|
||||
add_move("Y", self.machine_back)
|
||||
else:
|
||||
add_move("Y", self.machine_front)
|
||||
if len(moves) == 1:
|
||||
moves.append(f"G0 F{self.speed_travel} Y{self.start_y} ; Move to start Y\n")
|
||||
# Combine moves into a single G-code string and return
|
||||
return "".join(moves)
|
||||
|
||||
def _get_build_plate_extents(self):
|
||||
"""
|
||||
Machine disallowed areas can be ordered at the whim of the definition author and cannot be counted on when parsed
|
||||
This determines a simple rectangle that will be available for the purge lines. For some machines (Ex: UM3) it can be a small rectangle.
|
||||
If there are "extruder offsets" then use them to adjust the 'machine_right' and 'machine_back' independent of any disallowed areas.
|
||||
"""
|
||||
if self.bed_shape == "rectangular":
|
||||
if self.disallowed_areas:
|
||||
if len(self.disallowed_areas) > 4:
|
||||
self.show_warning = True
|
||||
mid_x = 0
|
||||
mid_y = 0
|
||||
left_x = -(self.machine_width / 2)
|
||||
right_x = (self.machine_width / 2)
|
||||
front_y = (self.machine_depth / 2)
|
||||
back_y = -(self.machine_depth / 2)
|
||||
for rect in self.disallowed_areas:
|
||||
for corner in rect:
|
||||
x = corner[0]
|
||||
if mid_x > x > left_x:
|
||||
left_x = x
|
||||
if mid_x < x < right_x:
|
||||
right_x = x
|
||||
y = corner[1]
|
||||
if mid_y < y < front_y:
|
||||
front_y = y
|
||||
if mid_y > y > back_y:
|
||||
back_y = y
|
||||
if self.origin_at_center:
|
||||
self.machine_left = round(left_x, 2)
|
||||
self.machine_right = round(right_x, 2)
|
||||
self.machine_front = round(front_y, 2)
|
||||
self.machine_back = round(back_y, 2)
|
||||
else:
|
||||
self.machine_left = round(left_x + self.machine_width / 2, 2)
|
||||
self.machine_right = round(right_x + self.machine_width / 2, 2)
|
||||
self.machine_front = round((self.machine_depth / 2) - front_y, 2)
|
||||
self.machine_back = round((self.machine_depth / 2) - back_y, 2)
|
||||
else:
|
||||
if self.origin_at_center:
|
||||
self.machine_left = round(-(self.machine_width / 2), 2)
|
||||
self.machine_right = round((self.machine_width / 2) - self.nozzle_offset_x, 2)
|
||||
self.machine_front = round(-(self.machine_depth / 2) + self.nozzle_offset_y, 2)
|
||||
self.machine_back = round((self.machine_depth / 2) - self.nozzle_offset_y, 2)
|
||||
else:
|
||||
self.machine_left = 0
|
||||
self.machine_right = self.machine_width - self.nozzle_offset_x
|
||||
if self.nozzle_offset_y >= 0:
|
||||
self.machine_front = 0
|
||||
self.machine_back = self.machine_depth - self.nozzle_offset_y
|
||||
elif self.nozzle_offset_y < 0:
|
||||
self.machine_front = abs(self.nozzle_offset_y)
|
||||
self.machine_back = self.machine_depth
|
||||
return
|
||||
|
||||
# Add Purge Lines to the user defined position on the build plate
|
||||
def _add_purge_lines(self, data: str):
|
||||
if self.t0_has_offsets:
|
||||
data[0] += "; [Purge Lines and Unload] 'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets.\n"
|
||||
Message(title = "[Purge Lines and Unload]", text = "'Add Purge Lines' did not run because the assumed primary nozzle (T0) has tool offsets").show()
|
||||
return data
|
||||
|
||||
def calculate_purge_volume(line_width, purge_length, volume_per_mm):
|
||||
return round((line_width * 0.3 * purge_length) * 1.25 / volume_per_mm, 5)
|
||||
|
||||
def adjust_for_prime_blob_gcode(retract_speed, retract_distance):
|
||||
"""Generates G-code lines for prime blob adjustment."""
|
||||
gcode_lines = [
|
||||
f"G1 F{retract_speed} E{retract_distance} ; Unretract",
|
||||
"G92 E0 ; Reset extruder\n"
|
||||
]
|
||||
return "\n".join(gcode_lines)
|
||||
|
||||
purge_location = self.getSettingValueByKey("purge_line_location")
|
||||
purge_extrusion_full = True if self.getSettingValueByKey("purge_line_length") == "purge_full" else False
|
||||
purge_str = ";TYPE:CUSTOM----------[Purge Lines]\nG0 F600 Z2 ; Move up\nG92 E0 ; Reset extruder\n"
|
||||
purge_str += self._get_blob_code()
|
||||
# Normal cartesian printer with origin at the left front corner
|
||||
if self.bed_shape == "rectangular" and not self.origin_at_center:
|
||||
if purge_location == Location.LEFT:
|
||||
purge_len = int(self.machine_back - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2)
|
||||
y_stop = int(self.machine_back - 10) if purge_extrusion_full else int(self.machine_depth / 2)
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
purge_str = purge_str.replace("Lines", "Lines at MinX")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + self.border_distance} Y{y_stop} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{y_stop} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
purge_len = int(self.machine_depth - 20) if purge_extrusion_full else int((self.machine_back - self.machine_front) / 2)
|
||||
y_stop = int(self.machine_front + 10) if purge_extrusion_full else int(self.machine_depth / 2)
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
purge_str = purge_str.replace("Lines", "Lines at MaxX")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} ; Move\nG0 Y{self.machine_back - 10} ; Move\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - self.border_distance} Y{y_stop} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{y_stop} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
purge_len = int(self.machine_width) - self.nozzle_offset_x - 20 if purge_extrusion_full else int(
|
||||
(self.machine_right - self.machine_left) / 2)
|
||||
x_stop = int(self.machine_right - 10) if purge_extrusion_full else int(self.machine_width / 2)
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
purge_str = purge_str.replace("Lines", "Lines at MinY")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Y{self.machine_front + self.border_distance} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front + self.border_distance} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{x_stop} Y{self.machine_front + 3 + self.border_distance} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3 + self.border_distance} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
purge_len = int(self.machine_width - 20) if purge_extrusion_full else int(
|
||||
(self.machine_right - self.machine_left) / 2)
|
||||
x_stop = int(self.machine_left + 10) if purge_extrusion_full else int(self.machine_width / 2)
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
purge_str = purge_str.replace("Lines", "Lines at MaxY")
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} ; Ortho Move to back\n"
|
||||
purge_str += f"G0 X{self.machine_right - 10} ; Ortho move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back - self.border_distance} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{x_stop} Y{self.machine_back - 3 - self.border_distance} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3 - self.border_distance} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
# Some cartesian printers (BIBO, Weedo, MethodX, etc.) are Origin at Center
|
||||
elif self.bed_shape == "rectangular" and self.origin_at_center:
|
||||
if purge_location == Location.LEFT:
|
||||
purge_len = int(self.machine_back - self.machine_front - 20) if purge_extrusion_full else abs(
|
||||
int(self.machine_front - 10))
|
||||
y_stop = int(self.machine_back - 10) if purge_extrusion_full else 0
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + self.border_distance} Y{self.machine_front + 10} ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + self.border_distance} Y{y_stop} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{y_stop} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 10} E{round(purge_volume * 2, 5)} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 3 + self.border_distance} Y{self.machine_front + 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
purge_len = int(self.machine_back - 20) if purge_extrusion_full else int(
|
||||
(self.machine_back - self.machine_front) / 2)
|
||||
y_stop = int(self.machine_front + 10) if purge_extrusion_full else 0
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_right - self.border_distance} Z2 ; Move\nG0 Y{self.machine_back - 10} Z2 ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - self.border_distance} Y{y_stop} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{y_stop} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 10} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 20} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 3 - self.border_distance} Y{self.machine_back - 35} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else int(
|
||||
(self.machine_right - self.machine_left) / 2)
|
||||
x_stop = int(self.machine_right - 10) if purge_extrusion_full else 0
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{self.machine_left + 10} Z2 ; Move\nG0 Y{self.machine_front + self.border_distance} Z2 ; Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_front + self.border_distance} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{x_stop} Y{self.machine_front + 3 + self.border_distance} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_left + 10} Y{self.machine_front + 3 + self.border_distance} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_left + 20} Y{self.machine_front + 3 + self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_left + 35} Y{self.machine_front + 3 + self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
purge_len = int(self.machine_right - self.machine_left - 20) if purge_extrusion_full else abs(
|
||||
int(self.machine_right - 10))
|
||||
x_stop = int(self.machine_left + 10) if purge_extrusion_full else 0
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} Y{self.machine_back - self.border_distance} Z2; Ortho Move to back\n"
|
||||
purge_str += f"G0 X{self.machine_right - 10} Z2 ; Ortho Move to start\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
if self.prime_blob_enable:
|
||||
purge_str += adjust_for_prime_blob_gcode(self.retract_speed, self.retract_dist)
|
||||
# Purge two lines
|
||||
purge_str += f"G1 F{self.print_speed} X{x_stop} Y{self.machine_back - self.border_distance} E{purge_volume} ; First line\n"
|
||||
purge_str += f"G0 X{x_stop} Y{self.machine_back - 3 - self.border_distance} ; Move over\n"
|
||||
purge_str += f"G1 F{self.print_speed} X{self.machine_right - 10} Y{self.machine_back - 3 - self.border_distance} E{purge_volume * 2} ; Second line\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round(purge_volume * 2 - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z8 ; Move Up\nG4 S1 ; Wait for 1 second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{self.machine_right - 20} Y{self.machine_back - 3 - self.border_distance} Z0.3 ; Slide over and down\n"
|
||||
purge_str += f"G0 X{self.machine_right - 35} Y{self.machine_back - 3 - self.border_distance} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
# Elliptic printers with Origin at Center
|
||||
elif self.bed_shape == "elliptic":
|
||||
if purge_location in [Location.LEFT, Location.RIGHT]:
|
||||
radius_1 = round((self.machine_width / 2) - 1, 2)
|
||||
else: # For purge_location in [Location.FRONT, Location.REAR]
|
||||
radius_1 = round((self.machine_depth / 2) - 1, 2)
|
||||
purge_len = int(radius_1) * math.pi / 4
|
||||
purge_volume = calculate_purge_volume(self.init_line_width, purge_len, self.mm3_per_mm)
|
||||
if purge_location == Location.LEFT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G2 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
purge_str += f"G3 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n"
|
||||
purge_str += f"G1 X-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.RIGHT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
purge_str += f"G2 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n"
|
||||
purge_str += f"G1 X{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
elif purge_location == Location.FRONT:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X-{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X{round(radius_1 * .707, 2)} Y-{round(radius_1 * .707, 2)} I{round(radius_1 * .707, 2)} J{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
purge_str += f"G2 F{self.print_speed} X-{round((radius_1 - 3) * .707, 2)} Y-{round((radius_1 - 3) * .707, 2)} I-{round((radius_1 - 3) * .707, 2)} J{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n"
|
||||
purge_str += f"G1 Y-{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5 ; Move Up\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y-{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
elif purge_location == Location.REAR:
|
||||
# Travel to the purge start
|
||||
purge_str += f"G0 F{self.speed_travel} X{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} ; Travel\n"
|
||||
purge_str += f"G0 F600 Z0.3 ; Move down\n"
|
||||
# Purge two arcs
|
||||
purge_str += f"G3 F{self.print_speed} X-{round(radius_1 * .707, 2)} Y{round(radius_1 * .707, 2)} I-{round(radius_1 * .707, 2)} J-{round(radius_1 * .707, 2)} E{purge_volume} ; First Arc\n"
|
||||
purge_str += f"G0 X-{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} ; Move Over\n"
|
||||
purge_str += f"G2 F{self.print_speed} X{round((radius_1 - 3) * .707, 2)} Y{round((radius_1 - 3) * .707, 2)} I{round((radius_1 - 3) * .707, 2)} J-{round((radius_1 - 3) * .707, 2)} E{purge_volume * 2} ; Second Arc\n"
|
||||
purge_str += f"G1 Y{round((radius_1 - 3) * .707 - 25, 2)} E{round(purge_volume * 2 + 1, 5)} ; Move Over\n"
|
||||
# Retract if enabled
|
||||
purge_str += f"G1 F{int(self.retract_speed)} E{round((purge_volume * 2 + 1) - self.retract_dist, 5)} ; Retract\n" if self.retraction_enable else ""
|
||||
purge_str += "G0 F600 Z5\nG4 S1 ; Wait 1 Second\n"
|
||||
# Wipe
|
||||
purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707 - 15, 2)} Z0.3 ; Slide Over\n"
|
||||
purge_str += f"G0 F{self.print_speed} Y{round((radius_1 - 3) * .707, 2)} ; Wipe\n"
|
||||
self.end_purge_location = Position.RIGHT_REAR
|
||||
|
||||
# Common ending for purge_str
|
||||
purge_str += "G0 F600 Z2 ; Move Z\n;---------------------[End of Purge]"
|
||||
|
||||
# Comment out any existing purge lines in data
|
||||
startup = data[1].split("\n")
|
||||
for index, line in enumerate(startup):
|
||||
if "G1" in line and " E" in line and (" X" in line or " Y" in line):
|
||||
next_line = index
|
||||
try:
|
||||
while not startup[next_line].startswith("G92 E0"):
|
||||
startup[next_line] = ";" + startup[next_line]
|
||||
next_line += 1
|
||||
except IndexError:
|
||||
break
|
||||
data[1] = "\n".join(startup)
|
||||
|
||||
# Find the insertion location in data
|
||||
purge_str = self._format_string(purge_str)
|
||||
startup_section = data[1].split("\n")
|
||||
insert_index = len(startup_section) - 1
|
||||
for num in range(len(startup_section) - 1, 0, -1):
|
||||
# In Absolute Extrusion mode - insert above the last G92 E0 line
|
||||
if "G92 E0" in startup_section[num]:
|
||||
insert_index = num
|
||||
break
|
||||
# In Relative Extrusion mode - insert above the M83 line
|
||||
elif "M83" in startup_section[num]:
|
||||
insert_index = num
|
||||
break
|
||||
startup_section.insert(insert_index, purge_str)
|
||||
data[1] = "\n".join(startup_section)
|
||||
return data
|
||||
|
||||
# Travel moves around the bed periphery to keep strings from crossing the footprint of the model.
|
||||
def _move_to_start(self, data: str) -> str:
|
||||
if self.t0_has_offsets:
|
||||
data[0] += "; [Purge Lines and Unload] 'Circle Around to Layer Start' did not run because the assumed primary nozzle (T0) has tool offsets.\n"
|
||||
Message(title = "[Purge Lines and Unload]", text = "'Circle Around to Layer Start' did not run because the assumed primary nozzle (T0) has tool offsets.").show()
|
||||
return data
|
||||
self.start_x = None
|
||||
self.start_y = None
|
||||
move_str = None
|
||||
layer = data[2].split("\n")
|
||||
for line in layer:
|
||||
if line.startswith("G0") and " X" in line and " Y" in line:
|
||||
self.start_x = self.getValue(line, "X")
|
||||
self.start_y = self.getValue(line, "Y")
|
||||
break
|
||||
self.start_x = self.start_x or 0
|
||||
self.start_y = self.start_y or 0
|
||||
if self.end_purge_location is None:
|
||||
self.end_purge_location = Position.LEFT_FRONT
|
||||
midpoint_x = self.machine_width / 2
|
||||
midpoint_y = self.machine_depth / 2
|
||||
if not self.origin_at_center:
|
||||
if float(self.start_x) <= float(midpoint_x):
|
||||
x_target = Location.LEFT
|
||||
else:
|
||||
x_target = Location.RIGHT
|
||||
if float(self.start_y) <= float(midpoint_y):
|
||||
y_target = Location.FRONT
|
||||
else:
|
||||
y_target = Location.REAR
|
||||
else:
|
||||
if float(self.start_x) <= 0:
|
||||
x_target = Location.LEFT
|
||||
else:
|
||||
x_target = Location.RIGHT
|
||||
if float(self.start_y) <= 0:
|
||||
y_target = Location.FRONT
|
||||
else:
|
||||
y_target = Location.REAR
|
||||
target_location = (x_target, y_target)
|
||||
if self.bed_shape == "rectangular":
|
||||
move_str = self._move_to_location("Layer Start", target_location)
|
||||
elif self.bed_shape == "elliptic" and self.origin_at_center:
|
||||
move_str = f";MESH:NONMESH---------[Travel to Layer Start]\nG0 F600 Z2 ; Move up\n"
|
||||
radius = self.machine_width / 2
|
||||
offset_sin = round(2 ** .5 / 2 * radius, 2)
|
||||
if target_location == Position.LEFT_FRONT:
|
||||
move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Move\nG0 Y-{offset_sin} Z2 ; Move to start\n"
|
||||
elif target_location == Position.LEFT_REAR:
|
||||
if self.end_purge_location == Position.LEFT_REAR:
|
||||
move_str += f"G2 X0 Y{offset_sin} I{offset_sin} J{offset_sin} ; Move around to start\n"
|
||||
else:
|
||||
move_str += f"G0 F{self.speed_travel} X-{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n"
|
||||
elif target_location == Position.RIGHT_FRONT:
|
||||
move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y-{offset_sin} Z2 ; Ortho move\n"
|
||||
elif target_location == Position.RIGHT_REAR:
|
||||
move_str += f"G0 F{self.speed_travel} X{offset_sin} Z2 ; Ortho move\nG0 Y{offset_sin} Z2 ; Ortho move\n"
|
||||
move_str += ";---------------------[End of layer start travels]"
|
||||
# Add the move_str to the end of the StartUp section and move 'LAYER_COUNT' to the end.
|
||||
startup = data[1].split("\n")
|
||||
move_str = self._format_string(move_str)
|
||||
if move_str.startswith("\n"):
|
||||
move_str = move_str[1:]
|
||||
startup.append(move_str)
|
||||
# Move the 'LAYER_COUNT' line so it's at the end of data[1]
|
||||
for index, line in enumerate(startup):
|
||||
if "LAYER_COUNT" in line:
|
||||
lay_count = startup.pop(index) + "\n"
|
||||
startup.append(lay_count)
|
||||
break
|
||||
|
||||
data[1] = "\n".join(startup)
|
||||
# Remove any double-spaced lines
|
||||
data[1] = data[1].replace("\n\n", "\n")
|
||||
return data
|
||||
|
||||
# Unloading a large amount of filament in a single command can trip the 'Overlong Extrusion' warning in some firmware. Unloads longer than 150mm are split into individual 150mm segments.
|
||||
def _unload_filament(self, data: str) -> str:
|
||||
extrude_speed = 3000
|
||||
quick_purge_speed = round(float(self.nozzle_size) * 500)
|
||||
if self.material_diameter > 2: quick_purge_speed *= .38 # Adjustment for 2.85 filament
|
||||
retract_amount = self.extruder[0].getProperty("retraction_amount", "value")
|
||||
quick_purge_amount = retract_amount + 5 if retract_amount < 2.0 else retract_amount * 2
|
||||
unload_distance = self.getSettingValueByKey("unload_distance")
|
||||
quick_purge = self.getSettingValueByKey("unload_quick_purge")
|
||||
lines = data[-1].split("\n")
|
||||
for index, line in enumerate(lines):
|
||||
# Unload the filament just before the hot end turns off.
|
||||
if line.startswith("M104") and "S0" in line:
|
||||
filament_str = (
|
||||
"M83 ; [Unload] Relative extrusion\n"
|
||||
"M400 ; Complete all moves\n"
|
||||
)
|
||||
if quick_purge:
|
||||
filament_str += f"G1 F{quick_purge_speed} E{quick_purge_amount} ; Quick Purge before unload\n"
|
||||
if unload_distance > 150:
|
||||
filament_str += "".join(
|
||||
f"G1 F{extrude_speed} E-150 ; Unload some\n"
|
||||
for _ in range(unload_distance // 150)
|
||||
)
|
||||
remaining_unload = unload_distance % 150
|
||||
if remaining_unload > 0:
|
||||
filament_str += f"G1 F{extrude_speed} E-{remaining_unload} ; Unload the remainder\n"
|
||||
else:
|
||||
filament_str += f"G1 F{extrude_speed} E-{unload_distance} ; Unload\n"
|
||||
filament_str += (
|
||||
"M82 ; Absolute Extrusion\n"
|
||||
"G92 E0 ; Reset Extruder\n"
|
||||
)
|
||||
lines[index] = filament_str + line
|
||||
break
|
||||
data[-1] = "\n".join(lines)
|
||||
return data
|
||||
|
||||
# Make an adjustment to the starting E location so the skirt/brim/raft starts out when the nozzle starts out.
|
||||
def _adjust_starting_e(self, data: str) -> str:
|
||||
if not self.extruder[0].getProperty("retraction_enable", "value"):
|
||||
return data
|
||||
adjust_amount = self.getSettingValueByKey("adjust_e_loc_to")
|
||||
lines = data[1].split("\n")
|
||||
lines.reverse()
|
||||
if self.global_stack.getProperty("machine_firmware_retract", "value"):
|
||||
search_pattern = r"G10"
|
||||
else:
|
||||
search_pattern = r"G1 F(\d*) E-(\d.*)"
|
||||
for index, line in enumerate(lines):
|
||||
if re.search(search_pattern, line):
|
||||
lines[index] = re.sub(search_pattern, f"G92 E{adjust_amount}", line)
|
||||
lines.reverse()
|
||||
data[1] = "\n".join(lines)
|
||||
break
|
||||
return data
|
||||
|
||||
# Format the purge or travel-to-start strings. No reason they shouldn't look nice.
|
||||
def _format_string(self, any_gcode_str: str):
|
||||
temp_lines = any_gcode_str.split("\n")
|
||||
gap_len = 0
|
||||
for temp_line in temp_lines:
|
||||
if ";" in temp_line and not temp_line.startswith(";"):
|
||||
if gap_len - len(temp_line.split(";")[0]) + 1 < 0:
|
||||
gap_len = len(temp_line.split(";")[0]) + 1
|
||||
if gap_len < 30: gap_len = 30
|
||||
for temp_index, temp_line in enumerate(temp_lines):
|
||||
if ";" in temp_line and not temp_line.startswith(";"):
|
||||
temp_lines[temp_index] = temp_line.replace(temp_line.split(";")[0], temp_line.split(";")[0] + str(
|
||||
" " * (gap_len - len(temp_line.split(";")[0]))), 1)
|
||||
# This formats lines that are commented out but contain additional comments Ex: ;M420 ; leveling mesh
|
||||
elif temp_line.startswith(";") and ";" in temp_line[1:]:
|
||||
temp_lines[temp_index] = temp_line[1:].replace(temp_line[1:].split(";")[0],
|
||||
";" + temp_line[1:].split(";")[0] + str(" " * (
|
||||
gap_len - 1 - len(
|
||||
temp_line[1:].split(";")[0]))), 1)
|
||||
any_gcode_str = "\n".join(temp_lines)
|
||||
return any_gcode_str
|
||||
|
||||
def _get_initial_tool(self) -> int:
|
||||
# Get the Initial Extruder
|
||||
num = Application.getInstance().getExtruderManager().getInitialExtruderNr()
|
||||
if num is None or num == -1:
|
||||
num = 0
|
||||
# If there is an extruder offset X then it will be used to adjust the "machine_right" and a Y offset will adjust the "machine_back"
|
||||
if self.extruder_count > 1 and bool(self.global_stack.getProperty("machine_use_extruder_offset_to_offset_coords", "value")):
|
||||
self.nozzle_offset_x = self.extruder[1].getProperty("machine_nozzle_offset_x", "value")
|
||||
self.nozzle_offset_y = self.extruder[1].getProperty("machine_nozzle_offset_y", "value")
|
||||
else:
|
||||
self.nozzle_offset_x = 0.0
|
||||
self.nozzle_offset_y = 0.0
|
||||
self.material_diameter = self.extruder[num].getProperty("material_diameter", "value")
|
||||
self.nozzle_size = self.extruder[num].getProperty("machine_nozzle_size", "value")
|
||||
self.init_line_width = self.extruder[num].getProperty("skirt_brim_line_width", "value")
|
||||
self.print_speed = round(self.extruder[num].getProperty("speed_print", "value") * 60 * .75)
|
||||
self.speed_travel = round(self.extruder[num].getProperty("speed_travel", "value") * 60)
|
||||
self.retract_dist = self.extruder[num].getProperty("retraction_amount", "value")
|
||||
self.retraction_enable = self.extruder[num].getProperty("retraction_enable", "value")
|
||||
self.retract_speed = self.extruder[num].getProperty("retraction_retract_speed", "value") * 60
|
||||
self.mm3_per_mm = (self.material_diameter / 2) ** 2 * math.pi
|
||||
# Don't add purge lines if 'T0' has offsets.
|
||||
t0_x_offset = self.extruder[0].getProperty("machine_nozzle_offset_x", "value")
|
||||
t0_y_offset = self.extruder[0].getProperty("machine_nozzle_offset_y", "value")
|
||||
if t0_x_offset or t0_y_offset:
|
||||
self.t0_has_offsets = True
|
||||
return num
|
||||
|
||||
def _get_blob_code(self) -> str:
|
||||
if not self.prime_blob_enable or self.prime_blob_distance == 0 or self.getSettingValueByKey("purge_line_location") not in ["front", "left"]:
|
||||
return ""
|
||||
# Set extruder speed for 1.75 filament
|
||||
speed_blob = round(float(self.nozzle_size) * 500)
|
||||
# Adjust speed if 2.85 filament
|
||||
if self.material_diameter > 2: speed_blob *= .4
|
||||
blob_x = self.getSettingValueByKey("prime_blob_loc_x")
|
||||
blob_y = self.getSettingValueByKey("prime_blob_loc_y")
|
||||
blob_string = "G0 F1200 Z20 ; Move up\n"
|
||||
blob_string += f"G0 F{self.speed_travel} X{blob_x} Y{blob_y} ; Move to blob location\n"
|
||||
blob_string += f"G1 F{speed_blob} E{self.prime_blob_distance} ; Blob\n"
|
||||
blob_string += f"G1 F{self.retract_speed} E{self.prime_blob_distance - self.retract_dist} ; Retract\n"
|
||||
blob_string += "G92 E0 ; Reset extruder\n"
|
||||
blob_string += "M300 P500 S600 ; Beep\n"
|
||||
blob_string += "G4 S2 ; Wait\n"
|
||||
return blob_string
|
||||
|
|
@ -101,7 +101,8 @@ class RemovableDriveOutputDevice(OutputDevice):
|
|||
self._stream = open(file_name, "wt", buffering = 1, encoding = "utf-8")
|
||||
else: #Binary mode.
|
||||
self._stream = open(file_name, "wb", buffering = 1)
|
||||
job = WriteFileJob(writer, self._stream, nodes, preferred_format["mode"])
|
||||
writer_args = {"mime_type": preferred_format["mime_type"]}
|
||||
job = WriteFileJob(writer, self._stream, nodes, preferred_format["mode"], writer_args)
|
||||
job.setFileName(file_name)
|
||||
job.progress.connect(self._onProgress)
|
||||
job.finished.connect(self._onFinished)
|
||||
|
|
|
|||
|
|
@ -67,39 +67,40 @@ class SimulationPass(RenderPass):
|
|||
if not self._compatibility_mode:
|
||||
self._layer_shader.setUniformValue("u_starts_color", Color(*Application.getInstance().getTheme().getColor("layerview_starts").getRgb()))
|
||||
|
||||
if self._layer_view:
|
||||
self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
|
||||
self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
|
||||
self._layer_shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
|
||||
self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
|
||||
self._layer_shader.setUniformValue("u_max_line_width", self._layer_view.getMaxLineWidth())
|
||||
self._layer_shader.setUniformValue("u_min_line_width", self._layer_view.getMinLineWidth())
|
||||
self._layer_shader.setUniformValue("u_max_flow_rate", self._layer_view.getMaxFlowRate())
|
||||
self._layer_shader.setUniformValue("u_min_flow_rate", self._layer_view.getMinFlowRate())
|
||||
self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
|
||||
self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
|
||||
self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
|
||||
self._layer_shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
|
||||
self._layer_shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
|
||||
self._layer_shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
|
||||
self._layer_shader.setUniformValue("u_show_starts", self._layer_view.getShowStarts())
|
||||
else:
|
||||
#defaults
|
||||
self._layer_shader.setUniformValue("u_max_feedrate", 1)
|
||||
self._layer_shader.setUniformValue("u_min_feedrate", 0)
|
||||
self._layer_shader.setUniformValue("u_max_thickness", 1)
|
||||
self._layer_shader.setUniformValue("u_min_thickness", 0)
|
||||
self._layer_shader.setUniformValue("u_max_flow_rate", 1)
|
||||
self._layer_shader.setUniformValue("u_min_flow_rate", 0)
|
||||
self._layer_shader.setUniformValue("u_max_line_width", 1)
|
||||
self._layer_shader.setUniformValue("u_min_line_width", 0)
|
||||
self._layer_shader.setUniformValue("u_layer_view_type", 1)
|
||||
self._layer_shader.setUniformValue("u_extruder_opacity", [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
|
||||
self._layer_shader.setUniformValue("u_show_travel_moves", 0)
|
||||
self._layer_shader.setUniformValue("u_show_helpers", 1)
|
||||
self._layer_shader.setUniformValue("u_show_skin", 1)
|
||||
self._layer_shader.setUniformValue("u_show_infill", 1)
|
||||
self._layer_shader.setUniformValue("u_show_starts", 1)
|
||||
for shader in [self._layer_shader, self._layer_shadow_shader]:
|
||||
if self._layer_view:
|
||||
shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate())
|
||||
shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate())
|
||||
shader.setUniformValue("u_max_thickness", self._layer_view.getMaxThickness())
|
||||
shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness())
|
||||
shader.setUniformValue("u_max_line_width", self._layer_view.getMaxLineWidth())
|
||||
shader.setUniformValue("u_min_line_width", self._layer_view.getMinLineWidth())
|
||||
shader.setUniformValue("u_max_flow_rate", self._layer_view.getMaxFlowRate())
|
||||
shader.setUniformValue("u_min_flow_rate", self._layer_view.getMinFlowRate())
|
||||
shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType())
|
||||
shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities())
|
||||
shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves())
|
||||
shader.setUniformValue("u_show_helpers", self._layer_view.getShowHelpers())
|
||||
shader.setUniformValue("u_show_skin", self._layer_view.getShowSkin())
|
||||
shader.setUniformValue("u_show_infill", self._layer_view.getShowInfill())
|
||||
shader.setUniformValue("u_show_starts", self._layer_view.getShowStarts())
|
||||
else:
|
||||
#defaults
|
||||
shader.setUniformValue("u_max_feedrate", 1)
|
||||
shader.setUniformValue("u_min_feedrate", 0)
|
||||
shader.setUniformValue("u_max_thickness", 1)
|
||||
shader.setUniformValue("u_min_thickness", 0)
|
||||
shader.setUniformValue("u_max_flow_rate", 1)
|
||||
shader.setUniformValue("u_min_flow_rate", 0)
|
||||
shader.setUniformValue("u_max_line_width", 1)
|
||||
shader.setUniformValue("u_min_line_width", 0)
|
||||
shader.setUniformValue("u_layer_view_type", 1)
|
||||
shader.setUniformValue("u_extruder_opacity", [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
|
||||
shader.setUniformValue("u_show_travel_moves", 0)
|
||||
shader.setUniformValue("u_show_helpers", 1)
|
||||
shader.setUniformValue("u_show_skin", 1)
|
||||
shader.setUniformValue("u_show_infill", 1)
|
||||
shader.setUniformValue("u_show_starts", 1)
|
||||
|
||||
if not self._tool_handle_shader:
|
||||
self._tool_handle_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "toolhandle.shader"))
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class UFPWriter(MeshWriter):
|
|||
# Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled
|
||||
# by the Job class.
|
||||
@call_on_qt_thread
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
|
||||
def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode, **kwargs):
|
||||
archive = VirtualFile()
|
||||
archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly)
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 620 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 398 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 899 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 682 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 874 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 90 KiB |
BIN
plugins/UM3NetworkPrinting/resources/png/Ultimaker S6.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 77 KiB |
BIN
plugins/UM3NetworkPrinting/resources/png/Ultimaker S8.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
|
|
@ -21,7 +21,7 @@ def main() -> None:
|
|||
parser.add_argument("--diagnose", action="store_true", help="Diagnose the files")
|
||||
parser.add_argument("--deleted", action="store_true", help="Check for deleted files")
|
||||
parser.add_argument("--fix", action="store_true", help="Attempt to apply the suggested fixes on the files")
|
||||
parser.add_argument("Files", metavar="F", type=Path, nargs="+", help="Files or directories to format")
|
||||
parser.add_argument("Files", type=Path, nargs="+", help="Files or directories to format")
|
||||
|
||||
args = parser.parse_args()
|
||||
files = extractFilePaths(args.Files)
|
||||
|
|
@ -39,7 +39,7 @@ def main() -> None:
|
|||
return
|
||||
|
||||
with open(setting_path, "r") as f:
|
||||
settings = yaml.load(f, yaml.FullLoader)
|
||||
settings = yaml.safe_load(f)
|
||||
|
||||
full_body_check = {"Diagnostics": []}
|
||||
comments_check = {"Error Files": []}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,21 @@
|
|||
{
|
||||
"3DConnexion": {
|
||||
"package_info": {
|
||||
"package_id": "3DConnexion",
|
||||
"package_type": "plugin",
|
||||
"display_name": "3DConnexion mouses",
|
||||
"description": "Allows working with 3D mouses inside Cura.\nOnly available on Windows and MacOS.",
|
||||
"package_version": "1.0.0",
|
||||
"sdk_version": "8.6.0",
|
||||
"website": "https://3dconnexion.com",
|
||||
"author": {
|
||||
"author_id": "UltimakerPackages",
|
||||
"display_name": "3DConnexion",
|
||||
"email": "plugins@ultimaker.com",
|
||||
"website": "https://3dconnexion.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3MFReader": {
|
||||
"package_info": {
|
||||
"package_id": "3MFReader",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
version: "5.9.0"
|
||||
version: "5.11.0-alpha.0"
|
||||
|
|
|
|||
42
resources/definitions/bambulab_a1.def.json
Normal file
42
resources/definitions/bambulab_a1mini.def.json
Normal file
267
resources/definitions/bambulab_base.def.json
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "BambuLab base definition",
|
||||
"inherits": "fdmprinter",
|
||||
"metadata":
|
||||
{
|
||||
"visible": false,
|
||||
"author": "UltiMaker",
|
||||
"manufacturer": "BambuLab",
|
||||
"file_formats": "application/vnd.bambulab-package.3dmanufacturing-3dmodel+xml"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_infill": { "value": "acceleration_print" },
|
||||
"acceleration_layer_0": { "value": 2000 },
|
||||
"acceleration_prime_tower": { "value": "acceleration_print" },
|
||||
"acceleration_print": { "value": 20000 },
|
||||
"acceleration_print_layer_0": { "value": "acceleration_layer_0" },
|
||||
"acceleration_roofing": { "value": "acceleration_wall_0" },
|
||||
"acceleration_skirt_brim": { "value": "acceleration_layer_0" },
|
||||
"acceleration_support": { "value": "acceleration_print" },
|
||||
"acceleration_support_bottom": { "value": "acceleration_support_interface" },
|
||||
"acceleration_support_infill": { "value": "acceleration_support" },
|
||||
"acceleration_support_interface": { "value": "acceleration_support" },
|
||||
"acceleration_support_roof": { "value": "acceleration_support_interface" },
|
||||
"acceleration_topbottom": { "value": "acceleration_print" },
|
||||
"acceleration_travel": { "value": 20000 },
|
||||
"acceleration_travel_enabled": { "value": true },
|
||||
"acceleration_travel_layer_0": { "value": "acceleration_layer_0" },
|
||||
"acceleration_wall": { "value": "acceleration_print/8" },
|
||||
"acceleration_wall_0": { "value": "acceleration_wall" },
|
||||
"acceleration_wall_0_roofing": { "value": "acceleration_wall_0" },
|
||||
"acceleration_wall_x": { "value": "acceleration_print" },
|
||||
"acceleration_wall_x_roofing": { "value": "acceleration_wall" },
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
"bottom_thickness": { "value": 0.6 },
|
||||
"bridge_skin_speed":
|
||||
{
|
||||
"unit": "mm/s",
|
||||
"value": "bridge_wall_speed"
|
||||
},
|
||||
"bridge_sparse_infill_max_density": { "value": 50 },
|
||||
"bridge_wall_min_length": { "value": 10 },
|
||||
"bridge_wall_speed":
|
||||
{
|
||||
"unit": "mm/s",
|
||||
"value": 50
|
||||
},
|
||||
"cool_min_layer_time": { "value": 6 },
|
||||
"cool_min_speed": { "value": 6 },
|
||||
"cool_min_temperature": { "value": "material_print_temperature-15" },
|
||||
"default_material_print_temperature": { "maximum_value_warning": 320 },
|
||||
"extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" },
|
||||
"gradual_flow_enabled": { "value": false },
|
||||
"hole_xy_offset": { "value": 0.075 },
|
||||
"infill_overlap": { "value": 10 },
|
||||
"infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'gyroid'" },
|
||||
"infill_sparse_density": { "value": 15 },
|
||||
"infill_wall_line_count": { "value": "1 if infill_sparse_density > 80 else 0" },
|
||||
"jerk_infill": { "value": "jerk_print" },
|
||||
"jerk_layer_0": { "value": "jerk_print/2" },
|
||||
"jerk_prime_tower": { "value": "jerk_print" },
|
||||
"jerk_print": { "value": "50" },
|
||||
"jerk_print_layer_0": { "value": "jerk_layer_0" },
|
||||
"jerk_roofing": { "value": "jerk_wall_0" },
|
||||
"jerk_skirt_brim": { "value": "jerk_layer_0" },
|
||||
"jerk_support": { "value": "jerk_print" },
|
||||
"jerk_support_bottom": { "value": "jerk_support_interface" },
|
||||
"jerk_support_infill": { "value": "jerk_support" },
|
||||
"jerk_support_interface": { "value": "jerk_support" },
|
||||
"jerk_support_roof": { "value": "jerk_support_interface" },
|
||||
"jerk_topbottom": { "value": "jerk_print" },
|
||||
"jerk_travel": { "value": 50 },
|
||||
"jerk_travel_enabled": { "value": true },
|
||||
"jerk_travel_layer_0": { "value": "jerk_travel" },
|
||||
"jerk_wall": { "value": "jerk_print/5" },
|
||||
"jerk_wall_0": { "value": "jerk_wall" },
|
||||
"jerk_wall_0_roofing": { "value": "jerk_wall_0" },
|
||||
"jerk_wall_x": { "value": "jerk_print" },
|
||||
"jerk_wall_x_roofing": { "value": "jerk_wall_0" },
|
||||
"line_width": { "value": 0.42 },
|
||||
"machine_acceleration": { "value": 10000 },
|
||||
"machine_buildplate_type":
|
||||
{
|
||||
"default_value": "textured_pei_plate",
|
||||
"options":
|
||||
{
|
||||
"cool_plate": "Cool Plate",
|
||||
"engineering_plate": "Engineering Plate",
|
||||
"high_temp_plate": "High Temp Plate",
|
||||
"textured_pei_plate": "Textured PEI Plate"
|
||||
}
|
||||
},
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_gcode_flavor": { "default_value": "BambuLab" },
|
||||
"machine_heated_bed": { "default_value": true },
|
||||
"machine_max_feedrate_e": { "value": 150 },
|
||||
"machine_max_feedrate_x": { "value": 500 },
|
||||
"machine_max_feedrate_y": { "value": 500 },
|
||||
"machine_max_feedrate_z": { "value": 15 },
|
||||
"machine_max_jerk_e": { "default_value": 100 },
|
||||
"machine_max_jerk_xy": { "default_value": 5000 },
|
||||
"machine_max_jerk_z": { "default_value": 100 },
|
||||
"machine_nozzle_cool_down_speed": { "default_value": 1.3 },
|
||||
"machine_nozzle_heat_up_speed": { "default_value": 1.9 },
|
||||
"machine_nozzle_size": { "default_value": 0.4 },
|
||||
"machine_show_variants": { "value": true },
|
||||
"machine_use_extruder_offset_to_offset_coords": { "value": false },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"material_flush_purge_length":
|
||||
{
|
||||
"default_value": 80,
|
||||
"enabled": "not prime_tower_enable"
|
||||
},
|
||||
"material_flush_purge_speed":
|
||||
{
|
||||
"default_value": 500,
|
||||
"enabled": "not prime_tower_enable"
|
||||
},
|
||||
"material_max_flowrate": { "enabled": true },
|
||||
"max_skin_angle_for_expansion": { "value": 45 },
|
||||
"meshfix_maximum_resolution": { "value": 0.4 },
|
||||
"min_infill_area": { "default_value": 10 },
|
||||
"optimize_wall_printing_order": { "value": false },
|
||||
"prime_tower_enable": { "default_value": true },
|
||||
"prime_tower_line_width": { "value": "1.5 * line_width" },
|
||||
"prime_tower_min_volume": { "default_value": 250 },
|
||||
"prime_tower_size": { "default_value": 40 },
|
||||
"relative_extrusion": { "value": true },
|
||||
"retraction_amount": { "value": 0.5 },
|
||||
"retraction_combing_max_distance": { "value": 100 },
|
||||
"retraction_extra_prime_amount": { "value": 0.12 },
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
"retraction_hop_after_extruder_switch_height": { "value": 2 },
|
||||
"retraction_hop_enabled": { "value": true },
|
||||
"retraction_min_travel": { "value": "5 if support_enable and support_structure=='tree' else line_width * 2" },
|
||||
"retraction_prime_speed": { "value": 15 },
|
||||
"retraction_speed": { "value": 30 },
|
||||
"skin_edge_support_thickness": { "value": 0 },
|
||||
"skin_material_flow": { "value": 95 },
|
||||
"skin_overlap": { "value": 0 },
|
||||
"skin_preshrink": { "value": 0 },
|
||||
"skirt_brim_speed": { "maximum_value_warning": 500 },
|
||||
"skirt_line_count": { "value": 5 },
|
||||
"small_skin_on_surface": { "value": false },
|
||||
"small_skin_width": { "value": 4 },
|
||||
"speed_infill":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_ironing":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": 20
|
||||
},
|
||||
"speed_layer_0":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_print/6"
|
||||
},
|
||||
"speed_prime_tower":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_print":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": 300
|
||||
},
|
||||
"speed_print_layer_0":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_layer_0"
|
||||
},
|
||||
"speed_roofing":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_support":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall_0"
|
||||
},
|
||||
"speed_support_bottom":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_support_interface"
|
||||
},
|
||||
"speed_support_infill":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_support"
|
||||
},
|
||||
"speed_support_interface":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": 50
|
||||
},
|
||||
"speed_support_roof":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_support_interface"
|
||||
},
|
||||
"speed_topbottom":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_travel":
|
||||
{
|
||||
"maximum_value": 500,
|
||||
"value": 500
|
||||
},
|
||||
"speed_travel_layer_0":
|
||||
{
|
||||
"maximum_value": 500,
|
||||
"value": 150
|
||||
},
|
||||
"speed_wall":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_print*2/3"
|
||||
},
|
||||
"speed_wall_0":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_wall_0_roofing":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_wall_x":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall_x_roofing":
|
||||
{
|
||||
"maximum_value_warning": 500,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"support_brim_line_count": { "value": 5 },
|
||||
"support_infill_rate": { "value": "80 if gradual_support_infill_steps != 0 else 15" },
|
||||
"support_pattern": { "value": "'gyroid'" },
|
||||
"support_structure": { "value": "'tree'" },
|
||||
"switch_extruder_retraction_amount": { "value": 5 },
|
||||
"travel_avoid_other_parts": { "value": false },
|
||||
"wall_0_acceleration": { "value": 1000 },
|
||||
"wall_0_deceleration": { "value": 1000 },
|
||||
"wall_0_end_speed_ratio": { "value": 100 },
|
||||
"wall_0_speed_split_distance": { "value": 0.2 },
|
||||
"wall_0_start_speed_ratio": { "value": 100 },
|
||||
"wall_0_wipe_dist": { "value": 0 },
|
||||
"wall_material_flow": { "value": 95 },
|
||||
"wall_overhang_angle": { "value": 10 },
|
||||
"wall_overhang_speed_factors": { "default_value": "[25,15,5,5]" },
|
||||
"wall_x_material_flow": { "value": 100 },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"z_seam_position": { "value": "'backright'" },
|
||||
"z_seam_type": { "value": "'sharpest_corner'" }
|
||||
}
|
||||
}
|
||||
58
resources/definitions/bambulab_x1.def.json
Normal file
|
|
@ -48,7 +48,7 @@
|
|||
"machine_width": { "default_value": 220 },
|
||||
"material_print_temp_wait": { "default_value": false },
|
||||
"retraction_amount": { "default_value": 0.8 },
|
||||
"retraction_combing": { "value": "no_outer_surfaces" },
|
||||
"retraction_combing": { "value": "'no_outer_surfaces'" },
|
||||
"retraction_combing_max_distance": { "value": 5.0 },
|
||||
"retraction_extrusion_window": { "value": "retraction_amount" },
|
||||
"retraction_min_travel": { "value": 2.0 },
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"author": "Ultimaker",
|
||||
"manufacturer": "Unknown",
|
||||
"position": "0",
|
||||
"setting_version": 24,
|
||||
"setting_version": 25,
|
||||
"type": "extruder"
|
||||
},
|
||||
"settings":
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"type": "machine",
|
||||
"author": "Unknown",
|
||||
"manufacturer": "Unknown",
|
||||
"setting_version": 24,
|
||||
"setting_version": 25,
|
||||
"file_formats": "text/x-gcode;model/stl;application/x-wavefront-obj;application/x3g",
|
||||
"visible": false,
|
||||
"has_materials": true,
|
||||
|
|
@ -214,8 +214,8 @@
|
|||
},
|
||||
"machine_buildplate_type":
|
||||
{
|
||||
"label": "Build Plate Material",
|
||||
"description": "The material of the build plate installed on the printer.",
|
||||
"label": "Build Plate Type",
|
||||
"description": "The type of build plate installed on the printer.",
|
||||
"default_value": "glass",
|
||||
"type": "enum",
|
||||
"options":
|
||||
|
|
@ -388,7 +388,8 @@
|
|||
"Makerbot": "Makerbot",
|
||||
"BFB": "Bits from Bytes",
|
||||
"MACH3": "Mach3",
|
||||
"Repetier": "Repetier"
|
||||
"Repetier": "Repetier",
|
||||
"BambuLab": "BambuLab"
|
||||
},
|
||||
"default_value": "RepRap (Marlin/Sprinter)",
|
||||
"settable_per_mesh": false,
|
||||
|
|
@ -3021,6 +3022,7 @@
|
|||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3035,6 +3037,7 @@
|
|||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3049,6 +3052,7 @@
|
|||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3063,6 +3067,7 @@
|
|||
"minimum_value": "0.0001",
|
||||
"minimum_value_warning": "50",
|
||||
"maximum_value_warning": "150",
|
||||
"enabled": "flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
|
|
@ -3372,6 +3377,20 @@
|
|||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"material_max_flowrate":
|
||||
{
|
||||
"default_value": 16,
|
||||
"description": "Maximum flow rate that the printer can extrude for the material",
|
||||
"enabled": false,
|
||||
"label": "Material Maximum Flow Rate",
|
||||
"maximum_value": "machine_max_feedrate_e * (material_diameter/2)**2 * math.pi",
|
||||
"minimum_value": "0",
|
||||
"settable_per_extruder": true,
|
||||
"settable_per_mesh": false,
|
||||
"type": "float",
|
||||
"unit": "mm\u00b3/s",
|
||||
"value": "16"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -3464,6 +3483,7 @@
|
|||
"maximum_value_warning": "150",
|
||||
"default_value": 30,
|
||||
"value": "speed_wall_0",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3478,6 +3498,7 @@
|
|||
"maximum_value_warning": "150",
|
||||
"default_value": 60,
|
||||
"value": "speed_wall_x",
|
||||
"enabled": "roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3492,6 +3513,7 @@
|
|||
"maximum_value_warning": "150",
|
||||
"default_value": 30,
|
||||
"value": "speed_wall_0",
|
||||
"enabled": "flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3506,6 +3528,7 @@
|
|||
"maximum_value_warning": "150",
|
||||
"default_value": 60,
|
||||
"value": "speed_wall_x",
|
||||
"enabled": "flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
|
|
@ -3878,7 +3901,7 @@
|
|||
"maximum_value_warning": "10000",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_wall_0",
|
||||
"enabled": "resolveOrValue('acceleration_enabled')",
|
||||
"enabled": "resolveOrValue('acceleration_enabled') and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3893,7 +3916,7 @@
|
|||
"maximum_value_warning": "10000",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_wall_x",
|
||||
"enabled": "resolveOrValue('acceleration_enabled')",
|
||||
"enabled": "resolveOrValue('acceleration_enabled') and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3908,7 +3931,7 @@
|
|||
"maximum_value_warning": "10000",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_wall_0",
|
||||
"enabled": "resolveOrValue('acceleration_enabled')",
|
||||
"enabled": "resolveOrValue('acceleration_enabled') and flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -3923,7 +3946,7 @@
|
|||
"maximum_value_warning": "10000",
|
||||
"default_value": 3000,
|
||||
"value": "acceleration_wall_x",
|
||||
"enabled": "resolveOrValue('acceleration_enabled')",
|
||||
"enabled": "resolveOrValue('acceleration_enabled') and flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
|
|
@ -4251,7 +4274,7 @@
|
|||
"maximum_value_warning": "50",
|
||||
"default_value": 20,
|
||||
"value": "jerk_wall_0",
|
||||
"enabled": "resolveOrValue('jerk_enabled')",
|
||||
"enabled": "resolveOrValue('jerk_enabled') and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -4265,7 +4288,7 @@
|
|||
"maximum_value_warning": "50",
|
||||
"default_value": 20,
|
||||
"value": "jerk_wall_x",
|
||||
"enabled": "resolveOrValue('jerk_enabled')",
|
||||
"enabled": "resolveOrValue('jerk_enabled') and roofing_layer_count > 0 and top_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -4279,7 +4302,7 @@
|
|||
"maximum_value_warning": "50",
|
||||
"default_value": 20,
|
||||
"value": "jerk_wall_0",
|
||||
"enabled": "resolveOrValue('jerk_enabled')",
|
||||
"enabled": "resolveOrValue('jerk_enabled') and flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_0_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
|
|
@ -4293,7 +4316,7 @@
|
|||
"maximum_value_warning": "50",
|
||||
"default_value": 20,
|
||||
"value": "jerk_wall_x",
|
||||
"enabled": "resolveOrValue('jerk_enabled')",
|
||||
"enabled": "resolveOrValue('jerk_enabled') and flooring_layer_count > 0 and bottom_layers > 0",
|
||||
"limit_to_extruder": "wall_x_extruder_nr",
|
||||
"settable_per_mesh": true
|
||||
}
|
||||
|
|
@ -4847,7 +4870,7 @@
|
|||
},
|
||||
"build_fan_full_at_height":
|
||||
{
|
||||
"label": "Build Fan Speed at Height",
|
||||
"label": "Build Volume Fan Speed at Height",
|
||||
"description": "The height at which the fans spin on regular fan speed. At the layers below the fan speed gradually increases from Initial Fan Speed to Regular Fan Speed.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
|
|
@ -4862,8 +4885,8 @@
|
|||
{
|
||||
"build_fan_full_layer":
|
||||
{
|
||||
"label": "Build Fan Speed at Layer",
|
||||
"description": "The layer at which the build fans spin on full fan speed. This value is calculated and rounded to a whole number.",
|
||||
"label": "Build Volume Fan Speed at Layer",
|
||||
"description": "The layer at which the build-volume fans spin on full fan speed. This value is calculated and rounded to a whole number.",
|
||||
"type": "int",
|
||||
"default_value": 0,
|
||||
"minimum_value": "0",
|
||||
|
|
@ -4876,6 +4899,34 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"build_volume_fan_speed_0":
|
||||
{
|
||||
"label": "Initial Layers Build Volume Fan Speed",
|
||||
"description": "The fan speed (as a percentage) for the auxiliary or build-volume fan, that is set until the layer specified at 'Build Volume Fan Speed at Layer' is reached. After that, the speed is set by 'Build Volume Fan Speed' instead (so not this 'Initial Layers' one).",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 0,
|
||||
"enabled": "build_volume_fan_nr != 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"build_volume_fan_speed":
|
||||
{
|
||||
"label": "Build Volume Fan Speed",
|
||||
"description": "The fan speed (as a percentage) for the auxiliary or build-volume fan, that is set from the moment that the layer specified at 'Build Volume Fan Speed at Layer' is reached and onwards. Before that, the speed is set by 'Initial Layers Build Volume Fan Speed' instead.",
|
||||
"unit": "%",
|
||||
"type": "float",
|
||||
"minimum_value": "0",
|
||||
"maximum_value": "100",
|
||||
"default_value": 100,
|
||||
"enabled": "build_volume_fan_nr != 0",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_meshgroup": false
|
||||
},
|
||||
"cool_fan_speed":
|
||||
{
|
||||
"label": "Fan Speed",
|
||||
|
|
@ -4985,31 +5036,6 @@
|
|||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_min_layer_time_overhang":
|
||||
{
|
||||
"label": "Minimum Layer Time with Overhang",
|
||||
"description": "The minimum time spent in a layer that contains overhanging extrusions. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer. Layers may still take shorter than the minimal layer time if Lift Head is disabled and if the Minimum Speed would otherwise be violated.",
|
||||
"unit": "s",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"value": "cool_min_layer_time",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "600",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_min_layer_time_overhang_min_segment_length":
|
||||
{
|
||||
"label": "Minimum Overhang Segment Length",
|
||||
"description": "When trying to apply the minimum layer time specific for overhanging layers, it will be applied only if at least one consecutive overhanging extrusion move is longer than this value.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "500",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_min_speed":
|
||||
{
|
||||
"label": "Minimum Speed",
|
||||
|
|
@ -7482,6 +7508,17 @@
|
|||
"limit_to_extruder": "raft_surface_extruder_nr"
|
||||
}
|
||||
}
|
||||
},
|
||||
"machine_scan_first_layer":
|
||||
{
|
||||
"default_value": false,
|
||||
"description": "Whether to scan the first layer for layer adhesion problems.",
|
||||
"enabled": false,
|
||||
"label": "Scan the first layer",
|
||||
"settable_per_extruder": false,
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_meshgroup": false,
|
||||
"type": "bool"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -8699,6 +8736,33 @@
|
|||
"default_value": 90,
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"cool_min_layer_time_overhang":
|
||||
{
|
||||
"label": "Minimum Layer Time with Overhang",
|
||||
"description": "The minimum time spent in a layer that contains overhanging extrusions. This forces the printer to slow down, to at least spend the time set here in one layer. This allows the printed material to cool down properly before printing the next layer. Layers may still take shorter than the minimal layer time if Lift Head is disabled and if the Minimum Speed would otherwise be violated.",
|
||||
"unit": "s",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"value": "cool_min_layer_time",
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "600",
|
||||
"enabled": "wall_overhang_angle < 90",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"cool_min_layer_time_overhang_min_segment_length":
|
||||
{
|
||||
"label": "Minimum Overhang Segment Length",
|
||||
"description": "When trying to apply the minimum layer time specific for overhanging layers, it will be applied only if at least one consecutive overhanging extrusion move is longer than this value.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default_value": 5,
|
||||
"minimum_value": "0",
|
||||
"maximum_value_warning": "500",
|
||||
"enabled": "wall_overhang_angle < 90",
|
||||
"settable_per_mesh": false,
|
||||
"settable_per_extruder": true
|
||||
},
|
||||
"seam_overhang_angle":
|
||||
{
|
||||
"label": "Seam Overhanging Wall Angle",
|
||||
|
|
@ -8719,6 +8783,7 @@
|
|||
"unit": "%",
|
||||
"type": "[int]",
|
||||
"default_value": "[100]",
|
||||
"enabled": "wall_overhang_angle < 90",
|
||||
"settable_per_mesh": true
|
||||
},
|
||||
"bridge_settings_enabled":
|
||||
|
|
|
|||
|
|
@ -48,23 +48,111 @@
|
|||
"infill_material_flow": { "value": "(1.95-infill_sparse_density / 100 if infill_sparse_density > 95 else 1) * material_flow" },
|
||||
"infill_overlap": { "value": "0 if infill_sparse_density > 80 else 10" },
|
||||
"inset_direction": { "value": "'outside_in'" },
|
||||
"jerk_infill": { "minimum_value_warning": 20 },
|
||||
"jerk_prime_tower": { "minimum_value_warning": 20 },
|
||||
"jerk_flooring":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_infill":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_layer_0": { "minimum_value": 1 },
|
||||
"jerk_prime_tower":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_print":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20,
|
||||
"value": "20"
|
||||
},
|
||||
"jerk_print_layer_0": { "value": "max(20, jerk_wall_0)" },
|
||||
"jerk_roofing": { "minimum_value_warning": 20 },
|
||||
"jerk_support": { "minimum_value_warning": 20 },
|
||||
"jerk_support_infill": { "minimum_value_warning": 20 },
|
||||
"jerk_support_interface": { "minimum_value_warning": 20 },
|
||||
"jerk_topbottom": { "minimum_value_warning": 20 },
|
||||
"jerk_travel": { "value": "jerk_print" },
|
||||
"jerk_print_layer_0":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"value": "max(20, jerk_wall_0)"
|
||||
},
|
||||
"jerk_roofing":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_skirt_brim": { "minimum_value": 1 },
|
||||
"jerk_support":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_bottom":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_infill":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_interface":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_support_roof":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_topbottom":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_travel":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"value": "jerk_print"
|
||||
},
|
||||
"jerk_travel_enabled": { "value": false },
|
||||
"jerk_wall": { "minimum_value_warning": 20 },
|
||||
"jerk_wall_0": { "minimum_value_warning": 20 },
|
||||
"jerk_travel_layer_0": { "minimum_value": 1 },
|
||||
"jerk_wall":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_0":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_0_flooring":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_0_roofing":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_x":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_x_flooring":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"jerk_wall_x_roofing":
|
||||
{
|
||||
"minimum_value": 1,
|
||||
"minimum_value_warning": 20
|
||||
},
|
||||
"layer_height": { "value": 0.15 },
|
||||
"layer_height_0": { "value": "max(0.2, layer_height)" },
|
||||
"line_width": { "value": "machine_nozzle_size" },
|
||||
|
|
@ -84,7 +172,7 @@
|
|||
"material_standby_temperature":
|
||||
{
|
||||
"minimum_value": "0",
|
||||
"value": "material_print_temperature - 100"
|
||||
"value": "resolveOrValue('material_print_temperature') - 100"
|
||||
},
|
||||
"meshfix_maximum_deviation": { "value": "machine_nozzle_size / 10" },
|
||||
"meshfix_maximum_resolution": { "value": "max(speed_wall_0 / 75, 0.5)" },
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
},
|
||||
"acceleration_infill":
|
||||
{
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -50,7 +51,7 @@
|
|||
},
|
||||
"acceleration_prime_tower":
|
||||
{
|
||||
"enabled": "acceleration_enabled and prime_tower_enable and extruders_enabled_count > 1",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -58,7 +59,7 @@
|
|||
},
|
||||
"acceleration_print":
|
||||
{
|
||||
"enabled": "acceleration_enabled",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -71,6 +72,7 @@
|
|||
},
|
||||
"acceleration_roofing":
|
||||
{
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -78,7 +80,7 @@
|
|||
},
|
||||
"acceleration_skirt_brim":
|
||||
{
|
||||
"enabled": "acceleration_enabled and (adhesion_type == 'skirt' or adhesion_type == 'brim')",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -86,6 +88,7 @@
|
|||
},
|
||||
"acceleration_support":
|
||||
{
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -98,6 +101,7 @@
|
|||
},
|
||||
"acceleration_support_infill":
|
||||
{
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -105,6 +109,7 @@
|
|||
},
|
||||
"acceleration_support_interface":
|
||||
{
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -122,7 +127,7 @@
|
|||
},
|
||||
"acceleration_travel":
|
||||
{
|
||||
"enabled": "acceleration_enabled",
|
||||
"enabled": false,
|
||||
"maximum_value": 5000,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -140,7 +145,7 @@
|
|||
},
|
||||
"acceleration_wall":
|
||||
{
|
||||
"enabled": "acceleration_enabled",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -148,7 +153,7 @@
|
|||
},
|
||||
"acceleration_wall_0":
|
||||
{
|
||||
"enabled": "acceleration_enabled",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -161,7 +166,7 @@
|
|||
},
|
||||
"acceleration_wall_x":
|
||||
{
|
||||
"enabled": "acceleration_enabled",
|
||||
"enabled": false,
|
||||
"maximum_value": 3500,
|
||||
"minimum_value": 200,
|
||||
"minimum_value_warning": 750,
|
||||
|
|
@ -391,7 +396,7 @@
|
|||
"machine_heated_bed": { "default_value": false },
|
||||
"machine_heated_build_volume": { "default_value": true },
|
||||
"machine_height": { "default_value": 196.749 },
|
||||
"machine_min_cool_heat_time_window": { "value": 15 },
|
||||
"machine_min_cool_heat_time_window": { "value": 0 },
|
||||
"machine_name": { "default_value": "UltiMaker Method" },
|
||||
"machine_nozzle_cool_down_speed": { "value": 0.8 },
|
||||
"machine_nozzle_heat_up_speed": { "value": 3.5 },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Ultimaker Original Dual Extrusion",
|
||||
"inherits": "ultimaker",
|
||||
"inherits": "ultimaker_original",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
|
|
@ -49,10 +49,13 @@
|
|||
},
|
||||
"overrides":
|
||||
{
|
||||
"cool_fan_speed_0": { "value": "cool_fan_speed_min" },
|
||||
"gantry_height": { "value": "55" },
|
||||
"infill_before_walls": { "value": "True" },
|
||||
"layer_height_0": { "value": "max(0.2, layer_height)" },
|
||||
"machine_center_is_zero": { "default_value": false },
|
||||
"machine_depth": { "default_value": 195 },
|
||||
"machine_end_gcode": { "default_value": "M104 T0 S0 ;1st extruder heater off\nM104 T1 S0 ;2nd extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" },
|
||||
"machine_end_gcode": { "value": "'M104 T0 S0 ;1st extruder heater off\\nM104 T1 S0 ;2nd extruder heater off' + ('\\nM140 S0 ;heated bed heater off' if machine_heated_bed else '') + '\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning'" },
|
||||
"machine_extruder_count": { "default_value": 2 },
|
||||
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
|
||||
"machine_head_with_fans_polygon":
|
||||
|
|
@ -65,9 +68,11 @@
|
|||
]
|
||||
},
|
||||
"machine_height": { "default_value": 200 },
|
||||
"machine_name": { "default_value": "Ultimaker Original" },
|
||||
"machine_name": { "default_value": "Ultimaker Original Dual Extrusion" },
|
||||
"machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nT1 ;Switch to the 2nd extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F200 E-{switch_extruder_retraction_amount}\nT0 ;Switch to the 1st extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." },
|
||||
"machine_use_extruder_offset_to_offset_coords": { "default_value": true },
|
||||
"machine_width": { "default_value": 205 }
|
||||
"machine_width": { "default_value": 205 },
|
||||
"material_print_temp_wait": { "value": false },
|
||||
"speed_slowdown_layers": { "value": 1 }
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
"machine_extruder_trains": { "0": "ultimaker_replicator_extruder" },
|
||||
"preferred_material": "ultimaker_pla_175",
|
||||
"preferred_quality_type": "draft",
|
||||
"preferred_variant_name": "ultimaker_replicator_smart_extruder_plus",
|
||||
"preferred_variant_name": "Smart Extruder+",
|
||||
"reference_machine_id": "replicator_b",
|
||||
"supports_network_connection": true,
|
||||
"supports_usb_connection": false,
|
||||
|
|
|
|||
|
|
@ -72,7 +72,11 @@
|
|||
"brim_width": { "value": "3" },
|
||||
"build_volume_temperature": { "maximum_value": 50 },
|
||||
"cool_fan_speed": { "value": "50" },
|
||||
"default_material_print_temperature": { "value": "200" },
|
||||
"default_material_print_temperature":
|
||||
{
|
||||
"maximum_value_warning": "320",
|
||||
"value": "200"
|
||||
},
|
||||
"extruder_prime_pos_abs": { "default_value": true },
|
||||
"gantry_height": { "value": "55" },
|
||||
"infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'triangles'" },
|
||||
|
|
@ -104,6 +108,7 @@
|
|||
"machine_nozzle_heat_up_speed": { "default_value": 1.4 },
|
||||
"machine_start_gcode": { "default_value": "" },
|
||||
"machine_width": { "default_value": 330 },
|
||||
"material_print_temperature_layer_0": { "maximum_value_warning": "320" },
|
||||
"multiple_mesh_overlap": { "value": "0" },
|
||||
"optimize_wall_printing_order": { "value": "True" },
|
||||
"prime_blob_enable":
|
||||
|
|
|
|||
51
resources/definitions/ultimaker_s6.def.json
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "UltiMaker S6",
|
||||
"inherits": "ultimaker_s8",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "UltiMaker",
|
||||
"manufacturer": "Ultimaker B.V.",
|
||||
"file_formats": "application/x-ufp;text/x-gcode",
|
||||
"platform": "ultimaker_s5_platform.obj",
|
||||
"bom_numbers": [10700, 10701],
|
||||
"firmware_update_info":
|
||||
{
|
||||
"check_urls": [ "https://software.ultimaker.com/releases/firmware/5078167/stable/um-update.swu.version" ],
|
||||
"id": 5078167,
|
||||
"update_url": "https://ultimaker.com/firmware?utm_source=cura&utm_medium=software&utm_campaign=fw-update"
|
||||
},
|
||||
"first_start_actions": [ "DiscoverUM3Action" ],
|
||||
"has_machine_quality": true,
|
||||
"has_materials": true,
|
||||
"has_variants": true,
|
||||
"machine_extruder_trains":
|
||||
{
|
||||
"0": "ultimaker_s6_extruder_left",
|
||||
"1": "ultimaker_s6_extruder_right"
|
||||
},
|
||||
"nozzle_offsetting_for_disallowed_areas": false,
|
||||
"platform_offset": [
|
||||
0,
|
||||
-30,
|
||||
-10
|
||||
],
|
||||
"platform_texture": "UltimakerS6backplate.png",
|
||||
"preferred_material": "ultimaker_pla_blue",
|
||||
"preferred_variant_name": "AA+ 0.4",
|
||||
"quality_definition": "ultimaker_s8",
|
||||
"supported_actions": [ "DiscoverUM3Action" ],
|
||||
"supports_material_export": true,
|
||||
"supports_network_connection": true,
|
||||
"supports_usb_connection": false,
|
||||
"variants_name": "Print Core",
|
||||
"variants_name_has_translation": true,
|
||||
"weight": -2
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"adhesion_type": { "value": "'brim'" },
|
||||
"machine_name": { "default_value": "Ultimaker S6" }
|
||||
}
|
||||
}
|
||||
|
|
@ -46,8 +46,6 @@
|
|||
},
|
||||
"overrides":
|
||||
{
|
||||
"default_material_print_temperature": { "maximum_value_warning": "320" },
|
||||
"machine_name": { "default_value": "Ultimaker S7" },
|
||||
"material_print_temperature_layer_0": { "maximum_value_warning": "320" }
|
||||
"machine_name": { "default_value": "Ultimaker S7" }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,28 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Ultimaker S8",
|
||||
"inherits": "ultimaker_s7",
|
||||
"name": "UltiMaker S8",
|
||||
"inherits": "ultimaker_s5",
|
||||
"metadata":
|
||||
{
|
||||
"visible": true,
|
||||
"author": "Ultimaker",
|
||||
"author": "UltiMaker",
|
||||
"manufacturer": "Ultimaker B.V.",
|
||||
"file_formats": "application/x-ufp;text/x-gcode",
|
||||
"platform": "ultimaker_s7_platform.obj",
|
||||
"bom_numbers": [
|
||||
10600
|
||||
],
|
||||
"exclude_materials": [
|
||||
"generic_hips",
|
||||
"generic_flexible",
|
||||
"generic_cffpps",
|
||||
"generic_cffpa",
|
||||
"generic_cffcpe",
|
||||
"generic_gffpa",
|
||||
"generic_gffcpe",
|
||||
"structur3d_",
|
||||
"ultimaker_ppscf"
|
||||
],
|
||||
"firmware_update_info":
|
||||
{
|
||||
"check_urls": [ "https://software.ultimaker.com/releases/firmware/10600/stable/um-update.swu.version" ],
|
||||
|
|
@ -37,7 +48,6 @@
|
|||
"preferred_material": "ultimaker_pla_blue",
|
||||
"preferred_quality_type": "draft",
|
||||
"preferred_variant_name": "AA+ 0.4",
|
||||
"quality_definition": "ultimaker_s8",
|
||||
"supported_actions": [ "DiscoverUM3Action" ],
|
||||
"supports_material_export": true,
|
||||
"supports_network_connection": true,
|
||||
|
|
@ -48,29 +58,147 @@
|
|||
},
|
||||
"overrides":
|
||||
{
|
||||
"acceleration_infill": { "value": "acceleration_print" },
|
||||
"acceleration_layer_0": { "value": 2000 },
|
||||
"acceleration_prime_tower": { "value": "acceleration_print" },
|
||||
"acceleration_print": { "value": 20000 },
|
||||
"acceleration_print_layer_0": { "value": "acceleration_layer_0" },
|
||||
"acceleration_roofing": { "value": "acceleration_wall_0" },
|
||||
"acceleration_skirt_brim": { "value": "acceleration_layer_0" },
|
||||
"acceleration_support": { "value": "acceleration_print" },
|
||||
"acceleration_support_bottom": { "value": "acceleration_support_interface" },
|
||||
"acceleration_support_infill": { "value": "acceleration_support" },
|
||||
"acceleration_support_interface": { "value": "acceleration_support" },
|
||||
"acceleration_support_roof": { "value": "acceleration_support_interface" },
|
||||
"acceleration_topbottom": { "value": "acceleration_print" },
|
||||
"acceleration_travel": { "value": 10000 },
|
||||
"acceleration_flooring":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_roofing"
|
||||
},
|
||||
"acceleration_infill":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print"
|
||||
},
|
||||
"acceleration_layer_0":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": 2000
|
||||
},
|
||||
"acceleration_prime_tower":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print"
|
||||
},
|
||||
"acceleration_print":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": 20000
|
||||
},
|
||||
"acceleration_print_layer_0":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_layer_0"
|
||||
},
|
||||
"acceleration_roofing":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall_0"
|
||||
},
|
||||
"acceleration_skirt_brim":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_layer_0"
|
||||
},
|
||||
"acceleration_support":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print"
|
||||
},
|
||||
"acceleration_support_bottom":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_support_interface"
|
||||
},
|
||||
"acceleration_support_infill":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_support"
|
||||
},
|
||||
"acceleration_support_interface":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_support"
|
||||
},
|
||||
"acceleration_support_roof":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_support_interface"
|
||||
},
|
||||
"acceleration_topbottom":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print"
|
||||
},
|
||||
"acceleration_travel":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": 10000
|
||||
},
|
||||
"acceleration_travel_enabled": { "value": true },
|
||||
"acceleration_travel_layer_0": { "value": "acceleration_layer_0" },
|
||||
"acceleration_wall": { "value": "acceleration_print/8" },
|
||||
"acceleration_wall_0": { "value": "acceleration_wall" },
|
||||
"acceleration_wall_0_roofing": { "value": "acceleration_wall_0" },
|
||||
"acceleration_wall_x": { "value": "acceleration_print" },
|
||||
"acceleration_wall_x_roofing": { "value": "acceleration_wall" },
|
||||
"adhesion_type": { "value": "'skirt'" },
|
||||
"bottom_thickness": { "value": "3*layer_height if top_layers==4 else top_bottom_thickness" },
|
||||
"acceleration_travel_layer_0":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_layer_0"
|
||||
},
|
||||
"acceleration_wall":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print/8"
|
||||
},
|
||||
"acceleration_wall_0":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall"
|
||||
},
|
||||
"acceleration_wall_0_flooring":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall_0_roofing"
|
||||
},
|
||||
"acceleration_wall_0_roofing":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall_0"
|
||||
},
|
||||
"acceleration_wall_x":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_print"
|
||||
},
|
||||
"acceleration_wall_x_flooring":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall_x_roofing"
|
||||
},
|
||||
"acceleration_wall_x_roofing":
|
||||
{
|
||||
"maximum_value": "machine_max_acceleration_x",
|
||||
"maximum_value_warning": "machine_max_acceleration_x*0.8",
|
||||
"value": "acceleration_wall"
|
||||
},
|
||||
"adhesion_type": { "value": "'brim' if support_enable and support_structure=='tree' else 'skirt'" },
|
||||
"bottom_thickness": { "value": "3*layer_height if top_layers==4 and not support_enable else top_bottom_thickness" },
|
||||
"bridge_skin_material_flow": { "value": 200 },
|
||||
"bridge_skin_speed":
|
||||
{
|
||||
|
|
@ -85,20 +213,40 @@
|
|||
"unit": "mm/s",
|
||||
"value": 50
|
||||
},
|
||||
"build_volume_temperature":
|
||||
{
|
||||
"force_depends_on_settings": [
|
||||
"support_extruder_nr",
|
||||
"support_enable"
|
||||
]
|
||||
},
|
||||
"cool_during_extruder_switch": { "value": "'all_fans'" },
|
||||
"cool_min_layer_time": { "value": 5 },
|
||||
"cool_min_layer_time_overhang": { "value": 9 },
|
||||
"cool_min_layer_time_overhang_min_segment_length": { "value": 2 },
|
||||
"cool_min_speed": { "value": 6 },
|
||||
"cool_min_temperature": { "value": "material_print_temperature-15" },
|
||||
"cool_min_temperature":
|
||||
{
|
||||
"minimum_value_warning": "material_print_temperature-15",
|
||||
"value": "material_print_temperature-15"
|
||||
},
|
||||
"default_material_print_temperature": { "maximum_value_warning": 320 },
|
||||
"extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" },
|
||||
"flooring_layer_count": { "value": 1 },
|
||||
"gradual_flow_enabled": { "value": false },
|
||||
"hole_xy_offset": { "value": 0.075 },
|
||||
"infill_material_flow": { "value": "material_flow" },
|
||||
"infill_overlap": { "value": 10 },
|
||||
"infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'grid'" },
|
||||
"infill_sparse_density": { "value": 15 },
|
||||
"infill_wall_line_count": { "value": "1 if infill_sparse_density > 80 else 0" },
|
||||
"initial_bottom_layers": { "value": 2 },
|
||||
"jerk_flooring":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_roofing"
|
||||
},
|
||||
"jerk_infill":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
|
|
@ -115,7 +263,7 @@
|
|||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_print"
|
||||
"value": "jerk_wall"
|
||||
},
|
||||
"jerk_print":
|
||||
{
|
||||
|
|
@ -202,6 +350,12 @@
|
|||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_wall"
|
||||
},
|
||||
"jerk_wall_0_flooring":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_wall_0_roofing"
|
||||
},
|
||||
"jerk_wall_0_roofing":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
|
|
@ -214,6 +368,12 @@
|
|||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_print"
|
||||
},
|
||||
"jerk_wall_x_flooring":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
"unit": "m/s\u00b3",
|
||||
"value": "jerk_wall_x_roofing"
|
||||
},
|
||||
"jerk_wall_x_roofing":
|
||||
{
|
||||
"maximum_value_warning": "machine_max_jerk_xy / 2",
|
||||
|
|
@ -221,6 +381,8 @@
|
|||
"value": "jerk_wall_0"
|
||||
},
|
||||
"machine_gcode_flavor": { "default_value": "Cheetah" },
|
||||
"machine_max_acceleration_x": { "default_value": 50000 },
|
||||
"machine_max_acceleration_y": { "default_value": 50000 },
|
||||
"machine_max_feedrate_x": { "default_value": 500 },
|
||||
"machine_max_feedrate_y": { "default_value": 500 },
|
||||
"machine_max_jerk_e":
|
||||
|
|
@ -245,6 +407,13 @@
|
|||
"machine_nozzle_cool_down_speed": { "default_value": 1.3 },
|
||||
"machine_nozzle_heat_up_speed": { "default_value": 0.6 },
|
||||
"machine_start_gcode": { "default_value": "M213 U0.1 ;undercut 0.1mm" },
|
||||
"material_bed_temperature":
|
||||
{
|
||||
"force_depends_on_settings": [
|
||||
"support_extruder_nr",
|
||||
"support_enable"
|
||||
]
|
||||
},
|
||||
"material_extrusion_cool_down_speed": { "value": 0 },
|
||||
"material_final_print_temperature": { "value": "material_print_temperature - 5" },
|
||||
"material_initial_print_temperature": { "value": "material_print_temperature - 5" },
|
||||
|
|
@ -253,6 +422,7 @@
|
|||
"enabled": true,
|
||||
"value": 0.5
|
||||
},
|
||||
"material_print_temperature": { "maximum_value_warning": 320 },
|
||||
"material_print_temperature_layer_0": { "maximum_value_warning": 320 },
|
||||
"max_flow_acceleration": { "value": 8.0 },
|
||||
"max_skin_angle_for_expansion": { "value": 45 },
|
||||
|
|
@ -261,18 +431,20 @@
|
|||
"optimize_wall_printing_order": { "value": false },
|
||||
"prime_tower_brim_enable": { "value": true },
|
||||
"prime_tower_min_volume": { "value": 10 },
|
||||
"prime_tower_mode": { "resolve": "'normal'" },
|
||||
"retraction_amount": { "value": 6.5 },
|
||||
"retraction_combing_avoid_distance": { "value": 1.2 },
|
||||
"retraction_combing_max_distance": { "value": 100 },
|
||||
"retraction_hop": { "value": 0.2 },
|
||||
"retraction_combing_max_distance": { "value": 50 },
|
||||
"retraction_hop": { "value": 1 },
|
||||
"retraction_hop_after_extruder_switch_height": { "value": 2 },
|
||||
"retraction_hop_enabled": { "value": true },
|
||||
"retraction_min_travel": { "value": "5 if support_enable and support_structure=='tree' else line_width * 2" },
|
||||
"retraction_min_travel": { "value": "5 if support_enable and support_structure=='tree' else line_width * 2.5" },
|
||||
"retraction_prime_speed": { "value": 15 },
|
||||
"skin_edge_support_thickness": { "value": 0 },
|
||||
"skin_material_flow": { "value": 95 },
|
||||
"skin_overlap": { "value": 0 },
|
||||
"skin_preshrink": { "value": 0 },
|
||||
"skirt_brim_minimal_length": { "value": 1000 },
|
||||
"skirt_brim_speed":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
|
|
@ -281,6 +453,11 @@
|
|||
"skirt_line_count": { "value": 5 },
|
||||
"small_skin_on_surface": { "value": false },
|
||||
"small_skin_width": { "value": 4 },
|
||||
"speed_flooring":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_roofing"
|
||||
},
|
||||
"speed_infill":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
|
|
@ -294,12 +471,12 @@
|
|||
"speed_layer_0":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall"
|
||||
"value": "min(speed_wall, 50)"
|
||||
},
|
||||
"speed_prime_tower":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall"
|
||||
"value": "min(speed_wall, 50)"
|
||||
},
|
||||
"speed_print":
|
||||
{
|
||||
|
|
@ -319,7 +496,7 @@
|
|||
"speed_support":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall_0"
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_support_bottom":
|
||||
{
|
||||
|
|
@ -334,7 +511,7 @@
|
|||
"speed_support_interface":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": 50
|
||||
"value": 80
|
||||
},
|
||||
"speed_support_roof":
|
||||
{
|
||||
|
|
@ -349,11 +526,13 @@
|
|||
"speed_travel":
|
||||
{
|
||||
"maximum_value": 500,
|
||||
"maximum_value_warning": 500,
|
||||
"value": 500
|
||||
},
|
||||
"speed_travel_layer_0":
|
||||
{
|
||||
"maximum_value": 500,
|
||||
"maximum_value_warning": 500,
|
||||
"value": 150
|
||||
},
|
||||
"speed_wall":
|
||||
|
|
@ -366,6 +545,11 @@
|
|||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"speed_wall_0_flooring":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall_0_roofing"
|
||||
},
|
||||
"speed_wall_0_roofing":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
|
|
@ -376,19 +560,39 @@
|
|||
"maximum_value_warning": 300,
|
||||
"value": "speed_print"
|
||||
},
|
||||
"speed_wall_x_flooring":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall_x_roofing"
|
||||
},
|
||||
"speed_wall_x_roofing":
|
||||
{
|
||||
"maximum_value_warning": 300,
|
||||
"value": "speed_wall"
|
||||
},
|
||||
"support_brim_line_count": { "value": 5 },
|
||||
"support_infill_rate": { "value": "80 if gradual_support_infill_steps != 0 else 15" },
|
||||
"support_angle": { "value": 60 },
|
||||
"support_bottom_distance": { "maximum_value_warning": "3*layer_height" },
|
||||
"support_bottom_offset": { "value": 0 },
|
||||
"support_brim_width": { "value": 10 },
|
||||
"support_interface_enable": { "value": true },
|
||||
"support_interface_offset": { "value": "support_offset" },
|
||||
"support_line_width": { "value": "1.25*line_width" },
|
||||
"support_offset": { "value": "1.2 if support_structure == 'tree' else 0.8" },
|
||||
"support_pattern": { "value": "'gyroid' if support_structure == 'tree' else 'lines'" },
|
||||
"support_roof_height": { "minimum_value_warning": 0 },
|
||||
"support_structure": { "value": "'normal'" },
|
||||
"support_top_distance": { "maximum_value_warning": "3*layer_height" },
|
||||
"support_tree_angle": { "value": 50 },
|
||||
"support_tree_angle_slow": { "value": 35 },
|
||||
"support_tree_bp_diameter": { "value": 15 },
|
||||
"support_tree_branch_diameter": { "value": 8 },
|
||||
"support_tree_tip_diameter": { "value": 1.0 },
|
||||
"support_tree_top_rate": { "value": 20 },
|
||||
"support_xy_distance_overhang": { "value": "machine_nozzle_size" },
|
||||
"support_z_distance": { "value": "0.4*material_shrinkage_percentage_z/100.0" },
|
||||
"top_bottom_thickness": { "value": "round(4*layer_height, 2)" },
|
||||
"travel_avoid_other_parts": { "value": false },
|
||||
"travel_avoid_other_parts": { "value": true },
|
||||
"travel_avoid_supports": { "value": true },
|
||||
"wall_0_acceleration": { "value": 1000 },
|
||||
"wall_0_deceleration": { "value": 1000 },
|
||||
"wall_0_end_speed_ratio": { "value": 100 },
|
||||
|
|
@ -398,6 +602,7 @@
|
|||
"wall_material_flow": { "value": 95 },
|
||||
"wall_overhang_angle": { "value": 45 },
|
||||
"wall_x_material_flow": { "value": 100 },
|
||||
"xy_offset": { "value": 0.05 },
|
||||
"z_seam_corner": { "value": "'z_seam_corner_weighted'" },
|
||||
"z_seam_position": { "value": "'backright'" },
|
||||
"z_seam_type": { "value": "'sharpest_corner'" }
|
||||
|
|
|
|||
|
|
@ -55,10 +55,6 @@
|
|||
"basf_",
|
||||
"jabil_",
|
||||
"polymaker_",
|
||||
"ultimaker_rapidrinse",
|
||||
"ultimaker_sr30",
|
||||
"ultimaker_petg",
|
||||
"ultimaker_pva",
|
||||
"ultimaker_pc-abs",
|
||||
"ultimaker_pc-abs-fr"
|
||||
],
|
||||
|
|
@ -66,6 +62,7 @@
|
|||
"has_materials": true,
|
||||
"has_variants": true,
|
||||
"machine_extruder_trains": { "0": "ultimaker_sketch_extruder" },
|
||||
"preferred_material": "ultimaker_pla_175",
|
||||
"preferred_quality_type": "draft",
|
||||
"preferred_variant_name": "0.4mm",
|
||||
"reference_machine_id": "sketch",
|
||||
|
|
|
|||
|
|
@ -342,10 +342,12 @@
|
|||
"value": 20
|
||||
},
|
||||
"support_angle": { "value": "60" },
|
||||
"support_bottom_density": { "value": "support_infill_rate" },
|
||||
"support_bottom_distance": { "value": "layer_height" },
|
||||
"support_bottom_density": { "value": "80 if support_structure == 'tree' else support_infill_rate" },
|
||||
"support_bottom_distance": { "value": 0.2 },
|
||||
"support_bottom_enable": { "value": true },
|
||||
"support_bottom_height": { "value": "layer_height * 2" },
|
||||
"support_bottom_height": { "value": "layer_height if support_structure == 'tree' else layer_height * 2" },
|
||||
"support_bottom_offset": { "value": "0 if support_structure == 'tree' else support_interface_offset" },
|
||||
"support_bottom_wall_count": { "value": "1 if support_structure == 'tree' else 0" },
|
||||
"support_brim_line_count": { "value": 5 },
|
||||
"support_infill_angles":
|
||||
{
|
||||
|
|
@ -371,7 +373,7 @@
|
|||
"support_use_towers": { "value": false },
|
||||
"support_xy_distance": { "value": 0.3 },
|
||||
"support_xy_overrides_z": { "value": "'xy_overrides_z'" },
|
||||
"support_z_distance": { "value": "layer_height if support_structure == 'tree' else 0.25" },
|
||||
"support_z_distance": { "value": "0.2 if support_structure == 'tree' else 0.25" },
|
||||
"top_bottom_thickness": { "value": "5 * layer_height" },
|
||||
"top_layers": { "value": 5 },
|
||||
"travel_avoid_distance": { "value": 0.625 },
|
||||
|
|
@ -395,8 +397,6 @@
|
|||
"z_seam_corner": { "value": "'z_seam_corner_inner'" },
|
||||
"z_seam_position": { "value": "'backleft'" },
|
||||
"z_seam_type": { "value": "'sharpest_corner'" },
|
||||
"z_seam_x": { "value": 150 },
|
||||
"z_seam_y": { "value": 180 },
|
||||
"zig_zaggify_infill": { "value": true }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1_extruder_0.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1 extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X267 F18000\nG1 Y128 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1 extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1 extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S4\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1 extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1_extruder_1.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1",
|
||||
"position": "1"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 1 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1 extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X267 F18000\nG1 Y128 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1 extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1 extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S4\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1 extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1_extruder_2.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1",
|
||||
"position": "2"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 2 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1 extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X267 F18000\nG1 Y128 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1 extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1 extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S4\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1 extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1_extruder_3.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1",
|
||||
"position": "3"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 3 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1 extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X267 F18000\nG1 Y128 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1 extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1 extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S4\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1 extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1mini_extruder_0.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1mini",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1mini extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X180 F18000\nG1 Y90 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1mini extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1mini extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E5 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1mini extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1mini_extruder_1.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1mini",
|
||||
"position": "1"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 1 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1mini extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X180 F18000\nG1 Y90 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1mini extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1mini extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E5 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1mini extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1mini_extruder_2.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1mini",
|
||||
"position": "2"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 2 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1mini extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X180 F18000\nG1 Y90 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1mini extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1mini extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E5 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1mini extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
19
resources/extruders/bambulab_a1mini_extruder_3.def.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_a1mini",
|
||||
"position": "3"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 3 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": ";===== A1mini extruder end {extruder_nr} begin =====\nG392 S0\nM1007 S0 ; turn off mass estimation\nM204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X180 F18000\nG1 Y90 F9000\n\nM400\nM106 P1 S0\nM106 P2 S0\n{if material_print_temperature > 142, extruder_nr}\nM104 S{material_print_temperature, extruder_nr}\n{endif}\n\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A0 F{material_flush_purge_speed}\n\nM628 S1\nG92 E0\nG1 E-18 F{material_flush_purge_speed}\nM400\nM629 S1\n\n;===== A1mini extruder end {extruder_nr} finish =====\n" },
|
||||
"machine_extruder_start_code": { "default_value": ";===== A1mini extruder start {extruder_nr} begin =====\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\nM620.10 A1 F{material_flush_purge_speed} L{material_flush_purge_length} H{machine_nozzle_size} T{material_print_temperature, extruder_nr}\n\nM400\nG92 E0\n\n{if not prime_tower_enable}\n\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n\nM400\nM106 P1 S60\nG1 E5 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n; G1 E-{retraction_amount} F1800\nM400\nM106 P1 S178\nM400 S3\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nG1 X-3.5 F18000\nG1 X-13.5 F3000\nM400\nM106 P1 S0\n\nM622.1 S0\n\nM621 S{extruder_nr}A\nG392 S0\n\nM1007 S1\n;===== A1mini extruder start {extruder_nr} finish =====\n" },
|
||||
"material_diameter": { "default_value": 1.75 },
|
||||
"switch_extruder_retraction_amount": { "default_value": 18 }
|
||||
}
|
||||
}
|
||||
18
resources/extruders/bambulab_x1_extruder_0.def.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_x1",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 0 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": "M204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n\n{if material_print_temperature > 142}\nM104 S{material_print_temperature}\n{endif}\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\nG1 X20 Y50 F21000\nG1 Y-3\n\n;{if toolchange_count == 2}\n; get travel path for change filament\n;M620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\n;M620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\n;M620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n;{endif}\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n" },
|
||||
"machine_extruder_start_code": { "default_value": "M620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n\nG92 E0\n\n; always use highest temperature to flush\n{if material_type == 'PETG'}\nM109 S260\n{elsif material_type == 'PVA'}\nM109 S210\n{else}\nM109 S{material_print_temperature}\n{endif}\n\n{if not prime_tower_enable}\n\nM83\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; FLUSH_START\nM400\nM109 S{material_print_temperature}\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n;G1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X105 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\n\nG1 X70 F10000\nG1 X80 F15000\nG1 X60\nG1 X80\nG1 X60\nG1 X80 ; shake to put down garbage\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\n\nM621 S{extruder_nr}A\n" },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
||||
18
resources/extruders/bambulab_x1_extruder_1.def.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_x1",
|
||||
"position": "1"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 1 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": "M204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n\n{if material_print_temperature > 142}\nM104 S{material_print_temperature}\n{endif}\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\nG1 X20 Y50 F21000\nG1 Y-3\n\n;{if toolchange_count == 2}\n; get travel path for change filament\n;M620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\n;M620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\n;M620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n;{endif}\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n" },
|
||||
"machine_extruder_start_code": { "default_value": "M620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n\nG92 E0\n\n; always use highest temperature to flush\n{if material_type == 'PETG'}\nM109 S260\n{elsif material_type == 'PVA'}\nM109 S210\n{else}\nM109 S{material_print_temperature}\n{endif}\n\n{if not prime_tower_enable}\n\nM83\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; FLUSH_START\nM400\nM109 S{material_print_temperature}\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n;G1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X105 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\n\nG1 X70 F10000\nG1 X80 F15000\nG1 X60\nG1 X80\nG1 X60\nG1 X80 ; shake to put down garbage\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\n\nM621 S{extruder_nr}A\n" },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
||||
18
resources/extruders/bambulab_x1_extruder_2.def.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_x1",
|
||||
"position": "2"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 2 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": "M204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n\n{if material_print_temperature > 142}\nM104 S{material_print_temperature}\n{endif}\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\nG1 X20 Y50 F21000\nG1 Y-3\n\n;{if toolchange_count == 2}\n; get travel path for change filament\n;M620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\n;M620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\n;M620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n;{endif}\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n" },
|
||||
"machine_extruder_start_code": { "default_value": "M620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n\nG92 E0\n\n; always use highest temperature to flush\n{if material_type == 'PETG'}\nM109 S260\n{elsif material_type == 'PVA'}\nM109 S210\n{else}\nM109 S{material_print_temperature}\n{endif}\n\n{if not prime_tower_enable}\n\nM83\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; FLUSH_START\nM400\nM109 S{material_print_temperature}\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n;G1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X105 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\n\nG1 X70 F10000\nG1 X80 F15000\nG1 X60\nG1 X80\nG1 X60\nG1 X80 ; shake to put down garbage\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\n\nM621 S{extruder_nr}A\n" },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
||||
18
resources/extruders/bambulab_x1_extruder_3.def.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "bambulab_x1",
|
||||
"position": "3"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr": { "default_value": 3 },
|
||||
"machine_extruder_change_duration": { "default_value": 29 },
|
||||
"machine_extruder_end_code": { "default_value": "M204 S9000\n\nG91 ; set relative positioning\nG1 Z3.0 F1200\nG90 ; back to abolute positioning\n\nG1 X70 F21000\nG1 Y245\nG1 Y265 F3000\nM400\nM106 P1 S0\nM106 P2 S0\n\n{if material_print_temperature > 142}\nM104 S{material_print_temperature}\n{endif}\n\nM620.11 S1 I{extruder_nr} E-18 F1200\nM400\n\nG1 X90 F3000\nG1 Y255 F4000\nG1 X100 F5000\nG1 X120 F15000\nG1 X20 Y50 F21000\nG1 Y-3\n\n;{if toolchange_count == 2}\n; get travel path for change filament\n;M620.1 X[travel_point_1_x] Y[travel_point_1_y] F21000 P0\n;M620.1 X[travel_point_2_x] Y[travel_point_2_y] F21000 P1\n;M620.1 X[travel_point_3_x] Y[travel_point_3_y] F21000 P2\n;{endif}\n\nM620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n" },
|
||||
"machine_extruder_start_code": { "default_value": "M620.1 E F{material_flush_purge_speed} T{material_print_temperature, extruder_nr}\n\nG92 E0\n\n; always use highest temperature to flush\n{if material_type == 'PETG'}\nM109 S260\n{elsif material_type == 'PVA'}\nM109 S210\n{else}\nM109 S{material_print_temperature}\n{endif}\n\n{if not prime_tower_enable}\n\nM83\n; FLUSH_START\n; always use highest temperature to flush\nM400\nM1002 set_filament_type:UNKNOWN\nM109 S{material_print_temperature, extruder_nr}\nM106 P1 S60\nG1 E{material_flush_purge_length / 4.0} F{min(extruderValues('material_flush_purge_speed'))} ; do not need pulsatile flushing for start part\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{min(extruderValues('material_flush_purge_speed'))}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.23} F{material_flush_purge_speed, extruder_nr}\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\nM400\nM1002 set_filament_type:{material_type, extruder_nr}\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; WIPE\nM400\nM106 P1 S178\nM400 S3\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nG1 X-38.2 F18000\nG1 X-48.2 F3000\nM400\nM106 P1 S0\n\nM106 P1 S60\n; FLUSH_START\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\nG1 E{(material_flush_purge_length / 4.0) * 0.18} F{material_flush_purge_speed, extruder_nr}\nG1 E{(material_flush_purge_length / 4.0) * 0.02} F50\n; FLUSH_END\nG1 E-{retraction_amount * 2} F1800\nG1 E{retraction_amount * 2} F300\n\n; FLUSH_START\nM400\nM109 S{material_print_temperature}\nG1 E6 F{material_flush_purge_speed, extruder_nr} ;Compensate for filament spillage during waiting temperature\n; FLUSH_END\n\n{endif} ; prime_tower_enable\n\nM400\nG92 E0\n;G1 E-[new_retract_length_toolchange] F1800\nM106 P1 S255\nM400 S3\n\nG1 X70 F5000\nG1 X90 F3000\nG1 Y255 F4000\nG1 X105 F5000\nG1 Y265 F5000\nG1 X70 F10000\nG1 X100 F5000\nG1 X70 F10000\nG1 X100 F5000\n\nG1 X70 F10000\nG1 X80 F15000\nG1 X60\nG1 X80\nG1 X60\nG1 X80 ; shake to put down garbage\nG1 X100 F5000\nG1 X165 F15000; wipe and shake\nG1 Y256 ; move Y to aside, prevent collision\nM400\n\nM621 S{extruder_nr}A\n" },
|
||||
"material_diameter": { "default_value": 1.75 }
|
||||
}
|
||||
}
|
||||
31
resources/extruders/ultimaker_s6_extruder_left.def.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 1",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "ultimaker_s6",
|
||||
"position": "0"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr":
|
||||
{
|
||||
"default_value": 0,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"extruder_prime_pos_x": { "default_value": -3 },
|
||||
"extruder_prime_pos_y": { "default_value": 6 },
|
||||
"extruder_prime_pos_z": { "default_value": 2 },
|
||||
"machine_extruder_end_pos_abs": { "default_value": true },
|
||||
"machine_extruder_end_pos_x": { "default_value": 330 },
|
||||
"machine_extruder_end_pos_y": { "default_value": 237 },
|
||||
"machine_extruder_start_code": { "value": "\"M214 D0 K{material_pressure_advance_factor} R0.04\"" },
|
||||
"machine_extruder_start_pos_abs": { "default_value": true },
|
||||
"machine_extruder_start_pos_x": { "default_value": 330 },
|
||||
"machine_extruder_start_pos_y": { "default_value": 237 },
|
||||
"machine_nozzle_head_distance": { "default_value": 2.7 },
|
||||
"machine_nozzle_offset_x": { "default_value": 0 },
|
||||
"machine_nozzle_offset_y": { "default_value": 0 }
|
||||
}
|
||||
}
|
||||
31
resources/extruders/ultimaker_s6_extruder_right.def.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Extruder 2",
|
||||
"inherits": "fdmextruder",
|
||||
"metadata":
|
||||
{
|
||||
"machine": "ultimaker_s6",
|
||||
"position": "1"
|
||||
},
|
||||
"overrides":
|
||||
{
|
||||
"extruder_nr":
|
||||
{
|
||||
"default_value": 1,
|
||||
"maximum_value": "1"
|
||||
},
|
||||
"extruder_prime_pos_x": { "default_value": 333 },
|
||||
"extruder_prime_pos_y": { "default_value": 6 },
|
||||
"extruder_prime_pos_z": { "default_value": 2 },
|
||||
"machine_extruder_end_pos_abs": { "default_value": true },
|
||||
"machine_extruder_end_pos_x": { "default_value": 330 },
|
||||
"machine_extruder_end_pos_y": { "default_value": 219 },
|
||||
"machine_extruder_start_code": { "value": "\"M214 D0 K{material_pressure_advance_factor} R0.04\"" },
|
||||
"machine_extruder_start_pos_abs": { "default_value": true },
|
||||
"machine_extruder_start_pos_x": { "default_value": 330 },
|
||||
"machine_extruder_start_pos_y": { "default_value": 219 },
|
||||
"machine_nozzle_head_distance": { "default_value": 4.2 },
|
||||
"machine_nozzle_offset_x": { "default_value": 22 },
|
||||
"machine_nozzle_offset_y": { "default_value": 0 }
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Cura 5.1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-10-09 14:27+0200\n"
|
||||
"POT-Creation-Date: 2025-02-21 15:37+0100\n"
|
||||
"PO-Revision-Date: 2023-09-03 18:15+0200\n"
|
||||
"Last-Translator: Miroslav Šustek <sustmidown@centrum.cz>\n"
|
||||
"Language-Team: DenyCZ <www.github.com/DenyCZ>\n"
|
||||
|
|
@ -18,6 +18,10 @@ msgstr ""
|
|||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n>=2 && n<=4 ? 1 : 2);\n"
|
||||
"X-Generator: Poedit 3.3.2\n"
|
||||
|
||||
msgctxt "@title:label"
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm."
|
||||
msgid "%(width).1f x %(depth).1f x %(height).1f mm"
|
||||
|
|
@ -179,6 +183,10 @@ msgctxt "@info:tooltip"
|
|||
msgid "3D View"
|
||||
msgstr "3D Pohled"
|
||||
|
||||
msgctxt "name"
|
||||
msgid "3DConnexion mouses"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@item:inlistbox"
|
||||
msgid "3MF File"
|
||||
msgstr "Soubor 3MF"
|
||||
|
|
@ -476,6 +484,10 @@ msgctxt "description"
|
|||
msgid "Allows loading and displaying G-code files."
|
||||
msgstr "Povoluje načítání a zobrazení souborů G kódu."
|
||||
|
||||
msgctxt "description"
|
||||
msgid "Allows working with 3D mouses inside Cura."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@option:discardOrKeep"
|
||||
msgid "Always ask me this"
|
||||
msgstr "Vždy se zeptat"
|
||||
|
|
@ -516,10 +528,6 @@ msgctxt "@option:radio"
|
|||
msgid "Anonymous crash reports"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Application framework"
|
||||
msgstr "Aplikační framework"
|
||||
|
||||
msgctxt "@title:column"
|
||||
msgid "Applies on"
|
||||
msgstr ""
|
||||
|
|
@ -698,10 +706,6 @@ msgctxt "@label Is followed by the name of an author"
|
|||
msgid "By"
|
||||
msgstr "Od"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "C/C++ Binding library"
|
||||
msgstr "Binding knihovna C/C++"
|
||||
|
||||
msgctxt "@item:inlistbox"
|
||||
msgid "COLLADA Digital Asset Exchange"
|
||||
msgstr "COLLADA Digital Asset Exchange"
|
||||
|
|
@ -882,10 +886,6 @@ msgctxt "@label"
|
|||
msgid "Compatibility Mode"
|
||||
msgstr "Mód kompatibility"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Compatibility between Python 2 and 3"
|
||||
msgstr "Kompatibilita mezi Python 2 a 3"
|
||||
|
||||
msgctxt "@title:label"
|
||||
msgid "Compatible Printers"
|
||||
msgstr "Kompatibilní tiskárny"
|
||||
|
|
@ -1271,10 +1271,6 @@ msgctxt "@info:title"
|
|||
msgid "Data Sent"
|
||||
msgstr "Data poslána"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Data interchange format"
|
||||
msgstr "Formát výměny dat"
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "Decline"
|
||||
msgstr "Odmítnout"
|
||||
|
|
@ -1335,10 +1331,6 @@ msgctxt "@label"
|
|||
msgid "Density"
|
||||
msgstr "Husttoa"
|
||||
|
||||
msgctxt "@label Description for development tool"
|
||||
msgid "Dependency and package manager"
|
||||
msgstr "Správce závislostí a balíčků"
|
||||
|
||||
msgctxt "@action:label"
|
||||
msgid "Depth (mm)"
|
||||
msgstr "Hloubka (mm)"
|
||||
|
|
@ -1606,6 +1598,10 @@ msgctxt "@action:label"
|
|||
msgid "Extruder %1"
|
||||
msgstr "Extruder %1"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Extruder Change duration"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@title:label"
|
||||
msgid "Extruder End G-code"
|
||||
msgstr "Ukončující G-kód extuderu"
|
||||
|
|
@ -1614,6 +1610,10 @@ msgctxt "@label"
|
|||
msgid "Extruder End G-code duration"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@title:label"
|
||||
msgid "Extruder Prestart G-code"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@title:label"
|
||||
msgid "Extruder Start G-code"
|
||||
msgstr "Počáteční G-kód extuderu"
|
||||
|
|
@ -1803,6 +1803,10 @@ msgctxt "@label"
|
|||
msgid "First available"
|
||||
msgstr "První dostupný"
|
||||
|
||||
msgctxt "@option:check"
|
||||
msgid "Flip model's toolhandle Y axis (restart required)"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@label:listbox"
|
||||
msgid "Flow"
|
||||
msgstr "Průtok"
|
||||
|
|
@ -1819,10 +1823,6 @@ msgctxt "@text"
|
|||
msgid "Following a few simple steps, you will be able to synchronize all your material profiles with your printers."
|
||||
msgstr "Následováním několika jednoduchých kroků budete moci synchronizovat všechny vaše materiálové profily s vašimi tiskárnami."
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Font"
|
||||
msgstr "Font"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "For every position; insert a piece of paper under the nozzle and adjust the print build plate height. The print build plate height is right when the paper is slightly gripped by the tip of the nozzle."
|
||||
msgstr "Pro každou pozici; vložte kousek papíru pod trysku a upravte výšku tiskové desky. Výška desky pro sestavení tisku je správná, když je papír lehce uchopen špičkou trysky."
|
||||
|
|
@ -1878,10 +1878,6 @@ msgctxt "@label"
|
|||
msgid "G-code flavor"
|
||||
msgstr "Varianta G kódu"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "G-code generator"
|
||||
msgstr "Generátor G kódu"
|
||||
|
||||
msgctxt "@error:not supported"
|
||||
msgid "GCodeGzWriter does not support text mode."
|
||||
msgstr "GCodeGzWriter nepodporuje textový mód."
|
||||
|
|
@ -1894,14 +1890,6 @@ msgctxt "@item:inlistbox"
|
|||
msgid "GIF Image"
|
||||
msgstr "Obrázek GIF"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "GUI framework"
|
||||
msgstr "GUI framework"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "GUI framework bindings"
|
||||
msgstr "Propojení GUI frameworku"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Gantry Height"
|
||||
msgstr "Výška rámu tiskárny"
|
||||
|
|
@ -1914,10 +1902,6 @@ msgctxt "@label"
|
|||
msgid "Generate structures to support parts of the model which have overhangs. Without these structures, these parts would collapse during printing."
|
||||
msgstr "Vytvořit struktury pro podporu částí modelu, které mají přesahy. Bez těchto struktur by se takové části během tisku zhroutily."
|
||||
|
||||
msgctxt "@label Description for development tool"
|
||||
msgid "Generating Windows installers"
|
||||
msgstr "Generování instalátorů pro Windows"
|
||||
|
||||
msgctxt "@label:category menu label"
|
||||
msgid "Generic"
|
||||
msgstr "Obecné"
|
||||
|
|
@ -1938,10 +1922,6 @@ msgctxt "@title:tab"
|
|||
msgid "Global Settings"
|
||||
msgstr "Globální nastavení"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Graphical user interface"
|
||||
msgstr "Grafické uživatelské prostředí"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Grid Placement"
|
||||
msgstr ""
|
||||
|
|
@ -2187,10 +2167,6 @@ msgctxt "@label"
|
|||
msgid "Interface"
|
||||
msgstr "Rozhranní"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Interprocess communication library"
|
||||
msgstr "Meziprocesní komunikační knihovna"
|
||||
|
||||
msgctxt "@title:window"
|
||||
msgid "Invalid IP address"
|
||||
msgstr "Špatná IP adresa"
|
||||
|
|
@ -2219,10 +2195,6 @@ msgctxt "@item:inlistbox"
|
|||
msgid "JPG Image"
|
||||
msgstr "Obrázek JPG"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "JSON parser"
|
||||
msgstr "JSON parser"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Job Name"
|
||||
msgstr "Název úlohy"
|
||||
|
|
@ -2323,6 +2295,10 @@ msgctxt "@action"
|
|||
msgid "Level build plate"
|
||||
msgstr "Vyrovnat podložku"
|
||||
|
||||
msgctxt "@title:window The argument is a package name, and the second is the version."
|
||||
msgid "License for %1 %2"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@item:inlistbox"
|
||||
msgid "Lighter is higher"
|
||||
msgstr "Světlejší je vyšší"
|
||||
|
|
@ -2339,10 +2315,6 @@ msgctxt "@item:inlistbox"
|
|||
msgid "Linear"
|
||||
msgstr "Lineární"
|
||||
|
||||
msgctxt "@label Description for development tool"
|
||||
msgid "Linux cross-distribution application deployment"
|
||||
msgstr "Sestavování Linux aplikací nezávislých na distribuci"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Load %3 as material %1 (This cannot be overridden)."
|
||||
msgstr "Načíst %3 jako materiál %1 (Toho nemůže být přepsáno)."
|
||||
|
|
@ -2431,6 +2403,10 @@ msgctxt "name"
|
|||
msgid "Makerbot Printfile Writer"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@item:inlistbox"
|
||||
msgid "Makerbot Replicator+ Printfile"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@item:inlistbox"
|
||||
msgid "Makerbot Sketch Printfile"
|
||||
msgstr ""
|
||||
|
|
@ -2840,10 +2816,6 @@ msgctxt "@label"
|
|||
msgid "Nozzle offset Y"
|
||||
msgstr "Y offset trysky"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Nozzle Size"
|
||||
msgstr "Velikost trysky"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Nozzle size"
|
||||
msgstr "Velikost trysky"
|
||||
|
|
@ -3019,10 +2991,6 @@ msgctxt "@header"
|
|||
msgid "Package details"
|
||||
msgstr "Detaily balíčku"
|
||||
|
||||
msgctxt "@label Description for development tool"
|
||||
msgid "Packaging Python-applications"
|
||||
msgstr "Vytváření balíčků Python aplikací"
|
||||
|
||||
msgctxt "@info:status"
|
||||
msgid "Parsing G-code"
|
||||
msgstr "Zpracovávám G kód"
|
||||
|
|
@ -3182,14 +3150,6 @@ msgctxt "@button"
|
|||
msgid "Plugins"
|
||||
msgstr "Zásuvné moduly"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Polygon clipping library"
|
||||
msgstr "Knihovna pro výstřižky z mnohoúhelníků"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Polygon packing library, developed by Prusa Research"
|
||||
msgstr "Knihovna pro plošnou optimalizaci vyvinutá Prusa Research"
|
||||
|
||||
msgctxt "@item:inmenu"
|
||||
msgid "Post Processing"
|
||||
msgstr "Post Processing"
|
||||
|
|
@ -3465,10 +3425,6 @@ msgctxt "@label"
|
|||
msgid "Profiles compatible with active printer:"
|
||||
msgstr "Profily kompatibilní s aktivní tiskárnou:"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Programming language"
|
||||
msgstr "Programovací jazyk"
|
||||
|
||||
#, python-brace-format
|
||||
msgctxt "@info:status Don't translate the XML tags <filename> or <message>!"
|
||||
msgid "Project file <filename>{0}</filename> contains an unknown machine type <message>{1}</message>. Cannot import the machine. Models will be imported instead."
|
||||
|
|
@ -3597,18 +3553,6 @@ msgctxt "@label"
|
|||
msgid "PyQt version"
|
||||
msgstr "Verze PyQt"
|
||||
|
||||
msgctxt "@Label Description for application dependency"
|
||||
msgid "Python Error tracking library"
|
||||
msgstr "Knihovna pro sledování Python chyb"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Python bindings for Clipper"
|
||||
msgstr "Propojení Clipper s jazykem Python"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Python bindings for libnest2d"
|
||||
msgstr "Propojení libnest2d s jazykem Python"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Qt version"
|
||||
msgstr "Verze Qt"
|
||||
|
|
@ -3790,10 +3734,6 @@ msgctxt "@info:tooltip"
|
|||
msgid "Right View"
|
||||
msgstr "Pohled zprava"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Root Certificates for validating SSL trustworthiness"
|
||||
msgstr "Kořenové certifikáty pro ověření důvěryhodnosti SSL"
|
||||
|
||||
msgctxt "@info:title"
|
||||
msgid "Safely Remove Hardware"
|
||||
msgstr "Bezpečně vysunout hardware"
|
||||
|
|
@ -3981,10 +3921,6 @@ msgctxt "name"
|
|||
msgid "Sentry Logger"
|
||||
msgstr "Záznamník hlavy"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Serial communication library"
|
||||
msgstr "Knihovna pro sériovou komunikaci"
|
||||
|
||||
msgctxt "@action:inmenu"
|
||||
msgid "Set as Active Extruder"
|
||||
msgstr "Nastavit jako aktivní extruder"
|
||||
|
|
@ -4093,6 +4029,10 @@ msgctxt "@info:tooltip"
|
|||
msgid "Should slicing crashes be automatically reported to Ultimaker? Note, no models, IP addresses or other personally identifiable information is sent or stored, unless you give explicit permission."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@info:tooltip"
|
||||
msgid "Should the Y axis of the translate toolhandle be flipped? This will only affect model's Y coordinate, all other settings such as machine Printhead settings are unaffected and still behave as before."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@info:tooltip"
|
||||
msgid "Should the build plate be cleared before loading a new model in the single instance of Cura?"
|
||||
msgstr "Má být podložka vyčištěna před načtením nového modelu v jediné instanci Cury?"
|
||||
|
|
@ -4329,6 +4269,10 @@ msgctxt "@title:label"
|
|||
msgid "Start G-code"
|
||||
msgstr "Počáteční G kód"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Start GCode must be first"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Start the slicing process"
|
||||
msgstr "Začít proces slicování"
|
||||
|
|
@ -4410,34 +4354,6 @@ msgctxt "@action:label"
|
|||
msgid "Support Type"
|
||||
msgstr "Typ podpory"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Support library for faster math"
|
||||
msgstr "Podpůrný knihovna pro rychlejší matematické výpočty"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Support library for file metadata and streaming"
|
||||
msgstr "Podpůrná knihovna pro metadata souborů a streaming"
|
||||
|
||||
msgctxt "@label Description for application component"
|
||||
msgid "Support library for handling 3MF files"
|
||||
msgstr "Podpůrná knihovna pro manipulaci s 3MF soubory"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Support library for handling STL files"
|
||||
msgstr "Podpůrná knihovna pro práci se soubory STL"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Support library for handling triangular meshes"
|
||||
msgstr "Podpůrná knihovna pro manipulaci s trojúhelníkovými sítěmi"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Support library for scientific computing"
|
||||
msgstr "Podpůrná knihovna pro vědecké výpočty"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Support library for system keyring access"
|
||||
msgstr "Podpůrná knihovna pro přístup k systémové klíčence"
|
||||
|
||||
msgctxt "@action:button"
|
||||
msgid "Sync"
|
||||
msgstr "Synchronizovat"
|
||||
|
|
@ -4763,6 +4679,10 @@ msgctxt "@label"
|
|||
msgid "This configuration is not available because %1 is not recognized. Please visit %2 to download the correct material profile."
|
||||
msgstr "Tato konfigurace není k dispozici, protože %1 nebyl rozpoznán. Navštivte prosím %2 a stáhněte si správný materiálový profil."
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "This configuration is not available because there is a mismatch or other problem with core-type %1. Please visit <a href='%2'>the support page</a> to check which cores this printer-type supports w.r.t. new slices."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@text:window"
|
||||
msgid "This is a Cura Universal project file. Would you like to open it as a Cura Universal Project or import the models from it?"
|
||||
msgstr ""
|
||||
|
|
@ -5136,10 +5056,6 @@ msgctxt "@action:description Don't translate 'Universal Cura Project'"
|
|||
msgid "Universal Cura Project files can be printed on different 3D printers while retaining positional data and selected settings. When exported, all models present on the build plate will be included along with their current position, orientation, and scale. You can also select which per-extruder or per-model settings should be included to ensure proper printing."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@label Description for development tool"
|
||||
msgid "Universal build system configuration"
|
||||
msgstr "Univerzální konfigurace překladu programů"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Unknown"
|
||||
msgstr "Neznámý"
|
||||
|
|
@ -5333,6 +5249,10 @@ msgctxt "description"
|
|||
msgid "Upgrades configurations from Cura 5.8 to Cura 5.9."
|
||||
msgstr ""
|
||||
|
||||
msgctxt "description"
|
||||
msgid "Upgrades configurations from Cura 5.9 to Cura 5.10"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@action:button"
|
||||
msgid "Upload custom Firmware"
|
||||
msgstr "Nahrát vlastní firmware"
|
||||
|
|
@ -5357,14 +5277,6 @@ msgctxt "@label"
|
|||
msgid "User Agreement"
|
||||
msgstr "Uživatelská dohoda"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Utility functions, including an image loader"
|
||||
msgstr "Pomocné funkce zahrnující načítání obrázků"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "Utility library, including Voronoi generation"
|
||||
msgstr "Pomocná knihovna zahrnující Voronoi generátor"
|
||||
|
||||
msgctxt "@title:column"
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
|
@ -5481,6 +5393,10 @@ msgctxt "name"
|
|||
msgid "Version Upgrade 5.8 to 5.9"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "name"
|
||||
msgid "Version Upgrade 5.9 to 5.10"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@button"
|
||||
msgid "View printers in Digital Factory"
|
||||
msgstr "Zobrazit tiskárny v Digital Factory"
|
||||
|
|
@ -5643,12 +5559,12 @@ msgid "Y (Depth)"
|
|||
msgstr "Y (Hloubka)"
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Y max"
|
||||
msgstr "Y max"
|
||||
msgid "Y max ( '+' towards front)"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@label"
|
||||
msgid "Y min"
|
||||
msgstr "Y min"
|
||||
msgid "Y min ( '-' towards back)"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "@info"
|
||||
msgid "Yes"
|
||||
|
|
@ -5742,10 +5658,6 @@ msgctxt "@label"
|
|||
msgid "Z (Height)"
|
||||
msgstr "Z (Výška)"
|
||||
|
||||
msgctxt "@label Description for application dependency"
|
||||
msgid "ZeroConf discovery library"
|
||||
msgstr "Knihovna ZeroConf discovery"
|
||||
|
||||
msgctxt "@action:button"
|
||||
msgid "Zoom toward mouse direction"
|
||||
msgstr "Přiblížit směrem k směru myši"
|
||||
|
|
@ -5811,18 +5723,38 @@ msgstr "Nepovedlo se stáhnout {} zásuvných modulů"
|
|||
#~ msgstr[1] "... a {0} další"
|
||||
#~ msgstr[2] "... a {0} dalších"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Application framework"
|
||||
#~ msgstr "Aplikační framework"
|
||||
|
||||
#~ msgctxt "@tooltip:button"
|
||||
#~ msgid "Become a 3D printing expert with UltiMaker e-learning."
|
||||
#~ msgstr "Staňte se expertem na 3D tisk díky UltiMaker e-learningu."
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "C/C++ Binding library"
|
||||
#~ msgstr "Binding knihovna C/C++"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Compatibility between Python 2 and 3"
|
||||
#~ msgstr "Kompatibilita mezi Python 2 a 3"
|
||||
|
||||
#~ msgctxt "@info:status"
|
||||
#~ msgid "Cura does not accurately display layers when Wire Printing is enabled."
|
||||
#~ msgstr "Když je aktivován síťový tisk, Cura přesně nezobrazuje vrstvy."
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Data interchange format"
|
||||
#~ msgstr "Formát výměny dat"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Default"
|
||||
#~ msgstr "Výchozí"
|
||||
|
||||
#~ msgctxt "@label Description for development tool"
|
||||
#~ msgid "Dependency and package manager"
|
||||
#~ msgstr "Správce závislostí a balíčků"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards."
|
||||
#~ msgstr "Umožňuje tisk okraje nebo voru. Tímto způsobem se kolem nebo pod objekt přidá plochá oblast, kterou lze snadno odříznout."
|
||||
|
|
@ -5831,6 +5763,30 @@ msgstr "Nepovedlo se stáhnout {} zásuvných modulů"
|
|||
#~ msgid "Error writing 3mf file."
|
||||
#~ msgstr "Chyba při zápisu 3mf file."
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Font"
|
||||
#~ msgstr "Font"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "G-code generator"
|
||||
#~ msgstr "Generátor G kódu"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "GUI framework"
|
||||
#~ msgstr "GUI framework"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "GUI framework bindings"
|
||||
#~ msgstr "Propojení GUI frameworku"
|
||||
|
||||
#~ msgctxt "@label Description for development tool"
|
||||
#~ msgid "Generating Windows installers"
|
||||
#~ msgstr "Generování instalátorů pro Windows"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Graphical user interface"
|
||||
#~ msgstr "Grafické uživatelské prostředí"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Hex"
|
||||
#~ msgstr "Hexadecimální"
|
||||
|
|
@ -5847,18 +5803,70 @@ msgstr "Nepovedlo se stáhnout {} zásuvných modulů"
|
|||
#~ msgid "Install missing material"
|
||||
#~ msgstr "Nainstalovat chybějící materiál"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Interprocess communication library"
|
||||
#~ msgstr "Meziprocesní komunikační knihovna"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "JSON parser"
|
||||
#~ msgstr "JSON parser"
|
||||
|
||||
#~ msgctxt "@label Description for development tool"
|
||||
#~ msgid "Linux cross-distribution application deployment"
|
||||
#~ msgstr "Sestavování Linux aplikací nezávislých na distribuci"
|
||||
|
||||
#~ msgctxt "@info:title"
|
||||
#~ msgid "Material profiles not installed"
|
||||
#~ msgstr "Materiálové profily nejsou nainstalovány"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Nozzle Size"
|
||||
#~ msgstr "Velikost trysky"
|
||||
|
||||
#~ msgctxt "@label Description for development tool"
|
||||
#~ msgid "Packaging Python-applications"
|
||||
#~ msgstr "Vytváření balíčků Python aplikací"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Polygon clipping library"
|
||||
#~ msgstr "Knihovna pro výstřižky z mnohoúhelníků"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Polygon packing library, developed by Prusa Research"
|
||||
#~ msgstr "Knihovna pro plošnou optimalizaci vyvinutá Prusa Research"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Programming language"
|
||||
#~ msgstr "Programovací jazyk"
|
||||
|
||||
#~ msgctxt "description"
|
||||
#~ msgid "Provides support for writing 3MF files."
|
||||
#~ msgstr "Poskytuje podporu pro psaní souborů 3MF."
|
||||
|
||||
#~ msgctxt "@Label Description for application dependency"
|
||||
#~ msgid "Python Error tracking library"
|
||||
#~ msgstr "Knihovna pro sledování Python chyb"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Python bindings for Clipper"
|
||||
#~ msgstr "Propojení Clipper s jazykem Python"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Python bindings for libnest2d"
|
||||
#~ msgstr "Propojení libnest2d s jazykem Python"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Root Certificates for validating SSL trustworthiness"
|
||||
#~ msgstr "Kořenové certifikáty pro ověření důvěryhodnosti SSL"
|
||||
|
||||
#~ msgctxt "@option"
|
||||
#~ msgid "Save Cura project and print file"
|
||||
#~ msgstr "Uložit projekt Cura a tiskový soubor UFP"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Serial communication library"
|
||||
#~ msgstr "Knihovna pro sériovou komunikaci"
|
||||
|
||||
#~ msgctxt "@action:inmenu"
|
||||
#~ msgid "Show Online Troubleshooting"
|
||||
#~ msgstr "Zobrazit online řešení problémů"
|
||||
|
|
@ -5871,6 +5879,34 @@ msgstr "Nepovedlo se stáhnout {} zásuvných modulů"
|
|||
#~ msgid "Slicing failed with an unexpected error. Please consider reporting a bug on our issue tracker."
|
||||
#~ msgstr "Slicování selhalo na neočekávané chybě. Zvažte, prosím, nahlášení chyby v našem issue trackeru."
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Support library for faster math"
|
||||
#~ msgstr "Podpůrný knihovna pro rychlejší matematické výpočty"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Support library for file metadata and streaming"
|
||||
#~ msgstr "Podpůrná knihovna pro metadata souborů a streaming"
|
||||
|
||||
#~ msgctxt "@label Description for application component"
|
||||
#~ msgid "Support library for handling 3MF files"
|
||||
#~ msgstr "Podpůrná knihovna pro manipulaci s 3MF soubory"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Support library for handling STL files"
|
||||
#~ msgstr "Podpůrná knihovna pro práci se soubory STL"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Support library for handling triangular meshes"
|
||||
#~ msgstr "Podpůrná knihovna pro manipulaci s trojúhelníkovými sítěmi"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Support library for scientific computing"
|
||||
#~ msgstr "Podpůrná knihovna pro vědecké výpočty"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Support library for system keyring access"
|
||||
#~ msgstr "Podpůrná knihovna pro přístup k systémové klíčence"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "The material used in this project is currently not installed in Cura.<br/>Install the material profile and reopen the project."
|
||||
#~ msgstr "Materiál použitý v tomto projektu není aktuálně nainstalován v Cuře.<br/>Nainstalujte materiálový profil a znovu otevřete projekt."
|
||||
|
|
@ -5882,3 +5918,27 @@ msgstr "Nepovedlo se stáhnout {} zásuvných modulů"
|
|||
#~ msgctxt "@error:zip"
|
||||
#~ msgid "The operating system does not allow saving a project file to this location or with this file name."
|
||||
#~ msgstr "Operační systém nepovoluje uložit soubor s projektem do tohoto umístění nebo pod tímto názvem."
|
||||
|
||||
#~ msgctxt "@label Description for development tool"
|
||||
#~ msgid "Universal build system configuration"
|
||||
#~ msgstr "Univerzální konfigurace překladu programů"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Utility functions, including an image loader"
|
||||
#~ msgstr "Pomocné funkce zahrnující načítání obrázků"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "Utility library, including Voronoi generation"
|
||||
#~ msgstr "Pomocná knihovna zahrnující Voronoi generátor"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Y max"
|
||||
#~ msgstr "Y max"
|
||||
|
||||
#~ msgctxt "@label"
|
||||
#~ msgid "Y min"
|
||||
#~ msgstr "Y min"
|
||||
|
||||
#~ msgctxt "@label Description for application dependency"
|
||||
#~ msgid "ZeroConf discovery library"
|
||||
#~ msgstr "Knihovna ZeroConf discovery"
|
||||
|
|
|
|||