Merge branch 'main' into main

This commit is contained in:
HellAholic 2024-08-30 11:18:25 +02:00 committed by GitHub
commit 7db3af745e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
758 changed files with 29951 additions and 7725 deletions

View File

@ -6,9 +6,9 @@ body:
attributes:
value: |
### ✨Try our improved Cura 5.7✨
Before filling out the report below, we want you to try the latest Cura 5.7 Beta.
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/tag/5.7.0-beta.1) ####
#### [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.
### Project File
@ -35,7 +35,7 @@ body:
- type: markdown
attributes:
value: |
We work hard on improving our slicing crashes. Our most recent release is 5.6.0.
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:

View File

@ -0,0 +1,40 @@
name: conan-package-resources
on:
push:
paths:
- '.github/workflows/conan-package-resources.yml'
- 'resources/definitions/**'
- 'resources/extruders/**'
- 'resources/images/**'
- 'resources/intent/**'
- 'resources/meshes/**'
- 'resources/quality/**'
- 'resources/variants/**'
- 'resources/conanfile.py'
branches:
- 'main'
- 'CURA-*'
- 'PP-*'
- 'NP-*'
- '[0-9].[0-9]*'
- '[0-9].[0-9][0-9]*'
env:
CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }}
CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }}
jobs:
conan-recipe-version:
uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-version.yml@main
with:
project_name: cura_resources
conan-package-export:
needs: [ conan-recipe-version ]
uses: ultimaker/cura-workflows/.github/workflows/conan-recipe-export.yml@main
with:
recipe_id_full: ${{ needs.conan-recipe-version.outputs.recipe_id_full }}
recipe_id_latest: ${{ needs.conan-recipe-version.outputs.recipe_id_latest }}
conan_recipe_root: "./resources/"
secrets: inherit

View File

@ -4,12 +4,20 @@ on:
push:
paths:
- 'plugins/**'
- 'resources/**'
- 'cura/**'
- 'resources/bundled_packages/**'
- 'resources/i18n/**'
- 'resources/qml/**'
- 'resources/setting_visibility/**'
- 'resources/shaders/**'
- 'resources/texts/**'
- 'resources/themes/**'
- 'resources/public_key.pem'
- 'resources/README_resources.txt'
- 'icons/**'
- 'tests/**'
- 'packaging/**'
- '.github/workflows/conan-*.yml'
- '.github/workflows/conan-package.yml'
- '.github/workflows/notify.yml'
- '.github/workflows/requirements-runner.txt'
- 'requirements*.txt'
@ -20,6 +28,7 @@ on:
- 'main'
- 'CURA-*'
- 'PP-*'
- 'NP-*'
- '[0-9].[0-9]*'
- '[0-9].[0-9][0-9]*'

View File

@ -30,6 +30,29 @@ on:
required: true
type: boolean
workflow_call:
inputs:
cura_conan_version:
default: 'cura/latest@ultimaker/testing'
required: true
type: string
conan_args:
default: ''
required: false
type: string
enterprise:
default: false
required: true
type: boolean
staging:
default: false
required: true
type: boolean
nightly:
default: false
required: true
type: boolean
schedule:
# Daily at 4:15 CET (main-branch) and 5:15 CET (release-branch)
- cron: '15 3 * * *'
@ -70,7 +93,7 @@ jobs:
enterprise: ${{ github.event.inputs.enterprise == 'true' }}
staging: ${{ github.event.inputs.staging == 'true' }}
architecture: X64
operating_system: ubuntu-22.04
operating_system: self-hosted-Ubuntu22-X64
secrets: inherit
macos-installer:
@ -109,7 +132,7 @@ jobs:
fetch-depth: 1
- name: Download the run info
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: linux-run-info
@ -151,13 +174,13 @@ jobs:
f.writelines(f"NIGHTLY_TIME={nightly_creation_time}\n")
- name: Download linux installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.LINUX }}-AppImage
path: installers
- name: Download linux installer jobs asc artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.LINUX }}-asc
path: installers
@ -175,13 +198,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download win msi installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.WIN_MSI }}-msi
path: installers
- name: Download win exe installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.WIN_EXE }}-exe
path: installers
@ -199,13 +222,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download MacOS (X64) dmg installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.MAC_X64_DMG }}-dmg
path: installers
- name: Download MacOS (X64) pkg installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.MAC_X64_PKG }}-pkg
path: installers
@ -223,13 +246,13 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download MacOS (ARM-64) dmg installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.MAC_ARM_DMG }}-dmg
path: installers
- name: Download MacOS (ARM-64) pkg installer jobs artifacts
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: ${{ steps.filename.outputs.MAC_ARM_PKG }}-pkg
path: installers

View File

@ -34,10 +34,11 @@ on:
operating_system:
description: 'OS'
required: true
default: 'ubuntu-22.04'
default: 'self-hosted-Ubuntu22-X64'
type: choice
options:
- ubuntu-22.04
- self-hosted-Ubuntu22-X64
jobs:
linux-installer:

View File

@ -40,7 +40,6 @@ on:
options:
- self-hosted-X64
- self-hosted-ARM64
- macos-11
- macos-12
jobs:

View File

@ -5,6 +5,9 @@ on:
path:
- "resources/**"
permissions:
contents: read
jobs:
printer-linter-diagnose:
name: Printer linter PR diagnose
@ -18,6 +21,7 @@ jobs:
- uses: technote-space/get-diff-action@v6
with:
DIFF_FILTER: AMRCD
PATTERNS: |
resources/+(extruders|definitions)/*.def.json
resources/+(intent|quality|variants)/**/*.inst.cfg
@ -41,11 +45,15 @@ jobs:
if: env.GIT_DIFF && !env.MATCHED_FILES
run: python printer-linter/src/terminal.py --diagnose --report printer-linter-result/fixes.yml ${{ env.GIT_DIFF_FILTERED }}
- name: Check Deleted Files(s)
if: env.GIT_DIFF
run: python printer-linter/src/terminal.py --deleted --report printer-linter-result/comment.md ${{ env.GIT_DIFF_FILTERED }}
- name: Save PR metadata
run: |
echo ${{ github.event.number }} > printer-linter-result/pr-id.txt
echo ${{ github.event.pull_request.head.repo.full_name }} > printer-linter-result/pr-head-repo.txt
echo ${{ github.event.pull_request.head.ref }} > printer-linter-result/pr-head-ref.txt
echo ${{ github.event.pull_request.head.sha }} > printer-linter-result/pr-head-sha.txt
- uses: actions/upload-artifact@v2
with:

View File

@ -6,76 +6,106 @@ on:
types: [completed]
jobs:
clang-tidy-results:
printer-linter-result:
# Trigger the job only if the previous (insecure) workflow completed successfully
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Download analysis results
uses: actions/github-script@v3.1.0
uses: actions/github-script@v7
with:
script: |
let artifacts = await github.actions.listWorkflowRunArtifacts({
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "printer-linter-result"
})[0];
let download = await github.actions.downloadArtifact({
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: "zip",
});
let fs = require("fs");
fs.writeFileSync("${{github.workspace}}/printer-linter-result.zip", Buffer.from(download.data));
- name: Set environment variables
run: |
mkdir printer-linter-result
unzip printer-linter-result.zip -d printer-linter-result
echo "pr_id=$(cat printer-linter-result/pr-id.txt)" >> $GITHUB_ENV
echo "pr_head_repo=$(cat printer-linter-result/pr-head-repo.txt)" >> $GITHUB_ENV
echo "pr_head_ref=$(cat printer-linter-result/pr-head-ref.txt)" >> $GITHUB_ENV
- uses: actions/checkout@v3
with:
repository: ${{ env.pr_head_repo }}
ref: ${{ env.pr_head_ref }}
persist-credentials: false
- name: Redownload analysis results
uses: actions/github-script@v3.1.0
with:
script: |
let artifacts = await github.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "printer-linter-result"
})[0];
let download = await github.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: "zip",
});
let fs = require("fs");
const fs = require("fs");
fs.writeFileSync("${{ github.workspace }}/printer-linter-result.zip", Buffer.from(download.data));
- name: Extract analysis results
run: |
mkdir printer-linter-result
unzip printer-linter-result.zip -d printer-linter-result
unzip -j printer-linter-result.zip -d printer-linter-result
- name: Set PR details environment variables
uses: actions/github-script@v7
with:
script: |
const assert = require("node:assert").strict;
const fs = require("fs");
function exportVar(varName, fileName, regEx) {
const val = fs.readFileSync("${{ github.workspace }}/printer-linter-result/" + fileName, {
encoding: "ascii"
}).trimEnd();
assert.ok(regEx.test(val), "Invalid value format for " + varName);
core.exportVariable(varName, val);
}
exportVar("PR_ID", "pr-id.txt", /^[0-9]+$/);
exportVar("PR_HEAD_REPO", "pr-head-repo.txt", /^[-./0-9A-Z_a-z]+$/);
exportVar("PR_HEAD_SHA", "pr-head-sha.txt", /^[0-9A-Fa-f]+$/);
fs.access("${{ github.workspace }}/printer-linter-result/comment.md", fs.constants.F_OK, (err) => {
if (err) {
core.exportVariable("commentFileExists", "false");
} else {
core.exportVariable("commentFileExists", "true");
}
});
- uses: actions/checkout@v4
with:
repository: ${{ env.PR_HEAD_REPO }}
ref: ${{ env.PR_HEAD_SHA }}
persist-credentials: false
- name: Redownload analysis results
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "printer-linter-result"
})[0];
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: "zip",
});
const fs = require("fs");
fs.writeFileSync("${{ github.workspace }}/printer-linter-result.zip", Buffer.from(download.data));
- name: Extract analysis results
run: |
mkdir printer-linter-result
unzip -j printer-linter-result.zip -d printer-linter-result
- name: Run PR Comments
if: env.commentFileExists == 'true'
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ env.PR_ID }}
body-path: 'printer-linter-result/comment.md'
- name: Run clang-tidy-pr-comments action
uses: platisd/clang-tidy-pr-comments@bc0bb7da034a8317d54e7fe1e819159002f4cc40
uses: platisd/clang-tidy-pr-comments@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
clang_tidy_fixes: printer-linter-result/fixes.yml
pull_request_id: ${{ env.pr_id }}
pull_request_id: ${{ env.PR_ID }}
request_changes: true

View File

@ -0,0 +1,32 @@
name: Feature Freeze
run-name: Feature freeze Cura ${{ inputs.cura_version }} by @${{ github.actor }}
on:
workflow_dispatch:
inputs:
cura_version:
description: 'Cura version major and minor, e.g. 5.7'
required: true
type: string
jobs:
parse-version:
name: Parse input version string
runs-on: ubuntu-latest
outputs:
package_version: ${{ steps.version_parser.outputs.major }}.${{ steps.version_parser.outputs.minor }}.0-alpha.1
steps:
- name: Parse version string
id: version_parser
uses: booxmedialtd/ws-action-parse-semver@v1.4.7
with:
input_string: ${{ inputs.cura_version }}.0
feature-freeze:
name: Process feature freeze
uses: Ultimaker/Cura-workflows/.github/workflows/cura-set-packages-versions.yml@main
needs: [parse-version]
with:
cura_version: ${{ needs.parse-version.outputs.package_version }}
create_feature_branch: true
secrets: inherit

View File

@ -0,0 +1,187 @@
name: Prepare Release Candidate
run-name: Release Candidate for Cura ${{ inputs.cura_version }} by @${{ github.actor }}
on:
workflow_dispatch:
inputs:
cura_version:
description: 'Cura version number, e.g. 5.7.0, 5.7.2 or 5.8.0-beta.2'
required: true
type: string
jobs:
parse-version:
name: Parse input version string
runs-on: ubuntu-latest
outputs:
version_major: ${{ steps.version_parser.outputs.major }}
version_minor: ${{ steps.version_parser.outputs.minor }}
version_patch: ${{ steps.version_parser.outputs.patch }}
branch_name: ${{ steps.version_parser.outputs.major }}.${{ steps.version_parser.outputs.minor }}
steps:
- name: Parse version string
id: version_parser
uses: booxmedialtd/ws-action-parse-semver@v1.4.7
with:
input_string: ${{ inputs.cura_version }}
freeze-packages-versions:
name: Freeze packges versions
uses: Ultimaker/Cura-workflows/.github/workflows/cura-set-packages-versions.yml@main
needs: [parse-version]
with:
cura_version: ${{ inputs.cura_version }}
create_feature_branch: false
secrets: inherit
find-rc-tag:
name: Find RC tag name
runs-on: ubuntu-latest
needs: [freeze-packages-versions]
outputs:
tag_name: ${{ steps.find-available-tag-name.outputs.tag_name }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-tags: true
fetch-depth: 0
- name: Find available tag name
id: find-available-tag-name
run: |
VERSION=${{ inputs.cura_version }}
RC_INDEX=0
while
RC_INDEX=$((RC_INDEX+1))
TAG_NAME="$VERSION-RC$RC_INDEX"
[[ $(git tag -l "$TAG_NAME") ]]
do true; done
echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
create-tags:
name: Create tags
runs-on: ubuntu-latest
needs: [parse-version, find-rc-tag]
outputs:
main_commit: ${{ steps.export-main-commit.outputs.main_commit }}
strategy:
matrix:
repository: [Cura, Uranium, CuraEngine, cura-binary-data, fdm_materials]
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
repository: Ultimaker/${{ matrix.repository }}
ref: ${{ needs.parse-version.outputs.branch_name }}
token: ${{ secrets.CURA_AUTORELEASE_PAT }}
- name: Create RC tag
run: |
git tag ${{ needs.find-rc-tag.outputs.tag_name }}
git push origin tag ${{ needs.find-rc-tag.outputs.tag_name }}
- name: Create or update release tag
run: |
git tag -f ${{ inputs.cura_version }}
git push -f origin tag ${{ inputs.cura_version }}
- name: Export Cura tagged commit
id: export-main-commit
if: ${{ matrix.repository == 'Cura' }}
run: |
echo "main_commit=`git rev-parse HEAD`" >> "$GITHUB_OUTPUT"
create-dependencies-packages:
name: Create conan packages for dependencies
uses: ultimaker/cura-workflows/.github/workflows/conan-package-release.yml@main
needs: [parse-version, freeze-packages-versions]
strategy:
matrix:
repository: [Cura, Uranium, CuraEngine, cura-binary-data, fdm_materials]
include:
- conan_recipe_root: "."
- repository: Cura
conan_recipe_root: "resources"
with:
repository: ${{ matrix.repository }}
ref_name: ${{ needs.parse-version.outputs.branch_name }}
version: ${{ inputs.cura_version }}
conan_release: true
conan_user_channel: ultimaker/stable
conan_internal: false
conan_latest: true
conan_recipe_root: ${{ matrix.conan_recipe_root }}
secrets: inherit
create-cura-package:
name: Create conan package for Cura
uses: ultimaker/cura-workflows/.github/workflows/conan-package-release.yml@main
needs: [parse-version, create-dependencies-packages]
with:
repository: Cura
ref_name: ${{ needs.parse-version.outputs.branch_name }}
version: ${{ inputs.cura_version }}
conan_release: true
conan_user_channel: ultimaker/stable
conan_internal: false
conan_latest: true
secrets: inherit
create-installers:
name: Create installers
uses: ./.github/workflows/installers.yml
needs: [parse-version, create-cura-package]
with:
cura_conan_version: cura/${{ inputs.cura_version }}@/
enterprise: false
staging: false
nightly: false
secrets: inherit
create-release-draft:
name: Create the release draft
runs-on: ubuntu-latest
needs: [create-installers, parse-version, create-tags]
steps:
- name: Checkout Cura repo
uses: actions/checkout@v4
with:
ref: ${{ needs.parse-version.outputs.branch_name }}
- name: Extract changelog
run: python ./scripts/extract_changelog.py --version ${{ needs.parse-version.outputs.version_major }}.${{ needs.parse-version.outputs.version_minor }}.${{ needs.parse-version.outputs.version_patch }} --changelog ./resources/texts/change_log.txt > formatted_changelog.txt
- name: Create release
uses: notpeelz/action-gh-create-release@v5.0.1
with:
target: ${{ needs.create-tags.outputs.main_commit }}
tag: ${{ inputs.cura_version }}
strategy: replace
title: UltiMaker Cura ${{ inputs.cura_version }}
draft: true
body-source: file
body: formatted_changelog.txt
- name: Download artifacts
uses: actions/download-artifact@v4.1.7
with:
path: artifacts
merge-multiple: true
- name: Upload artifacts
working-directory: artifacts
run: |
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-linux-X64.AppImage --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-linux-X64.AppImage.asc --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-ARM64.dmg --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-ARM64.pkg --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-X64.dmg --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-macos-X64.pkg --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-win64-X64.exe --clobber
gh release upload ${{ inputs.cura_version }} UltiMaker-Cura-${{ inputs.cura_version }}-win64-X64.msi --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -2,7 +2,12 @@ checks:
diagnostic-mesh-file-extension: true
diagnostic-mesh-file-size: true
diagnostic-definition-redundant-override: true
diagnostic-definition-experimental-setting: true
diagnostic-resources-macos-app-directory-name: true
diagnostic-incorrect-formula: true
diagnostic-resource-file-deleted: true
diagnostic-material-temperature-defined: false
diagnostic-long-profile-names: true
fixes:
diagnostic-definition-redundant-override: true
format:

View File

@ -1,14 +1,14 @@
version: "5.7.0-beta.0"
version: "5.9.0-alpha.0"
requirements:
- "uranium/(latest)@ultimaker/stable"
- "curaengine/(latest)@ultimaker/stable"
- "cura_binary_data/(latest)@ultimaker/stable"
- "fdm_materials/(latest)@ultimaker/stable"
- "curaengine_plugin_gradual_flow/0.1.0-beta.3"
- "dulcificum/latest@ultimaker/testing"
- "cura_resources/(latest)@ultimaker/testing"
- "uranium/(latest)@ultimaker/testing"
- "curaengine/(latest)@ultimaker/testing"
- "cura_binary_data/(latest)@ultimaker/testing"
- "fdm_materials/(latest)@ultimaker/testing"
- "dulcificum/0.2.1"
- "pysavitar/5.3.0"
- "pynest2d/5.3.0"
- "curaengine_grpc_definitions/0.2.0"
- "native_cad_plugin/2.0.0"
requirements_internal:
- "fdm_materials/(latest)@internal/testing"
- "cura_private_data/(latest)@internal/testing"
@ -33,18 +33,22 @@ pyinstaller:
package: "cura"
src: "plugins"
dst: "share/cura/plugins"
curaengine_gradual_flow_plugin:
package: "curaengine_plugin_gradual_flow"
src: "res/plugins/CuraEngineGradualFlow"
dst: "share/cura/plugins/CuraEngineGradualFlow"
curaengine_gradual_flow_plugin_bundled:
package: "curaengine_plugin_gradual_flow"
native_cad_plugin:
package: "native_cad_plugin"
src: "res/plugins/NativeCADplugin"
dst: "share/cura/plugins/NativeCADplugin"
native_cad_plugin_bundled:
package: "native_cad_plugin"
src: "res/bundled_packages"
dst: "share/cura/resources/bundled_packages"
cura_resources:
package: "cura"
src: "resources"
dst: "share/cura/resources"
cura_shared_resources:
package: "cura_resources"
src: "res"
dst: "share/cura/resources"
cura_private_data:
package: "cura_private_data"
src: "res"
@ -92,11 +96,6 @@ pyinstaller:
src: "bin"
dst: "."
binary: "CuraEngine"
curaengine_gradual_flow_plugin_service:
package: "curaengine_plugin_gradual_flow"
src: "bin"
dst: "."
binary: "curaengine_plugin_gradual_flow"
hiddenimports:
- "pySavitar"
- "pyArcus"

View File

@ -231,6 +231,8 @@ class CuraConan(ConanFile):
else:
src_path = os.path.join(self.source_folder, data["src"])
else:
if data["package"] not in self.deps_cpp_info.deps:
continue
src_path = os.path.join(self.deps_cpp_info[data["package"]].rootpath, data["src"])
elif "root" in data: # get the paths relative from the install folder
src_path = os.path.join(self.install_folder, data["root"], data["src"])
@ -327,7 +329,6 @@ class CuraConan(ConanFile):
self.options["cpython"].shared = True
self.options["boost"].header_only = True
if self.settings.os == "Linux":
self.options["curaengine_grpc_definitions"].shared = True
self.options["openssl"].shared = True
if self.conf.get("user.curaengine:sentry_url", "", check_type=str) != "":
self.options["curaengine"].enable_sentry = True
@ -343,6 +344,8 @@ class CuraConan(ConanFile):
for req in self.conan_data["requirements"]:
if self._internal and "fdm_materials" in req:
continue
if not self._enterprise and "native_cad_plugin" in req:
continue
self.requires(req)
if self._internal:
for req in self.conan_data["requirements_internal"]:
@ -387,11 +390,11 @@ class CuraConan(ConanFile):
copy(self, "CuraEngine", curaengine.bindirs[0], self.source_folder, keep_path = False)
# Copy the external plugins that we want to bundle with Cura
rmdir(self,str(self.source_path.joinpath("plugins", "CuraEngineGradualFlow")))
curaengine_plugin_gradual_flow = self.dependencies["curaengine_plugin_gradual_flow"].cpp_info
copy(self, "*", curaengine_plugin_gradual_flow.resdirs[0], str(self.source_path.joinpath("plugins", "CuraEngineGradualFlow")), keep_path = True)
copy(self, "*", curaengine_plugin_gradual_flow.bindirs[0], self.source_folder, keep_path = False)
copy(self, "bundled_*.json", curaengine_plugin_gradual_flow.resdirs[1], str(self.source_path.joinpath("resources", "bundled_packages")), keep_path = False)
if self._enterprise:
rmdir(self, str(self.source_path.joinpath("plugins", "NativeCADplugin")))
native_cad_plugin = self.dependencies["native_cad_plugin"].cpp_info
copy(self, "*", native_cad_plugin.resdirs[0], str(self.source_path.joinpath("plugins", "NativeCADplugin")), keep_path = True)
copy(self, "bundled_*.json", native_cad_plugin.resdirs[1], str(self.source_path.joinpath("resources", "bundled_packages")), keep_path = False)
# Copy resources of cura_binary_data
cura_binary_data = self.dependencies["cura_binary_data"].cpp_info
@ -458,6 +461,12 @@ class CuraConan(ConanFile):
copy(self, "*", os.path.join(self.package_folder, self.cpp_info.resdirs[0]), str(self._share_dir.joinpath("cura", "resources")), keep_path = True)
copy(self, "*", os.path.join(self.package_folder, self.cpp_info.resdirs[1]), str(self._share_dir.joinpath("cura", "plugins")), keep_path = True)
# Copy the cura_resources resources from the package
rm(self, "conanfile.py", os.path.join(self.package_folder, self.cpp.package.resdirs[0]))
cura_resources = self.dependencies["cura_resources"].cpp_info
for res_dir in cura_resources.resdirs:
copy(self, "*", res_dir, str(self._share_dir.joinpath("cura", "resources", Path(res_dir).name)), keep_path = True)
# Copy resources of Uranium (keep folder structure)
uranium = self.dependencies["uranium"].cpp_info
copy(self, "*", uranium.resdirs[0], str(self._share_dir.joinpath("uranium", "resources")), keep_path = True)
@ -502,13 +511,15 @@ echo "CURA_APP_NAME={{ cura_app_name }}" >> ${{ env_prefix }}GITHUB_ENV
copy(self, "requirement*.txt", src = self.source_folder, 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]))
# Remove the CuraEngineGradualFlow plugin from the package
rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[1], "CuraEngineGradualFlow"))
rm(self, "bundled_*.json", os.path.join(self.package_folder, self.cpp.package.resdirs[0], "bundled_packages"), recursive = False)
# Remove the fdm_materials from the package
rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[0], "materials"))
# Remove the cura_resources resources from the package
rm(self, "conanfile.py", os.path.join(self.package_folder, self.cpp.package.resdirs[0]))
cura_resources = self.dependencies["cura_resources"].cpp_info
for res_dir in cura_resources.resdirs:
rmdir(self, os.path.join(self.package_folder, self.cpp.package.resdirs[0], Path(res_dir).name))
def package_info(self):
self.user_info.pip_requirements = "requirements.txt"
self.user_info.pip_requirements_git = "requirements-ultimaker.txt"

View File

@ -115,15 +115,15 @@ class Account(QObject):
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self.sync)
self._sync_services: Dict[str, int] = {}
"""contains entries "service_name" : SyncState"""
self.syncRequested.connect(self._updatePermissions)
self._sync_services: Dict[str, int] = {}
def initialize(self) -> None:
self._authorization_service.initialize(self._application.getPreferences())
self._authorization_service.onAuthStateChanged.connect(self._onLoginStateChanged)
self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged)
self._authorization_service.accessTokenChanged.connect(self._onAccessTokenChanged)
self._authorization_service.accessTokenChanged.connect(self._updatePermissions)
self._authorization_service.loadAuthDataFromPreferences()
@pyqtProperty(int, notify=syncStateChanged)
@ -190,6 +190,20 @@ class Account(QObject):
def isLoggedIn(self) -> bool:
return self._logged_in
@pyqtSlot()
def stopSyncing(self) -> None:
Logger.debug(f"Stopping sync of cloud printers")
self._setManualSyncEnabled(True)
if self._update_timer.isActive():
self._update_timer.stop()
@pyqtSlot()
def startSyncing(self) -> None:
Logger.debug(f"Starting sync of cloud printers")
self._setManualSyncEnabled(False)
if not self._update_timer.isActive():
self._update_timer.start()
def _onLoginStateChanged(self, logged_in: bool = False, error_message: Optional[str] = None) -> None:
if error_message:
if self._error_message:

View File

@ -1,7 +1,13 @@
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import TYPE_CHECKING
from dataclasses import asdict
from typing import cast, Dict, TYPE_CHECKING
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingFunction import SettingFunction
from cura.Settings.GlobalStack import GlobalStack
if TYPE_CHECKING:
from cura.CuraApplication import CuraApplication
@ -47,3 +53,57 @@ class Settings:
"""
return self.application.getSidebarCustomMenuItems()
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()
machine_manager = self.application.getMachineManager()
settings = {
"material": {
"length": print_information.materialLengths,
"weight": print_information.materialWeights,
"cost": print_information.materialCosts,
},
"global": {
"changes": {},
"all_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)
# 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)
for i, extruder in enumerate(global_stack.extruderList):
# Add extruder fields to settings dictionary
settings[f"extruder_{i}"] = {
"changes": {},
"all_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)
# 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)
return settings

View File

@ -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.7.0"
CuraSDKVersion = "8.8.0"
try:
from cura.CuraVersion import CuraLatestURL

View File

@ -18,8 +18,8 @@ class BackendPlugin(AdditionalSettingDefinitionsAppender, PluginObject):
catalog = i18nCatalog("cura")
settings_catalog = i18nCatalog("fdmprinter.def.json")
def __init__(self) -> None:
super().__init__(self.settings_catalog)
def __init__(self, catalog_i18n = settings_catalog) -> None:
super().__init__(catalog_i18n)
self.__port: int = 0
self._plugin_address: str = "127.0.0.1"
self._plugin_command: Optional[List[str]] = None

View File

@ -33,6 +33,7 @@ from UM.Message import Message
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.OutputDevice.ProjectOutputDevice import ProjectOutputDevice
from UM.Platform import Platform
from UM.PluginError import PluginNotFoundError
from UM.Preferences import Preferences
@ -1217,6 +1218,8 @@ class CuraApplication(QtApplication):
# Once we're at this point, everything should have been flushed already (past OnExitCallbackManager).
# It's more difficult to call sys.exit(0): That requires that it happens as the result of a pyqtSignal-emit.
# (See https://doc.qt.io/qt-6/qcoreapplication.html#quit)
# WARNING: With this in place you CAN NOT use cProfile. You will need to replace the next line with pass
# for it to work!
os._exit(0)
return super().event(event)
@ -1457,7 +1460,11 @@ class CuraApplication(QtApplication):
self._scene_bounding_box = scene_bounding_box
self.sceneBoundingBoxChanged.emit()
self._platform_activity = True if count > 0 else False
if count > 0:
self._platform_activity = True
else:
ProjectOutputDevice.setLastOutputName(None)
self._platform_activity = False
self.activityChanged.emit()
@pyqtSlot()

View File

@ -49,7 +49,7 @@ class MachineErrorChecker(QObject):
self._keys_to_check = set() # type: Set[str]
self._num_keys_to_check_per_update = 10
self._num_keys_to_check_per_update = 1
def initialize(self) -> None:
self._error_check_timer.timeout.connect(self._rescheduleCheck)

View File

@ -1,8 +1,9 @@
# Copyright (c) 2019 Ultimaker B.V.
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Dict, List
from UM.Decorators import deprecated
from UM.Logger import Logger
from UM.Signal import Signal
from UM.Util import parseBool
@ -168,13 +169,18 @@ class MachineNode(ContainerNode):
return self.global_qualities.get(self.preferred_quality_type, next(iter(self.global_qualities.values())))
def isExcludedMaterial(self, material: MaterialNode) -> bool:
def isExcludedMaterialBaseFile(self, material_base_file: str) -> bool:
"""Returns whether the material should be excluded from the list of materials."""
for exclude_material in self.exclude_materials:
if exclude_material in material["id"]:
if exclude_material in material_base_file:
return True
return False
@deprecated("Use isExcludedMaterialBaseFile instead.", since = "5.9.0")
def isExcludedMaterial(self, material: MaterialNode) -> bool:
"""Returns whether the material should be excluded from the list of materials."""
return self.isExcludedMaterialBaseFile(material.base_file)
@UM.FlameProfiler.profile
def _loadAll(self) -> None:
"""(Re)loads all variants under this printer."""

View File

@ -21,13 +21,20 @@ class MaterialNode(ContainerNode):
Its subcontainers are quality profiles.
"""
def __init__(self, container_id: str, variant: "VariantNode") -> None:
def __init__(self, container_id: str, variant: "VariantNode", *, container: ContainerInterface = None) -> None:
super().__init__(container_id)
self.variant = variant
self.qualities = {} # type: Dict[str, QualityNode] # Mapping container IDs to quality profiles.
self.materialChanged = Signal() # Triggered when the material is removed or its metadata is updated.
container_registry = ContainerRegistry.getInstance()
if container is not None:
self.base_file = container.getMetaDataEntry("base_file")
self.material_type = container.getMetaDataEntry("material")
self.brand = container.getMetaDataEntry("brand")
self.guid = container.getMetaDataEntry("GUID")
else:
my_metadata = container_registry.findContainersMetadata(id = container_id)[0]
self.base_file = my_metadata["base_file"]
self.material_type = my_metadata["material"]

View File

@ -54,10 +54,7 @@ class ActiveIntentQualitiesModel(ListModel):
self._updateDelayed()
def _update(self):
active_extruder_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStack
if active_extruder_stack:
self._intent_category = active_extruder_stack.intent.getMetaDataEntry("intent_category", "")
self._intent_category = IntentManager.getInstance().currentIntentCategory
new_items: List[Dict[str, Any]] = []
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:

View File

@ -24,6 +24,10 @@ intent_translations["quick"] = {
"name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction.")
}
intent_translations["annealing"] = {
"name": catalog.i18nc("@label", "Annealing"),
"description": catalog.i18nc("@text", "The annealing profile requires post-processing in an oven after the print is finished. This profile retains the dimensional accuracy of the printed part after annealing and improves strength, stiffness, and thermal resistance.")
}
intent_translations["solid"] = {
"name": catalog.i18nc("@label", "Solid"),
"description": catalog.i18nc("@text",

View File

@ -60,7 +60,7 @@ class VariantNode(ContainerNode):
materials = list(materials_per_base_file.values())
# Filter materials based on the exclude_materials property.
filtered_materials = [material for material in materials if not self.machine.isExcludedMaterial(material)]
filtered_materials = [material for material in materials if not self.machine.isExcludedMaterialBaseFile(material["id"])]
for material in filtered_materials:
base_file = material["base_file"]
@ -127,7 +127,7 @@ class VariantNode(ContainerNode):
material_definition = container.getMetaDataEntry("definition")
base_file = container.getMetaDataEntry("base_file")
if base_file in self.machine.exclude_materials:
if self.machine.isExcludedMaterialBaseFile(base_file):
return # Material is forbidden for this printer.
if base_file not in self.materials: # Completely new base file. Always better than not having a file as long as it matches our set-up.
if material_definition != "fdmprinter" and material_definition != self.machine.container_id:
@ -148,7 +148,7 @@ class VariantNode(ContainerNode):
if "empty_material" in self.materials:
del self.materials["empty_material"]
self.materials[base_file] = MaterialNode(container.getId(), variant = self)
self.materials[base_file] = MaterialNode(container.getId(), variant = self, container = container)
self.materials[base_file].materialChanged.connect(self.materialsChanged)
self.materialsChanged.emit(self.materials[base_file])

View File

@ -96,7 +96,7 @@ class AuthorizationHelpers:
return
if token_response.error() != QNetworkReply.NetworkError.NoError:
callback(AuthenticationResponse(success = False, err_message = token_data["error_description"]))
callback(AuthenticationResponse(success = False, err_message = token_data.get("error_description", "an unknown server error occurred")))
return
callback(AuthenticationResponse(success = True,

View File

@ -0,0 +1,106 @@
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Resources import Resources
import json
from typing import Dict, List, Optional
class FormatMaps:
# A map from the printer-type in their native file-formats to the internal name we use.
PRINTER_TYPE_NAME = {
"fire_e": "ultimaker_method",
"lava_f": "ultimaker_methodx",
"magma_10": "ultimaker_methodxl",
"sketch": "ultimaker_sketch"
}
# A map from the extruder-name in their native file-formats to the internal name we use.
EXTRUDER_NAME_MAP = {
"mk14_hot": "1XA",
"mk14_hot_s": "2XA",
"mk14_c": "1C",
"mk14": "1A",
"mk14_s": "2A",
"mk14_e": "LABS"
}
# A map from the material-name in their native file-formats to some info, including the internal name we use.
MATERIAL_MAP = {
"abs": {"name": "ABS", "guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"},
"abs-cf10": {"name": "ABS-CF", "guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"},
"abs-wss1": {"name": "ABS-R", "guid": "88c8919c-6a09-471a-b7b6-e801263d862d"},
"asa": {"name": "ASA", "guid": "f79bc612-21eb-482e-ad6c-87d75bdde066"},
"nylon12-cf": {"name": "Nylon 12 CF", "guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"},
"nylon": {"name": "Nylon", "guid": "283d439a-3490-4481-920c-c51d8cdecf9c"},
"pc": {"name": "PC", "guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"},
"petg": {"name": "PETG", "guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"},
"pla": {"name": "PLA", "guid": "abb9c58e-1f56-48d1-bd8f-055fde3a5b56"},
"pva": {"name": "PVA", "guid": "add51ef2-86eb-4c39-afd5-5586564f0715"},
"wss1": {"name": "RapidRinse", "guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"},
"sr30": {"name": "SR-30", "guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"},
"bvoh": {"name": "BVOH", "guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"},
"cpe": {"name": "CPE", "guid": "da1872c1-b991-4795-80ad-bdac0f131726"},
"hips": {"name": "HIPS", "guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"},
"tpu": {"name": "TPU 95A", "guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"},
"im-pla": {"name": "Tough", "guid": "de031137-a8ca-4a72-bd1b-17bb964033ad"}
}
__inverse_printer_name: Optional[Dict[str, str]] = None
__inverse_extruder_type: Optional[Dict[str, str]] = None
__inverse_material_map: Optional[Dict[str, str]] = None
__product_to_id_map: Optional[Dict[str, List[str]]] = None
@classmethod
def getInversePrinterNameMap(cls) -> Dict[str, str]:
"""Returns the inverse of the printer name map, that is, from the internal name to the name used in output."""
if cls.__inverse_printer_name is not None:
return cls.__inverse_printer_name
cls.__inverse_printer_name = {}
for key, value in cls.PRINTER_TYPE_NAME.items():
cls.__inverse_printer_name[value] = key
return cls.__inverse_printer_name
@classmethod
def getInverseExtruderTypeMap(cls) -> Dict[str, str]:
"""Returns the inverse of the extruder type map, that is, from the internal name to the name used in output."""
if cls.__inverse_extruder_type is not None:
return cls.__inverse_extruder_type
cls.__inverse_extruder_type = {}
for key, value in cls.EXTRUDER_NAME_MAP.items():
cls.__inverse_extruder_type[value] = key
return cls.__inverse_extruder_type
@classmethod
def getInverseMaterialMap(cls) -> Dict[str, str]:
"""Returns the inverse of the material map, that is, from the internal name to the name used in output.
Note that this drops the extra info saved in the non-inverse material map, use that if you need it.
"""
if cls.__inverse_material_map is not None:
return cls.__inverse_material_map
cls.__inverse_material_map = {}
for key, value in cls.MATERIAL_MAP.items():
cls.__inverse_material_map[value["name"]] = key
return cls.__inverse_material_map
@classmethod
def getProductIdMap(cls) -> Dict[str, List[str]]:
"""Gets a mapping from product names (for example, in the XML files) to their definition IDs.
This loads the mapping from a file.
"""
if cls.__product_to_id_map is not None:
return cls.__product_to_id_map
product_to_id_file = Resources.getPath(Resources.Texts, "product_to_id.json")
with open(product_to_id_file, encoding = "utf-8") as f:
contents = ""
for line in f:
contents += line if "#" not in line else "".join([line.replace("#", str(n)) for n in range(1, 12)])
cls.__product_to_id_map = json.loads(contents)
cls.__product_to_id_map = {key: [value] for key, value in cls.__product_to_id_map.items()}
#This also loads "Ultimaker S5" -> "ultimaker_s5" even though that is not strictly necessary with the default to change spaces into underscores.
#However it is not always loaded with that default; this mapping is also used in serialize() without that default.
return cls.__product_to_id_map

View File

@ -1,9 +1,10 @@
# Copyright (c) 2018 Ultimaker B.V.
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from PyQt6.QtCore import pyqtProperty, QObject, pyqtSignal
from cura.PrinterOutput.FormatMaps import FormatMaps
from .MaterialOutputModel import MaterialOutputModel
@ -45,15 +46,8 @@ class ExtruderConfigurationModel(QObject):
@staticmethod
def applyNameMappingHotend(hotendId) -> str:
_EXTRUDER_NAME_MAP = {
"mk14_hot":"1XA",
"mk14_hot_s":"2XA",
"mk14_c":"1C",
"mk14":"1A",
"mk14_s":"2A"
}
if hotendId in _EXTRUDER_NAME_MAP:
return _EXTRUDER_NAME_MAP[hotendId]
if hotendId in FormatMaps.EXTRUDER_NAME_MAP:
return FormatMaps.EXTRUDER_NAME_MAP[hotendId]
return hotendId
@pyqtProperty(str, fset = setHotendID, notify = extruderConfigurationChanged)

View File

@ -1,9 +1,10 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from PyQt6.QtCore import pyqtProperty, QObject
from cura.PrinterOutput.FormatMaps import FormatMaps
class MaterialOutputModel(QObject):
@ -23,29 +24,9 @@ class MaterialOutputModel(QObject):
@staticmethod
def getMaterialFromDefinition(guid, type, brand, name):
_MATERIAL_MAP = { "abs" :{"name" :"ABS" ,"guid": "2780b345-577b-4a24-a2c5-12e6aad3e690"},
"abs-cf10": {"name": "ABS-CF", "guid": "495a0ce5-9daf-4a16-b7b2-06856d82394d"},
"abs-wss1" :{"name" :"ABS-R" ,"guid": "88c8919c-6a09-471a-b7b6-e801263d862d"},
"asa" :{"name" :"ASA" ,"guid": "416eead4-0d8e-4f0b-8bfc-a91a519befa5"},
"nylon12-cf":{"name": "Nylon 12 CF" ,"guid": "3c6f2877-71cc-4760-84e6-4b89ab243e3b"},
"nylon" :{"name" :"Nylon" ,"guid": "283d439a-3490-4481-920c-c51d8cdecf9c"},
"pc" :{"name" :"PC" ,"guid": "62414577-94d1-490d-b1e4-7ef3ec40db02"},
"petg" :{"name" :"PETG" ,"guid": "69386c85-5b6c-421a-bec5-aeb1fb33f060"},
"pla" :{"name" :"PLA" ,"guid": "0ff92885-617b-4144-a03c-9989872454bc"},
"pva" :{"name" :"PVA" ,"guid": "a4255da2-cb2a-4042-be49-4a83957a2f9a"},
"wss1" :{"name" :"RapidRinse" ,"guid": "a140ef8f-4f26-4e73-abe0-cfc29d6d1024"},
"sr30" :{"name" :"SR-30" ,"guid": "77873465-83a9-4283-bc44-4e542b8eb3eb"},
"bvoh" :{"name" :"BVOH" ,"guid": "923e604c-8432-4b09-96aa-9bbbd42207f4"},
"cpe" :{"name" :"CPE" ,"guid": "da1872c1-b991-4795-80ad-bdac0f131726"},
"hips" :{"name" :"HIPS" ,"guid": "a468d86a-220c-47eb-99a5-bbb47e514eb0"},
"tpu" :{"name" :"TPU 95A" ,"guid": "19baa6a9-94ff-478b-b4a1-8157b74358d2"}
}
if guid is None and brand != "empty" and type in _MATERIAL_MAP:
name = _MATERIAL_MAP[type]["name"]
guid = _MATERIAL_MAP[type]["guid"]
if guid is None and brand != "empty" and type in FormatMaps.MATERIAL_MAP:
name = FormatMaps.MATERIAL_MAP[type]["name"]
guid = FormatMaps.MATERIAL_MAP[type]["guid"]
return name, guid

View File

@ -1,4 +1,4 @@
# Copyright (c) 2021 Ultimaker B.V.
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from UM.FileHandler.FileHandler import FileHandler #For typing.
@ -6,6 +6,7 @@ from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode #For typing.
from cura.API import Account
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.FormatMaps import FormatMaps
from cura.PrinterOutput.PrinterOutputDevice import PrinterOutputDevice, ConnectionState, ConnectionType
@ -419,13 +420,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice):
@staticmethod
def applyPrinterTypeMapping(printer_type):
_PRINTER_TYPE_NAME = {
"fire_e": "ultimaker_method",
"lava_f": "ultimaker_methodx",
"magma_10": "ultimaker_methodxl"
}
if printer_type in _PRINTER_TYPE_NAME:
return _PRINTER_TYPE_NAME[printer_type]
if printer_type in FormatMaps.PRINTER_TYPE_NAME:
return FormatMaps.PRINTER_TYPE_NAME[printer_type]
return printer_type
@pyqtProperty(str, constant = True)

View File

@ -83,6 +83,15 @@ class GlobalStack(CuraContainerStack):
"""
return self.getMetaDataEntry("supports_material_export", False)
@pyqtProperty("QVariantList", constant = True)
def getOutputFileFormats(self) -> List[str]:
"""
Which output formats the printer supports.
:return: A list of strings with MIME-types.
"""
all_formats_str = self.getMetaDataEntry("file_formats", "")
return all_formats_str.split(";")
@classmethod
def getLoadingPriority(cls) -> int:
return 2

View File

@ -145,10 +145,24 @@ class IntentManager(QObject):
@pyqtProperty(str, notify = intentCategoryChanged)
def currentIntentCategory(self) -> str:
application = cura.CuraApplication.CuraApplication.getInstance()
active_extruder_stack = application.getMachineManager().activeStack
if active_extruder_stack is None:
return ""
return active_extruder_stack.intent.getMetaDataEntry("intent_category", "")
global_stack = application.getGlobalContainerStack()
active_intent = "default"
if global_stack is None:
return active_intent
# Loop over all active extruders and check if they have an intent that isn't default.
# The logic behind this is that support materials (for instance, PVA) don't have intents, but they should be
# combinable with all other intents. So if one extruder has "engineering" as an intent and the other has
# "default" the 'dominant' intent is "engineering"
for extruder_stack in global_stack.extruderList:
if not extruder_stack.isEnabled: # Ignore disabled stacks
continue
extruder_intent = extruder_stack.intent.getMetaDataEntry("intent_category", "")
if extruder_intent != "default":
active_intent = extruder_intent
return active_intent
@pyqtSlot(str, str)
def selectIntent(self, intent_category: str, quality_type: str) -> None:

View File

@ -847,6 +847,24 @@ class MachineManager(QObject):
return result
@pyqtProperty(bool, notify = currentConfigurationChanged)
def variantCoreUsableForFactor4(self) -> bool:
"""The selected core is usable if it is in second extruder of Factor4
"""
result = True
if not self._global_container_stack:
return result
if self.activeMachine.definition.id != "ultimaker_factor4":
return result
for extruder_container in self._global_container_stack.extruderList:
if extruder_container.definition.id.startswith("ultimaker_factor4_extruder_right"):
if extruder_container.material == empty_material_container:
return True
if extruder_container.variant.id.startswith("ultimaker_factor4_bb"):
return False
return True
@pyqtSlot(str, result = str)
def getDefinitionByMachineId(self, machine_id: str) -> Optional[str]:
"""Get the Definition ID of a machine (specified by ID)

View File

@ -1,6 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import List, Optional, TYPE_CHECKING
from typing import List, Optional, Set, TYPE_CHECKING
from PyQt6.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal
from UM.FlameProfiler import pyqtSlot
@ -168,37 +168,26 @@ class SettingInheritanceManager(QObject):
def settingsWithInheritanceWarning(self) -> List[str]:
return self._settings_with_inheritance_warning
def _settingIsOverwritingInheritance(self, key: str, stack: ContainerStack = None) -> bool:
"""Check if a setting has an inheritance function that is overwritten"""
def _userSettingIsOverwritingInheritance(self, key: str, stack: ContainerStack, all_keys: Set[str] = set()) -> bool:
"""Check if a setting known as having a User state has an inheritance function that is overwritten"""
has_setting_function = False
if not stack:
stack = self._active_container_stack
if not stack: # No active container stack yet!
return False
if self._active_container_stack is None:
return False
all_keys = self._active_container_stack.getAllKeys()
containers = [] # type: List[ContainerInterface]
has_user_state = stack.getProperty(key, "state") == InstanceState.User
"""Check if the setting has a user state. If not, it is never overwritten."""
if not has_user_state:
return False
# If a setting is not enabled, don't label it as overwritten (It's never visible anyway).
if not stack.getProperty(key, "enabled"):
return False
user_container = stack.getTop()
"""Also check if the top container is not a setting function (this happens if the inheritance is restored)."""
# Also check if the top container is not a setting function (this happens if the inheritance is restored).
if user_container and isinstance(user_container.getProperty(key, "value"), SettingFunction):
return False
if not all_keys:
all_keys = self._active_container_stack.getAllKeys()
## Mash all containers for all the stacks together.
while stack:
containers.extend(stack.getContainers())
@ -229,17 +218,35 @@ class SettingInheritanceManager(QObject):
break # There is a setting function somewhere, stop looking deeper.
return has_setting_function and has_non_function_value
def _settingIsOverwritingInheritance(self, key: str, stack: ContainerStack = None) -> bool:
"""Check if a setting has an inheritance function that is overwritten"""
if not stack:
stack = self._active_container_stack
if not stack: # No active container stack yet!
return False
if self._active_container_stack is None:
return False
has_user_state = stack.getProperty(key, "state") == InstanceState.User
if not has_user_state:
return False
return self._userSettingIsOverwritingInheritance(key, stack)
def _update(self) -> None:
self._settings_with_inheritance_warning = [] # Reset previous data.
# Make sure that the GlobalStack is not None. sometimes the globalContainerChanged signal gets here late.
if self._global_container_stack is None:
if self._global_container_stack is None or self._active_container_stack is None:
return
# Check all setting keys that we know of and see if they are overridden.
for setting_key in self._global_container_stack.getAllKeys():
override = self._settingIsOverwritingInheritance(setting_key)
if override:
# Check all user setting keys that we know of and see if they are overridden.
all_keys = self._active_container_stack.getAllKeys()
for setting_key in self._active_container_stack.getAllKeysWithUserState():
if self._userSettingIsOverwritingInheritance(setting_key, self._active_container_stack, all_keys):
self._settings_with_inheritance_warning.append(setting_key)
# Check all the categories if any of their children have their inheritance overwritten.

View File

@ -1,8 +1,9 @@
# Copyright (c) 2024 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt6.QtCore import Qt
from PyQt6.QtCore import Qt, pyqtSignal
from UM import i18nCatalog
from UM.Logger import Logger
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Qt.ListModel import ListModel
@ -19,9 +20,11 @@ class SpecificSettingsModel(ListModel):
self.addRoleName(self.LabelRole, "label")
self.addRoleName(self.ValueRole, "value")
self._i18n_catalog = None
self._settings_catalog = i18nCatalog("fdmprinter.def.json")
self._update()
modelChanged = pyqtSignal()
def addSettingsFromStack(self, stack, category, settings):
for setting, value in settings.items():
@ -30,17 +33,30 @@ class SpecificSettingsModel(ListModel):
setting_type = stack.getProperty(setting, "type")
if setting_type is not None:
# This is not very good looking, but will do for now
value = str(SettingDefinition.settingValueToString(setting_type, value)) + " " + str(unit)
value = str(SettingDefinition.settingValueToString(setting_type, value))
if unit:
value += " " + str(unit)
if setting_type == "enum":
options = stack.getProperty(setting, "options")
value_msgctxt = f"{str(setting)} option {str(value)}"
value_msgid = options[stack.getProperty(setting, "value")]
value = self._settings_catalog.i18nc(value_msgctxt, value_msgid)
else:
value = str(value)
label_msgctxt = f"{str(setting)} label"
label_msgid = stack.getProperty(setting, "label")
label = self._settings_catalog.i18nc(label_msgctxt, label_msgid)
self.appendItem({
"category": category,
"label": stack.getProperty(setting, "label"),
"label": label,
"value": value
})
self.modelChanged.emit()
def _update(self):
Logger.debug(f"Updating {self.__class__.__name__}")
self.setItems([])
self.modelChanged.emit()
return

View File

@ -17,6 +17,7 @@ from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
from UM.Scene.GroupDecorator import GroupDecorator
from UM.Scene.SceneNode import SceneNode # For typing.
from UM.Scene.SceneNodeSettings import SceneNodeSettings
from UM.Util import parseBool
from cura.CuraApplication import CuraApplication
from cura.Machines.ContainerTree import ContainerTree
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
@ -182,7 +183,7 @@ class ThreeMFReader(MeshReader):
um_node.printOrder = int(setting_value)
continue
if key =="drop_to_buildplate":
um_node.setSetting(SceneNodeSettings.AutoDropDown, eval(setting_value))
um_node.setSetting(SceneNodeSettings.AutoDropDown, parseBool(setting_value))
continue
if key in known_setting_keys:
setting_container.setProperty(key, "value", setting_value)

View File

@ -10,6 +10,8 @@ from typing import cast, Dict, List, Optional, Tuple, Any, Set
import xml.etree.ElementTree as ET
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Math.Vector import Vector
from UM.Util import parseBool
from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Application import Application
@ -936,6 +938,24 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if nodes is None:
nodes = []
if self._is_ucp:
# We might be on a different printer than the one this project was made on.
# The offset to the printers' center isn't saved; instead, try to just fit everything on the buildplate.
full_extents = None
for node in nodes:
extents = node.getMeshData().getExtents() if node.getMeshData() else None
if extents is not None:
pos = node.getPosition()
node_box = AxisAlignedBox(extents.minimum + pos, extents.maximum + pos)
if full_extents is None:
full_extents = node_box
else:
full_extents = full_extents + node_box
if full_extents and full_extents.isValid():
for node in nodes:
pos = node.getPosition()
node.setPosition(Vector(pos.x - full_extents.center.x, pos.y, pos.z - full_extents.center.z))
base_file_name = os.path.basename(file_name)
self.setWorkspaceName(base_file_name)
@ -1225,7 +1245,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
node = machine_node.variants.get(machine_node.preferred_variant_name, next(iter(machine_node.variants.values())))
else:
variant_name = extruder_info.variant_info.parser["general"]["name"]
node = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants[variant_name]
node = ContainerTree.getInstance().machines[global_stack.definition.getId()].variants.get(variant_name, next(iter(machine_node.variants.values())))
extruder_stack.variant = node.container
def _applyMaterials(self, global_stack, extruder_stack_dict):
@ -1240,7 +1260,10 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
root_material_id = extruder_info.root_material_id
root_material_id = self._old_new_materials.get(root_material_id, root_material_id)
material_node = machine_node.variants[extruder_stack.variant.getName()].materials[root_material_id]
available_materials = machine_node.variants[extruder_stack.variant.getName()].materials
if root_material_id not in available_materials:
continue
material_node = available_materials[root_material_id]
extruder_stack.material = material_node.container
def _clearMachineSettings(self, global_stack, extruder_stack_dict):

View File

@ -77,6 +77,7 @@ class WorkspaceDialog(QObject):
self._is_compatible_machine = False
self._allow_create_machine = True
self._exported_settings_model = SpecificSettingsModel()
self._exported_settings_model.modelChanged.connect(self.exportedSettingModelChanged.emit)
self._current_machine_pos_index = 0
self._is_ucp = False
@ -104,6 +105,7 @@ class WorkspaceDialog(QObject):
missingPackagesChanged = pyqtSignal()
isCompatibleMachineChanged = pyqtSignal()
isUcpChanged = pyqtSignal()
exportedSettingModelChanged = pyqtSignal()
@pyqtProperty(bool, notify = isPrinterGroupChanged)
def isPrinterGroup(self) -> bool:
@ -356,10 +358,17 @@ class WorkspaceDialog(QObject):
def allowCreateMachine(self):
return self._allow_create_machine
@pyqtProperty(QObject)
@pyqtProperty(QObject, notify=exportedSettingModelChanged)
def exportedSettingModel(self):
return self._exported_settings_model
@pyqtProperty("QVariantList", notify=exportedSettingModelChanged)
def exportedSettingModelItems(self):
return self._exported_settings_model.items
@pyqtProperty(int, notify=exportedSettingModelChanged)
def exportedSettingModelRowCount(self):
return self._exported_settings_model.rowCount()
@pyqtSlot()
def closeBackend(self) -> None:
"""Close the backend: otherwise one could end up with "Slicing..."""

View File

@ -24,31 +24,36 @@ UM.Dialog
{
height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height
color: UM.Theme.getColor("main_background")
ColumnLayout
{
id: headerColumn
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.rightMargin: anchors.leftMargin
RowLayout
{
UM.Label
{
id: titleLabel
text: manager.isUcp? catalog.i18nc("@action:title Don't translate 'Universal Cura Project'", "Summary - Open Universal Cura Project (UCP)"): catalog.i18nc("@action:title", "Summary - Cura Project")
font: UM.Theme.getFont("large")
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.leftMargin: UM.Theme.getSize("default_margin").height
}
Cura.TertiaryButton
{
id: learnMoreButton
visible: manager.isUcp
anchors.right: parent.right
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.rightMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@button", "Learn more")
iconSource: UM.Theme.getIcon("LinkExternal")
isIconOnRightSide: true
onClicked: Qt.openUrlExternally("https://support.ultimaker.com/s/article/000002979")
}
}
}
}
Rectangle
{
@ -184,8 +189,9 @@ UM.Dialog
WorkspaceRow
{
id: numberOfOverrides
leftLabelText: catalog.i18nc("@action:label", "Settings Loaded from UCP file")
rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.exportedSettingModel.rowCount()).arg(manager.exportedSettingModel.rowCount())
rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.exportedSettingModelRowCount).arg(manager.exportedSettingModelRowCount)
buttonText: tableViewSpecificSettings.shouldBeVisible ? catalog.i18nc("@action:button", "Hide settings") : catalog.i18nc("@action:button", "Show settings")
onButtonClicked: tableViewSpecificSettings.shouldBeVisible = !tableViewSpecificSettings.shouldBeVisible
}
@ -208,15 +214,18 @@ UM.Dialog
{
id: tableModel
headers: ["category", "label", "value"]
rows: manager.exportedSettingModel.items
}
rows: manager.exportedSettingModelItems
}
property var modelRows: manager.exportedSettingModel.items
onModelRowsChanged:
Connections
{
target: manager
function onExportedSettingModelChanged()
{
tableModel.clear()
tableModel.rows = modelRows
tableModel.rows = manager.exportedSettingModelItems
}
}
}
}

View File

@ -91,11 +91,11 @@ Item
}
}
Loader
{
width: parent.width
height: content.height
z: -1
anchors.top: sectionTitleRow.bottom
sourceComponent: content
}

View File

@ -6,13 +6,15 @@ from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal
class SettingExport(QObject):
def __init__(self, id, name, value, selectable):
def __init__(self, id, name, value, value_name, selectable, show):
super().__init__()
self.id = id
self._name = name
self._value = value
self._value_name = value_name
self._selected = selectable
self._selectable = selectable
self._show_in_menu = show
@pyqtProperty(str, constant=True)
def name(self):
@ -22,6 +24,10 @@ class SettingExport(QObject):
def value(self):
return self._value
@pyqtProperty(str, constant=True)
def valuename(self):
return str(self._value_name)
selectedChanged = pyqtSignal(bool)
def setSelected(self, selected):
@ -36,3 +42,7 @@ class SettingExport(QObject):
@pyqtProperty(bool, constant=True)
def selectable(self):
return self._selectable
@pyqtProperty(bool, constant=True)
def isVisible(self):
return self._show_in_menu

View File

@ -24,7 +24,7 @@ RowLayout
UM.Label
{
text: modelData.value
text: modelData.valuename
}
UM.HelpIcon

View File

@ -23,6 +23,7 @@ class SettingsExportGroup(QObject):
self._category_details = category_details
self._extruder_index = extruder_index
self._extruder_color = extruder_color
self._visible_settings = []
@pyqtProperty(str, constant=True)
def name(self):
@ -32,6 +33,12 @@ class SettingsExportGroup(QObject):
def settings(self):
return self._settings
@pyqtProperty(list, constant=True)
def visibleSettings(self):
if self._visible_settings == []:
self._visible_settings = list(filter(lambda item : item.isVisible, self._settings))
return self._visible_settings
@pyqtProperty(int, constant=True)
def category(self):
return self._category

View File

@ -6,6 +6,7 @@ from typing import Optional, cast, List, Dict, Pattern, Set
from PyQt6.QtCore import QObject, pyqtProperty
from UM import i18nCatalog
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingFunction import SettingFunction
@ -109,6 +110,7 @@ class SettingsExportModel(QObject):
@staticmethod
def _exportSettings(settings_stack):
settings_catalog = i18nCatalog("fdmprinter.def.json")
user_settings_container = settings_stack.userChanges
user_keys = user_settings_container.getAllKeys()
exportable_settings = SettingsExportModel.EXPORTABLE_SETTINGS
@ -117,11 +119,22 @@ class SettingsExportModel(QObject):
is_exportable = any(key in SettingsExportModel.PER_MODEL_EXPORTABLE_SETTINGS_KEYS for key in user_keys)
for setting_to_export in user_keys:
label = settings_stack.getProperty(setting_to_export, "label")
show_in_menu = setting_to_export not in SettingsExportModel.PER_MODEL_EXPORTABLE_SETTINGS_KEYS
label_msgtxt = f"{str(setting_to_export)} label"
label_msgid = settings_stack.getProperty(setting_to_export, "label")
label = settings_catalog.i18nc(label_msgtxt, label_msgid)
value = settings_stack.getProperty(setting_to_export, "value")
unit = settings_stack.getProperty(setting_to_export, "unit")
setting_type = settings_stack.getProperty(setting_to_export, "type")
value_name = str(SettingDefinition.settingValueToString(setting_type, value))
if unit:
value_name += " " + str(unit)
if setting_type == "enum":
options = settings_stack.getProperty(setting_to_export, "options")
value_msgctxt = f"{str(setting_to_export)} option {str(value)}"
value_msgid = options.get(value, "")
value_name = settings_catalog.i18nc(value_msgctxt, value_msgid)
if setting_type is not None:
value = f"{str(SettingDefinition.settingValueToString(setting_type, value))} {unit}"
else:
@ -130,6 +143,8 @@ class SettingsExportModel(QObject):
settings_export.append(SettingExport(setting_to_export,
label,
value,
is_exportable or setting_to_export in exportable_settings))
value_name,
is_exportable or setting_to_export in exportable_settings,
show_in_menu))
return settings_export

View File

@ -71,8 +71,8 @@ ColumnLayout
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
spacing: 0
model: modelData.settings
visible: modelData.settings.length > 0
model: modelData.visibleSettings
visible: modelData.visibleSettings.length > 0
delegate: SettingSelection { }
}
@ -80,8 +80,7 @@ ColumnLayout
UM.Label
{
UM.I18nCatalog { id: catalog; name: "cura" }
text: catalog.i18nc("@label", "No specific value has been set")
visible: modelData.settings.length === 0
visible: modelData.visibleSettings.length === 0
}
}

View File

@ -96,7 +96,8 @@ class ThreeMFWriter(MeshWriter):
@staticmethod
def _convertUMNodeToSavitarNode(um_node,
transformation = Matrix(),
exported_settings: Optional[Dict[str, Set[str]]] = None):
exported_settings: Optional[Dict[str, Set[str]]] = None,
center_mesh = False):
"""Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
:returns: Uranium Scene node.
@ -111,20 +112,26 @@ class ThreeMFWriter(MeshWriter):
savitar_node = Savitar.SceneNode()
savitar_node.setName(um_node.getName())
node_matrix = Matrix()
mesh_data = um_node.getMeshData()
node_matrix = um_node.getLocalTransformation()
node_matrix.preMultiply(transformation)
if center_mesh:
center_matrix = Matrix()
# compensate for original center position, if object(s) is/are not around its zero position
if mesh_data is not None:
extents = mesh_data.getExtents()
if extents is not None:
# We use a different coordinate space while writing, so flip Z and Y
center_vector = Vector(extents.center.x, extents.center.z, extents.center.y)
node_matrix.setByTranslation(center_vector)
node_matrix.multiply(um_node.getLocalTransformation())
center_vector = Vector(-extents.center.x, -extents.center.y, -extents.center.z)
center_matrix.setByTranslation(center_vector)
node_matrix.preMultiply(center_matrix)
matrix_string = ThreeMFWriter._convertMatrixToString(node_matrix.preMultiply(transformation))
matrix_string = ThreeMFWriter._convertMatrixToString(node_matrix)
savitar_node.setTransformation(matrix_string)
if mesh_data is not None:
savitar_node.getMeshData().setVerticesFromBytes(mesh_data.getVerticesAsByteArray())
indices_array = mesh_data.getIndicesAsByteArray()
@ -283,7 +290,8 @@ class ThreeMFWriter(MeshWriter):
for root_child in node.getChildren():
savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(root_child,
transformation_matrix,
exported_model_settings)
exported_model_settings,
center_mesh = True)
if savitar_node:
savitar_scene.addSceneNode(savitar_node)
else:
@ -442,7 +450,7 @@ class ThreeMFWriter(MeshWriter):
def sceneNodesToString(scene_nodes: [SceneNode]) -> str:
savitar_scene = Savitar.Scene()
for scene_node in scene_nodes:
savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(scene_node)
savitar_node = ThreeMFWriter._convertUMNodeToSavitarNode(scene_node, center_mesh = True)
savitar_scene.addSceneNode(savitar_node)
parser = Savitar.ThreeMFParser()
scene_string = parser.sceneToString(savitar_scene)

View File

@ -69,7 +69,7 @@ class UCPDialog(QObject):
device.writeSuccess.connect(lambda: self._onSuccess())
device.writeFinished.connect(lambda: self._onFinished())
file_name = CuraApplication.getInstance().getPrintInformation().baseName
file_name = f"UCP_{CuraApplication.getInstance().getPrintInformation().baseName}"
try:
device.requestWrite(

View File

@ -544,7 +544,7 @@ class CuraEngineBackend(QObject, Backend):
if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder:
self._error_message = Message(catalog.i18nc("@info:status",
"Unable to slice because there are objects associated with disabled Extruder %s.") % job.getMessage(),
"Unable to slice because there are objects associated with disabled Extruder %s.") % job.getAssociatedDisabledExtruders(),
title = catalog.i18nc("@info:title", "Unable to slice"),
message_type = Message.MessageType.WARNING)
self._error_message.show()

View File

@ -146,6 +146,7 @@ class StartSliceJob(Job):
self._slice_message: Arcus.PythonMessage = slice_message
self._is_cancelled: bool = False
self._build_plate_number: Optional[int] = None
self._associated_disabled_extruders: Optional[str] = None
# cache for all setting values from all stacks (global & extruder) for the current machine
self._all_extruders_settings: Optional[Dict[str, Any]] = None
@ -153,6 +154,9 @@ class StartSliceJob(Job):
def getSliceMessage(self) -> Arcus.PythonMessage:
return self._slice_message
def getAssociatedDisabledExtruders(self) -> Optional[str]:
return self._associated_disabled_extruders
def setBuildPlate(self, build_plate_number: int) -> None:
self._build_plate_number = build_plate_number
@ -334,7 +338,7 @@ class StartSliceJob(Job):
if has_model_with_disabled_extruders:
self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
associated_disabled_extruders = {p + 1 for p in associated_disabled_extruders}
self.setMessage(", ".join(map(str, sorted(associated_disabled_extruders))))
self._associated_disabled_extruders = ", ".join(map(str, sorted(associated_disabled_extruders)))
return
# There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
@ -362,7 +366,12 @@ class StartSliceJob(Job):
for extruder_stack in global_stack.extruderList:
self._buildExtruderMessage(extruder_stack)
for plugin in CuraApplication.getInstance().getBackendPlugins():
backend_plugins = CuraApplication.getInstance().getBackendPlugins()
# Sort backend plugins by name. Not a very good strategy, but at least it is repeatable. This will be improved later.
backend_plugins = sorted(backend_plugins, key=lambda backend_plugin: backend_plugin.getId())
for plugin in backend_plugins:
if not plugin.usePlugin():
continue
for slot in plugin.getSupportedSlots():
@ -550,9 +559,13 @@ class StartSliceJob(Job):
start_gcode = settings["machine_start_gcode"]
# Remove all the comments from the start g-code
start_gcode = re.sub(r";.+?(\n|$)", "\n", start_gcode)
if settings["material_bed_temp_prepend"]:
bed_temperature_settings = ["material_bed_temperature", "material_bed_temperature_layer_0"]
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(bed_temperature_settings) # match {setting} as well as {setting, extruder_nr}
settings["material_bed_temp_prepend"] = re.search(pattern, start_gcode) == None
if settings["material_print_temp_prepend"]:
print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature", "print_temperature"]
pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr}
settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) is None

View File

@ -208,7 +208,14 @@ Item
anchors.rightMargin: UM.Theme.getSize("thin_margin").height
enabled: UM.Backend.state == UM.Backend.Done
currentIndex: UM.Backend.state == UM.Backend.Done ? dfFilenameTextfield.text.startsWith("MM")? 1 : 0 : 2
// Pre-select the correct index, depending on the situation (see the model-property below):
// - Don't select any post-slice-file-format when the engine isn't done.
// - Choose either the S-series or the Makerbot-series of printers' format otherwise, depending on the active printer.
// This way, the user can just click 'save' without having to worry about wether or not the format is right.
property int isMakerbotFormat: Cura.MachineManager.activeMachine.getOutputFileFormats.includes("application/x-makerbot") || Cura.MachineManager.activeMachine.getOutputFileFormats.includes("application/x-makerbot-sketch")
property int isBackendDone: UM.Backend.state == UM.Backend.Done
currentIndex: isBackendDone ? (isMakerbotFormat ? 1 : 0) : 2
textRole: "text"
valueRole: "value"

View File

@ -1,9 +1,8 @@
# Copyright (c) 2023 UltiMaker
# Copyright (c) 2024 UltiMaker
# Cura is released under the terms of the LGPLv3 or higher.
from io import StringIO, BufferedIOBase
import json
from typing import cast, List, Optional, Dict
from typing import cast, List, Optional, Dict, Tuple
from zipfile import BadZipFile, ZipFile, ZIP_DEFLATED
import pyDulcificum as du
@ -19,6 +18,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.i18n import i18nCatalog
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.FormatMaps import FormatMaps
from cura.Snapshot import Snapshot
from cura.Utils.Threading import call_on_qt_thread
from cura.CuraVersion import ConanInstalls
@ -39,16 +39,27 @@ class MakerbotWriter(MeshWriter):
suffixes=["makerbot"]
)
)
MimeTypeDatabase.addMimeType(
MimeType(
name="application/x-makerbot-sketch",
comment="Makerbot Toolpath Package",
suffixes=["makerbot"]
)
)
_PNG_FORMATS = [
_PNG_FORMAT = [
{"prefix": "isometric_thumbnail", "width": 120, "height": 120},
{"prefix": "isometric_thumbnail", "width": 320, "height": 320},
{"prefix": "isometric_thumbnail", "width": 640, "height": 640},
{"prefix": "thumbnail", "width": 90, "height": 90},
]
_PNG_FORMAT_METHOD = [
{"prefix": "thumbnail", "width": 140, "height": 106},
{"prefix": "thumbnail", "width": 212, "height": 300},
{"prefix": "thumbnail", "width": 960, "height": 1460},
{"prefix": "thumbnail", "width": 90, "height": 90},
]
_META_VERSION = "3.0.0"
# must be called from the main thread because of OpenGL
@ -74,6 +85,7 @@ class MakerbotWriter(MeshWriter):
return None
def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode=MeshWriter.OutputMode.BinaryMode) -> bool:
metadata, file_format = self._getMeta(nodes)
if mode != MeshWriter.OutputMode.BinaryMode:
Logger.log("e", "MakerbotWriter does not support text mode.")
self.setInformation(catalog.i18nc("@error:not supported", "MakerbotWriter does not support text mode."))
@ -92,14 +104,20 @@ class MakerbotWriter(MeshWriter):
gcode_text_io = StringIO()
success = gcode_writer.write(gcode_text_io, None)
filename, filedata = "", ""
# Writing the g-code failed. Then I can also not write the gzipped g-code.
if not success:
self.setInformation(gcode_writer.getInformation())
return False
json_toolpaths = du.gcode_2_miracle_jtp(gcode_text_io.getvalue())
metadata = self._getMeta(nodes)
match file_format:
case "application/x-makerbot-sketch":
filename, filedata = "print.gcode", gcode_text_io.getvalue()
self._PNG_FORMATS = self._PNG_FORMAT
case "application/x-makerbot":
filename, filedata = "print.jsontoolpath", du.gcode_2_miracle_jtp(gcode_text_io.getvalue())
self._PNG_FORMATS = self._PNG_FORMAT + self._PNG_FORMAT_METHOD
case _:
raise Exception("Unsupported Mime type")
png_files = []
for png_format in self._PNG_FORMATS:
@ -116,10 +134,34 @@ class MakerbotWriter(MeshWriter):
try:
with ZipFile(stream, "w", compression=ZIP_DEFLATED) as zip_stream:
zip_stream.writestr("meta.json", json.dumps(metadata, indent=4))
zip_stream.writestr("print.jsontoolpath", json_toolpaths)
zip_stream.writestr(filename, filedata)
for png_file in png_files:
file, data = png_file["file"], png_file["data"]
zip_stream.writestr(file, data)
api = CuraApplication.getInstance().getCuraAPI()
metadata_json = api.interface.settings.getSliceMetadata()
# All the mapping stuff we have to do:
product_to_id_map = FormatMaps.getProductIdMap()
printer_name_map = FormatMaps.getInversePrinterNameMap()
extruder_type_map = FormatMaps.getInverseExtruderTypeMap()
material_map = FormatMaps.getInverseMaterialMap()
for key, value in metadata_json.items():
if "all_settings" in value:
if "machine_name" in value["all_settings"]:
machine_name = value["all_settings"]["machine_name"]
if machine_name in product_to_id_map:
machine_name = product_to_id_map[machine_name][0]
value["all_settings"]["machine_name"] = printer_name_map.get(machine_name, machine_name)
if "machine_nozzle_id" in value["all_settings"]:
extruder_type = value["all_settings"]["machine_nozzle_id"]
value["all_settings"]["machine_nozzle_id"] = extruder_type_map.get(extruder_type, extruder_type)
if "material_type" in value["all_settings"]:
material_type = value["all_settings"]["material_type"]
value["all_settings"]["material_type"] = material_map.get(material_type, material_type)
slice_metadata = json.dumps(metadata_json, separators=(", ", ": "), indent=4)
zip_stream.writestr("slicemetadata.json", slice_metadata)
except (IOError, OSError, BadZipFile) as ex:
Logger.log("e", f"Could not write to (.makerbot) file because: '{ex}'.")
self.setInformation(catalog.i18nc("@error", "MakerbotWriter could not save to the designated path."))
@ -127,7 +169,7 @@ class MakerbotWriter(MeshWriter):
return True
def _getMeta(self, root_nodes: List[SceneNode]) -> Dict[str, any]:
def _getMeta(self, root_nodes: List[SceneNode]) -> Tuple[Dict[str, any], str]:
application = CuraApplication.getInstance()
machine_manager = application.getMachineManager()
global_stack = machine_manager.activeMachine
@ -143,7 +185,9 @@ class MakerbotWriter(MeshWriter):
nodes.append(node)
meta = dict()
# This is a bit of a "hack", the mime type should be passed through with the export writer but
# since this is not the case we get the mime type from the global stack instead
file_format = global_stack.definition.getMetaDataEntry("file_formats")
meta["bot_type"] = global_stack.definition.getMetaDataEntry("reference_machine_id")
bounds: Optional[AxisAlignedBox] = None
@ -155,7 +199,8 @@ class MakerbotWriter(MeshWriter):
bounds = node_bounds
else:
bounds = bounds + node_bounds
if file_format == "application/x-makerbot-sketch":
bounds = None
if bounds is not None:
meta["bounding_box"] = {
"x_min": bounds.left,
@ -196,7 +241,7 @@ class MakerbotWriter(MeshWriter):
meta["extruder_temperature"] = materials_temps[0]
meta["extruder_temperatures"] = materials_temps
meta["model_counts"] = [{"count": 1, "name": node.getName()} for node in nodes]
meta["model_counts"] = [{"count": len(nodes), "name": "instance0"}]
tool_types = [extruder.variant.getMetaDataEntry("reference_extruder_id") for extruder in extruders]
meta["tool_type"] = tool_types[0]
@ -205,14 +250,13 @@ class MakerbotWriter(MeshWriter):
meta["version"] = MakerbotWriter._META_VERSION
meta["preferences"] = dict()
for node in nodes:
bounds = node.getBoundingBox()
meta["preferences"][str(node.getName())] = {
bounds = application.getBuildVolume().getBoundingBox()
meta["preferences"]["instance0"] = {
"machineBounds": [bounds.right, bounds.back, bounds.left, bounds.front] if bounds is not None else None,
"printMode": CuraApplication.getInstance().getIntentManager().currentIntentCategory,
}
meta["miracle_config"] = {"gaggles": {str(node.getName()): {} for node in nodes}}
meta["miracle_config"] = {"gaggles": {"instance0": {}}}
version_info = dict()
cura_engine_info = ConanInstalls.get("curaengine", {"version": "unknown", "revision": "unknown"})
@ -245,7 +289,7 @@ class MakerbotWriter(MeshWriter):
# platform_temperature
# total_commands
return meta
return meta, file_format
def meterToMillimeter(value: float) -> float:

View File

@ -11,14 +11,23 @@ catalog = i18nCatalog("cura")
def getMetaData():
file_extension = "makerbot"
return {
"mesh_writer": {
"output": [{
"mesh_writer":
{
"output": [
{
"extension": file_extension,
"description": catalog.i18nc("@item:inlistbox", "Makerbot Printfile"),
"mime_type": "application/x-makerbot",
"mode": MakerbotWriter.MakerbotWriter.OutputMode.BinaryMode,
}],
},
{
"extension": file_extension,
"description": catalog.i18nc("@item:inlistbox", "Makerbot Sketch Printfile"),
"mime_type": "application/x-makerbot-sketch",
"mode": MakerbotWriter.MakerbotWriter.OutputMode.BinaryMode,
}
]
},
}

View File

@ -26,27 +26,40 @@ class InsertAtLayerChange(Script):
},
"gcode_to_add":
{
"label": "G-code to insert.",
"label": "G-code to insert",
"description": "G-code to add before or after layer change.",
"type": "str",
"default_value": ""
},
"skip_layers":
{
"label": "Skip layers",
"description": "Number of layers to skip between insertions (0 for every layer).",
"type": "int",
"default_value": 0,
"minimum_value": 0
}
}
}"""
def execute(self, data):
gcode_to_add = self.getSettingValueByKey("gcode_to_add") + "\n"
skip_layers = self.getSettingValueByKey("skip_layers")
count = 0
for layer in data:
# Check that a layer is being printed
lines = layer.split("\n")
for line in lines:
if ";LAYER:" in line:
index = data.index(layer)
if count == 0:
if self.getSettingValueByKey("insert_location") == "before":
layer = gcode_to_add + layer
else:
layer = layer + gcode_to_add
data[index] = layer
count = (count + 1) % (skip_layers + 1)
break
return data

View File

@ -153,7 +153,8 @@ class SimulationPass(RenderPass):
# In the current layer, we show just the indicated paths
if layer == self._layer_view._current_layer_num:
# We look for the position of the head, searching the point of the current path
index = int(self._layer_view.getCurrentPath())
index = int(self._layer_view.getCurrentPath()) if not math.isnan(
self._layer_view.getCurrentPath()) else 0
for polygon in layer_data.getLayer(layer).polygons:
# The size indicates all values in the two-dimension array, and the second dimension is
# always size 3 because we have 3D points.

View File

@ -1,5 +1,6 @@
# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import math
import sys
from PyQt6.QtCore import Qt
@ -216,7 +217,8 @@ class SimulationView(CuraView):
Logger.warn(
f"Binary search error (out of bounds): index {i}: left value {left_value} right value {right_value} and current time is {self._current_time}")
fractional_value = (self._current_time - left_value) / (right_value - left_value)
segment_duration = right_value - left_value
fractional_value = 0.0 if segment_duration == 0.0 else (self._current_time - left_value) / segment_duration
self.setPath(i + fractional_value)

View File

@ -356,7 +356,10 @@ geometry41core =
EndPrimitive();
}
if ((u_show_starts == 1) && (v_prev_line_type[0] != 1) && (v_line_type[0] == 1)) {
if ((u_show_starts == 1) && (
((v_prev_line_type[0] != 1) && (v_line_type[0] == 1)) ||
((v_prev_line_type[0] != 4) && (v_line_type[0] == 4))
)) {
float w = size_x;
float h = size_y;

View File

@ -24,6 +24,7 @@ from UM.Settings.InstanceContainer import InstanceContainer
from cura.CuraApplication import CuraApplication
from cura.Settings.GlobalStack import GlobalStack
from cura.Utils.Threading import call_on_qt_thread
from cura.API import CuraAPI
from UM.i18n import i18nCatalog
@ -85,7 +86,8 @@ class UFPWriter(MeshWriter):
try:
archive.addContentType(extension="json", mime_type="application/json")
setting_textio = StringIO()
json.dump(self._getSliceMetadata(), setting_textio, separators=(", ", ": "), indent=4)
api = CuraApplication.getInstance().getCuraAPI()
json.dump(api.interface.settings.getSliceMetadata(), setting_textio, separators=(", ", ": "), indent=4)
steam = archive.getStream(SLICE_METADATA_PATH)
steam.write(setting_textio.getvalue().encode("UTF-8"))
except EnvironmentError as e:
@ -210,57 +212,3 @@ class UFPWriter(MeshWriter):
return [{"name": item.getName()}
for item in DepthFirstIterator(node)
if item.getMeshData() is not None and not item.callDecoration("isNonPrintingMesh")]
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 = CuraApplication.getInstance().getPrintInformation()
machine_manager = CuraApplication.getInstance().getMachineManager()
settings = {
"material": {
"length": print_information.materialLengths,
"weight": print_information.materialWeights,
"cost": print_information.materialCosts,
},
"global": {
"changes": {},
"all_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, Application.getInstance().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)
# 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)
for i, extruder in enumerate(global_stack.extruderList):
# Add extruder fields to settings dictionary
settings[f"extruder_{i}"] = {
"changes": {},
"all_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)
# 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)
return settings

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

View File

@ -5,6 +5,7 @@ import urllib.parse
from json import JSONDecodeError
from time import time
from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast
from pathlib import Path
from PyQt6.QtCore import QUrl
from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
@ -38,14 +39,17 @@ class CloudApiClient:
# The cloud URL to use for this remote cluster.
ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
CLUSTER_API_ROOT = f"{ROOT_PATH}/connect/v1"
CURA_API_ROOT = f"{ROOT_PATH}/cura/v1"
DEFAULT_REQUEST_TIMEOUT = 10 # seconds
DEFAULT_REQUEST_TIMEOUT = 30 # seconds
# In order to avoid garbage collection we keep the callbacks in this list.
_anti_gc_callbacks = [] # type: List[Callable[[Any], None]]
# Custom machine definition ID to cloud cluster name mapping
_machine_id_to_name: Dict[str, str] = None
def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None:
"""Initializes a new cloud API client.
@ -84,13 +88,9 @@ class CloudApiClient:
# conversion!
# API points to "MakerBot Method" for a makerbot printertypes which we already changed to allign with other printer_type
method_x = {
"ultimaker_method":"MakerBot Method",
"ultimaker_methodx":"MakerBot Method X",
"ultimaker_methodxl":"MakerBot Method XL"
}
if machine_type in method_x:
machine_type = method_x[machine_type]
machine_id_to_name = self.getMachineIDMap()
if machine_type in machine_id_to_name:
machine_type = machine_id_to_name[machine_type]
else:
machine_type = machine_type.replace("_plus", "+")
machine_type = machine_type.replace("_", " ")
@ -174,6 +174,7 @@ class CloudApiClient:
:param cluster_id: The ID of the cluster.
:param cluster_job_id: The ID of the print job within the cluster.
:param action: The name of the action to execute.
:param data: Optional data to send with the POST request
"""
body = json.dumps({"data": data}).encode() if data else b""
@ -216,8 +217,11 @@ class CloudApiClient:
Logger.logException("e", "Could not parse the stardust response: %s", error.toDict())
return status_code, {"errors": [error.toDict()]}
def _parseResponse(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None:
def _parseResponse(self,
response: Dict[str, Any],
on_finished: Union[Callable[[CloudApiClientModel], Any],
Callable[[List[CloudApiClientModel]], Any]],
model_class: Type[CloudApiClientModel]) -> None:
"""Parses the given response and calls the correct callback depending on the result.
:param response: The response from the server, after being converted to a dict.
@ -276,3 +280,14 @@ class CloudApiClient:
self._anti_gc_callbacks.append(parse)
return parse
@classmethod
def getMachineIDMap(cls) -> Dict[str, str]:
if cls._machine_id_to_name is None:
try:
with open(Path(__file__).parent / "machine_id_to_name.json", "rt") as f:
cls._machine_id_to_name = json.load(f)
except Exception as e:
Logger.logException("e", f"Could not load machine_id_to_name.json: '{e}'")
cls._machine_id_to_name = {}
return cls._machine_id_to_name

View File

@ -331,7 +331,7 @@ class CloudOutputDevice(UltimakerNetworkedPrinterOutputDevice):
return False
[printer, *_] = self._printers
return printer.type in ("MakerBot Method X", "MakerBot Method XL")
return printer.type in ("MakerBot Method X", "MakerBot Method XL", "MakerBot Sketch")
@pyqtProperty(bool, notify=_cloudClusterPrintersChanged)
def supportsPrintJobActions(self) -> bool:

View File

@ -0,0 +1,7 @@
{
"ultimaker_method": "MakerBot Method",
"ultimaker_methodx": "MakerBot Method X",
"ultimaker_methodxl": "MakerBot Method XL",
"ultimaker_factor4": "Ultimaker Factor 4",
"ultimaker_sketch": "MakerBot Sketch"
}

View File

@ -152,9 +152,3 @@ class VersionUpgrade22to24(VersionUpgrade):
config.write(output)
return [filename], [output.getvalue()]
def getCfgVersion(self, serialised: str) -> int:
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised)
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
return format_version * 1000000 + setting_version

View File

@ -33,23 +33,6 @@ _renamed_i18n = {
class VersionUpgrade27to30(VersionUpgrade):
## Gets the version number from a CFG file in Uranium's 2.7 format.
#
# Since the format may change, this is implemented for the 2.7 format only
# and needs to be included in the version upgrade system rather than
# globally in Uranium.
#
# \param serialised The serialised form of a CFG file.
# \return The version number stored in the CFG file.
# \raises ValueError The format of the version number in the file is
# incorrect.
# \raises KeyError The format of the file is incorrect.
def getCfgVersion(self, serialised: str) -> int:
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised)
format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
return format_version * 1000000 + setting_version
## Upgrades a preferences file from version 2.7 to 3.0.
#

View File

@ -33,12 +33,6 @@ default_qualities_per_nozzle_and_material = {
class VersionUpgrade460to462(VersionUpgrade):
def getCfgVersion(self, serialised: str) -> int:
parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised)
format_version = int(parser.get("general", "version")) # Explicitly give an exception when this fails. That means that the file format is not recognised.
setting_version = int(parser.get("metadata", "setting_version", fallback = "0"))
return format_version * 1000000 + setting_version
def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]:
"""

View File

@ -17,6 +17,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.ConfigurationErrorMessage import ConfigurationErrorMessage
from cura.CuraApplication import CuraApplication
from cura.PrinterOutput.FormatMaps import FormatMaps
from cura.Machines.VariantType import VariantType
try:
@ -249,7 +250,7 @@ class XmlMaterialProfile(InstanceContainer):
machine_variant_map[definition_id][variant_name] = variant_dict
# Map machine human-readable names to IDs
product_id_map = self.getProductIdMap()
product_id_map = FormatMaps.getProductIdMap()
for definition_id, container in machine_container_map.items():
definition_id = container.getMetaDataEntry("definition")
@ -647,7 +648,7 @@ class XmlMaterialProfile(InstanceContainer):
self._dirty = False
# Map machine human-readable names to IDs
product_id_map = self.getProductIdMap()
product_id_map = FormatMaps.getProductIdMap()
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
for machine in machines:
@ -923,7 +924,7 @@ class XmlMaterialProfile(InstanceContainer):
result_metadata.append(base_metadata)
# Map machine human-readable names to IDs
product_id_map = cls.getProductIdMap()
product_id_map = FormatMaps.getProductIdMap()
for machine in data.iterfind("./um:settings/um:machine", cls.__namespaces):
machine_compatibility = common_compatibility
@ -1083,10 +1084,8 @@ class XmlMaterialProfile(InstanceContainer):
# Skip material properties (eg diameter) or metadata (eg GUID)
return
if instance.value is True:
data = "yes"
elif instance.value is False:
data = "no"
if tag_name != "cura:setting" and isinstance(instance.value, bool):
data = "yes" if instance.value else "no"
else:
data = str(instance.value)
@ -1129,29 +1128,6 @@ class XmlMaterialProfile(InstanceContainer):
id_list = list(id_list)
return id_list
__product_to_id_map: Optional[Dict[str, List[str]]] = None
@classmethod
def getProductIdMap(cls) -> Dict[str, List[str]]:
"""Gets a mapping from product names in the XML files to their definition IDs.
This loads the mapping from a file.
"""
if cls.__product_to_id_map is not None:
return cls.__product_to_id_map
plugin_path = cast(str, PluginRegistry.getInstance().getPluginPath("XmlMaterialProfile"))
product_to_id_file = os.path.join(plugin_path, "product_to_id.json")
with open(product_to_id_file, encoding = "utf-8") as f:
contents = ""
for line in f:
contents += line if "#" not in line else "".join([line.replace("#", str(n)) for n in range(1, 12)])
cls.__product_to_id_map = json.loads(contents)
cls.__product_to_id_map = {key: [value] for key, value in cls.__product_to_id_map.items()}
#This also loads "Ultimaker S5" -> "ultimaker_s5" even though that is not strictly necessary with the default to change spaces into underscores.
#However it is not always loaded with that default; this mapping is also used in serialize() without that default.
return cls.__product_to_id_map
@staticmethod
def _parseCompatibleValue(value: str):
"""Parse the value of the "material compatible" property."""

View File

@ -1,7 +1,7 @@
[project]
name = "printerlinter"
description = "Cura UltiMaker printer linting tool"
version = "0.1.1"
version = "0.1.2"
authors = [
{ name = "UltiMaker", email = "cura@ultimaker.com" }
]

View File

@ -32,3 +32,13 @@ class Diagnostic:
},
"Level": self.level
}
class GitComment:
def __init__(self, comment: str) -> None:
"""
@param comment: The comment text.
"""
self.comment = comment
def toDict(self) -> Dict[str, Any]:
return self.comment

View File

@ -6,20 +6,21 @@ from .linters.defintion import Definition
from .linters.linter import Linter
from .linters.meshes import Meshes
from .linters.directory import Directory
from .linters.formulas import Formulas
def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
""" Returns a Linter depending on the file format """
if not file.exists():
return None
return [Directory(file, settings)]
if ".inst" in file.suffixes and ".cfg" in file.suffixes:
return [Directory(file, settings), Profile(file, settings)]
if ".inst" in file.suffixes and file.suffixes[-1] == ".cfg":
return [Directory(file, settings), Profile(file, settings), Formulas(file, settings)]
if ".def" in file.suffixes and ".json" in file.suffixes:
if ".def" in file.suffixes and file.suffixes[-1] == ".json":
if file.stem in ("fdmprinter.def", "fdmextruder.def"):
return None
return [Directory(file, settings), Definition(file, settings)]
return [Formulas(file, settings)]
return [Directory(file, settings), Definition(file, settings), Formulas(file, settings)]
if file.parent.stem == "meshes":
return [Meshes(file, settings)]

View File

@ -13,8 +13,11 @@ class Definition(Linter):
def __init__(self, file: Path, settings: dict) -> None:
super().__init__(file, settings)
self._definitions = {}
self._definition_name = None
self._experimental_settings = []
self._loadDefinitionFiles(file)
self._content = self._file.read_text()
self._loadExperimentalSettings()
self._loadBasePrinterSettings()
@property
@ -28,6 +31,14 @@ class Definition(Linter):
for check in self.checkRedefineOverride():
yield check
if self._settings["checks"].get("diagnostic-material-temperature-defined", False):
for check in self.checkMaterialTemperature():
yield check
if self._settings["checks"].get("diagnostic-definition-experimental-setting", False):
for check in self.checkExperimentalSetting():
yield check
# Add other which will yield Diagnostic's
# TODO: A check to determine if the user set value is with the min and max value defined in the parent and doesn't trigger a warning
# TODO: A check if the key exist in the first place
@ -37,11 +48,10 @@ class Definition(Linter):
def checkRedefineOverride(self) -> Iterator[Diagnostic]:
""" Checks if definition file overrides its parents settings with the same value. """
definition_name = list(self._definitions.keys())[0]
definition = self._definitions[definition_name]
if "overrides" in definition and definition_name not in ("fdmprinter", "fdmextruder"):
definition = self._definitions[self._definition_name]
if "overrides" in definition and self._definition_name not in ("fdmprinter", "fdmextruder"):
for key, value_dict in definition["overrides"].items():
is_redefined, child_key, child_value, parent = self._isDefinedInParent(key, value_dict, definition['inherits'])
is_redefined, child_key, child_value, parent, inherited_by= self._isDefinedInParent(key, value_dict, definition['inherits'])
if is_redefined:
redefined = re.compile(r'.*(\"' + key + r'\"[\s\:\S]*?)\{[\s\S]*?\},?')
found = redefined.search(self._content)
@ -59,12 +69,55 @@ class Definition(Linter):
yield Diagnostic(
file = self._file,
diagnostic_name = "diagnostic-definition-redundant-override",
message = f"Overriding {key} with the same value ({child_key}: {child_value}) as defined in parent definition: {definition['inherits']}",
message = f"Overriding {key} with the same value ({child_key}: {child_value}) as defined in parent definition: {inherited_by}",
level = "Warning",
offset = found.span(0)[0],
replacements = replacements
)
def checkMaterialTemperature(self) -> Iterator[Diagnostic]:
"""Checks if definition file has material tremperature defined within them"""
definition = self._definitions[self._definition_name]
if "overrides" in definition and self._definition_name not in ("fdmprinter", "fdmextruder"):
for key, value_dict in definition["overrides"].items():
if "temperature" in key and "material" in key:
redefined = re.compile(r'.*(\"' + key + r'\"[\s\:\S]*?)\{[\s\S]*?\},?')
found = redefined.search(self._content)
if len(found.group().splitlines()) > 1:
replacements = []
else:
replacements = [Replacement(
file=self._file,
offset=found.span(1)[0],
length=len(found.group()),
replacement_text="")]
yield Diagnostic(
file=self._file,
diagnostic_name="diagnostic-material-temperature-defined",
message=f"Overriding {key} as it belongs to material temperature catagory and shouldn't be placed in machine definitions",
level="Warning",
offset=found.span(0)[0],
replacements=replacements
)
def checkExperimentalSetting(self) -> Iterator[Diagnostic]:
"""Checks if definition uses experimental settings"""
definition = self._definitions[self._definition_name]
if "overrides" in definition and self._definition_name not in ("fdmprinter", "fdmextruder"):
for setting in definition["overrides"]:
if setting in self._experimental_settings:
redefined = re.compile(setting)
found = redefined.search(self._content)
yield Diagnostic(
file=self._file,
diagnostic_name="diagnostic-definition-experimental-setting",
message=f"Setting {setting} is still experimental and should not be used in default profiles",
level="Warning",
offset=found.span(0)[0]
)
def _loadDefinitionFiles(self, definition_file) -> None:
""" Loads definition file contents into self._definitions. Also load parent definition if it exists. """
definition_name = Path(definition_file.stem).stem
@ -72,6 +125,9 @@ class Definition(Linter):
if not definition_file.exists() or definition_name in self._definitions:
return
if self._definition_name is None:
self._definition_name = definition_name
# Load definition file into dictionary
self._definitions[definition_name] = json.loads(definition_file.read_text())
@ -85,7 +141,7 @@ class Definition(Linter):
def _isDefinedInParent(self, key, value_dict, inherits_from):
if self._ignore(key, "diagnostic-definition-redundant-override"):
return False, None, None, None
return False, None, None, None, None
if "overrides" not in self._definitions[inherits_from]:
return self._isDefinedInParent(key, value_dict, self._definitions[inherits_from]["inherits"])
@ -114,11 +170,17 @@ class Definition(Linter):
v = child_value
cv = check_value
if v == cv:
return True, child_key, child_value, parent
return True, child_key, child_value, parent, inherits_from
if "inherits" in parent:
return self._isDefinedInParent(key, value_dict, parent["inherits"])
return False, None, None, None
return False, None, None, None, None
def _loadExperimentalSettings(self):
try:
self._experimental_settings = self._definitions[self.base_def]["settings"]["experimental"]["children"].keys()
except:
pass
def _loadBasePrinterSettings(self):
settings = {}

View File

@ -1,7 +1,7 @@
from pathlib import Path
from typing import Iterator
from ..diagnostic import Diagnostic
from ..diagnostic import Diagnostic, GitComment
from .linter import Linter
@ -11,9 +11,12 @@ class Directory(Linter):
super().__init__(file, settings)
def check(self) -> Iterator[Diagnostic]:
if self._settings["checks"].get("diagnostic-resources-macos-app-directory-name", False):
if self._file.exists() and self._settings["checks"].get("diagnostic-resources-macos-app-directory-name", False):
for check in self.checkForDotInDirName():
yield check
elif self._settings["checks"].get("diagnostic-resource-file-deleted", False):
for check in self.checkFilesDeleted():
yield check
yield
@ -29,3 +32,8 @@ class Directory(Linter):
)
yield
def checkFilesDeleted(self) -> Iterator[GitComment]:
if not self._file.exists():
""" Check if there is a file that is deleted, this causes upgrade scripts to not work properly """
yield GitComment( f'File: **{self._file}** must not be deleted as it is not allowed. It will create issues upgrading Cura' )
yield

View File

@ -0,0 +1,186 @@
import difflib
import json
import os
import re
from configparser import ConfigParser
from pathlib import Path
from typing import Iterator
from ..diagnostic import Diagnostic
from ..replacement import Replacement
from .linter import Linter
FORMULA_NAMES = [
"extruderValue",
"extruderValues",
"anyExtruderWithMaterial",
"anyExtruderNrWithOrDefault",
"resolveOrValue",
"defaultExtruderPosition",
"valueFromContainer",
"extruderValueFromContainer",
"math",
"round",
"max",
"ceil",
"min",
"sqrt",
"log",
"tan",
"cos",
"sin",
"atan",
"acos",
"asin",
"floor",
"sum",
"len",
"radians",
"degrees"
]
DELIMITERS = [r'\+', '-', '=', '/', '\*', r'\(', r'\)', r'\[', r'\]', '{', '}', ' ', '^']
class Formulas(Linter):
"""Finds Typos in the definition files and their formulas."""
def __init__(self, file: Path, settings: dict) -> None:
super().__init__(file, settings)
self._cura_correction_strings = FORMULA_NAMES + list(self.getCuraSettingList())
self._definition = {}
def getCuraSettingList(self) -> list:
settings_list = []
with open(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "resources", "definitions", "fdmprinter.def.json")) as data:
json_data = json.load(data)
settings_list += self.extractKeys(json_data)
with open(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "resources", "definitions", "fdmextruder.def.json")) as data:
json_data = json.load(data)
settings_list += self.extractKeys(json_data)
return settings_list
def extractKeys(self, json_obj, parent_key=''):
keys_with_value = []
for key, values in json_obj.items():
new_key = key
if isinstance(values, dict):
if 'label' in values:
keys_with_value.append(new_key)
keys_with_value.extend(self.extractKeys(values, new_key))
return keys_with_value
def check(self) -> Iterator[Diagnostic]:
if self._settings["checks"].get("diagnostic-incorrect-formula", False):
for check in self.checkFormulas():
yield check
yield
def checkFormulas(self) -> Iterator[Diagnostic]:
self._loadDefinitionFiles(self._file)
self._content = self._file.read_text()
definition_name = list(self._definition.keys())[0]
definition = self._definition[definition_name]
if "overrides" in definition:
for key, value_dict in definition["overrides"].items():
for value in value_dict:
if value in ("enable", "resolve", "value", "minimum_value_warning", "maximum_value_warning",
"maximum_value", "minimum_value"):
key_incorrect = self.checkValueIncorrect(key)
if key_incorrect:
found = self._appendCorrections(key, key)
value_incorrect = self.checkValueIncorrect(value_dict[value])
if value_incorrect:
found = self._appendCorrections(key, value_dict[value])
if key_incorrect or value_incorrect:
if len(found.group().splitlines()) > 1:
replacements = []
else:
replacements = [Replacement(
file=self._file,
offset=found.span(1)[0],
length=len(found.group()),
replacement_text=self._replacement_text)]
yield Diagnostic(
file=self._file,
diagnostic_name="diagnostic-incorrect-formula",
message=f"Given formula {found.group()} seems incorrect, Do you mean {self._correct_formula}? please correct the formula and try again.",
level="Error",
offset=found.span(0)[0],
replacements=replacements
)
yield
def _appendCorrections(self, key, incorrectString):
if self._file.suffix == '.cfg':
key_with_incorrectValue = re.compile(r'(\b' + key + r'\b\s*=\s*[^=\n]+.*)')
else:
key_with_incorrectValue = re.compile(r'.*(\"' + key + r'\"[\s\:\S]*?)\{[\s\S]*?\},?')
found = key_with_incorrectValue.search(self._content)
if len(found.group().splitlines()) > 1:
self._replacement_text = ''
else:
self._replacement_text = found.group().replace(incorrectString, self._correct_formula).strip(' ')
return found
def _loadDefinitionFiles(self, definition_file) -> None:
""" Loads definition file contents into self._definition. Also load parent definition if it exists. """
definition_name = Path(definition_file.stem).stem
if not definition_file.exists() or definition_name in self._definition:
return
if definition_file.suffix == ".json":
# Load definition file into dictionary
self._definition[definition_name] = json.loads(definition_file.read_text())
if definition_file.suffix == ".cfg":
self._definition[definition_name] = self._parseCfg(definition_file)
def _parseCfg(self, file_path:Path) -> dict:
config = ConfigParser()
config.read([file_path])
file_data ={}
overrides = {}
available_sections = ["values"]
for section in available_sections:
if config.has_section(section):
options = config.options(section)
for option in options:
values ={}
values["value"] = config.get(section, option)
overrides[option] = values
file_data["overrides"]= overrides# Process the value here
return file_data
def checkValueIncorrect(self, formula) -> bool:
if isinstance(formula, str):
self._correct_formula = self._correctTyposInFormula(formula)
return self._correct_formula != formula
else:
return False
def _correctTyposInFormula(self, formula):
pattern = '|'.join(DELIMITERS)
tokens = re.split(pattern, formula)
output = formula
for token in tokens:
if '(' not in token and ')' not in token:
cleaned_token = re.sub(r'[^\w\s]', '', token)
possible_matches = difflib.get_close_matches(cleaned_token, self._cura_correction_strings, n=1, cutoff=0.8)
if possible_matches:
output = output.replace(cleaned_token, possible_matches[0])
return output

View File

@ -1,9 +1,42 @@
from typing import Iterator
import re
from typing import Iterator, Tuple
from ..diagnostic import Diagnostic
from .linter import Linter
from pathlib import Path
from configparser import ConfigParser
class Profile(Linter):
MAX_SIZE_OF_NAME = 20
def __init__(self, file: Path, settings: dict) -> None:
""" Finds issues in the parent directory"""
super().__init__(file, settings)
self._content = self._file.read_text()
def check(self) -> Iterator[Diagnostic]:
yield
if self._file.exists() and self._settings["checks"].get("diagnostic-long-profile-names", False):
for check in self.checklengthofProfileName():
yield check
def checklengthofProfileName(self) -> Iterator[Diagnostic]:
""" check the name of profile and where it is found"""
name_of_profile, found = self._getprofileName()
if len(name_of_profile) > Profile.MAX_SIZE_OF_NAME:
yield Diagnostic(
file=self._file,
diagnostic_name="diagnostic-long-profile-names",
message = f"The profile name **{name_of_profile}** exceeds the maximum length limit. For optimal results, please limit it to 20 characters or fewer.",
level="Warning",
offset = found.span(0)[0]
)
def _getprofileName(self) -> Tuple[str, bool]:
config = ConfigParser()
config.read([self._file])
name_of_profile = config.get("general", "name")
redefined = re.compile(re.escape(name_of_profile))
found = redefined.search(self._content)
return name_of_profile, found

View File

@ -19,6 +19,7 @@ def main() -> None:
parser.add_argument("--report", required=False, type=Path, help="Path where the diagnostic report should be stored")
parser.add_argument("--format", action="store_true", help="Format the files")
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")
@ -41,12 +42,26 @@ def main() -> None:
settings = yaml.load(f, yaml.FullLoader)
full_body_check = {"Diagnostics": []}
comments_check = {"Error Files": []}
for file in files:
if not path.exists(file):
print(f"Can't find the file: {file}")
return
if args.deleted:
for file in args.Files:
if file not in files:
deletedFiles = diagnoseIssuesWithFile(file, settings)
comments_check["Error Files"].extend([d.toDict() for d in deletedFiles])
results = yaml.dump(comments_check, default_flow_style=False, indent=4, width=240)
if report:
report.write_text(results)
else:
print(results)
if to_fix or to_diagnose:
for file in files:
diagnostics = diagnoseIssuesWithFile(file, settings)
@ -82,7 +97,6 @@ def diagnoseIssuesWithFile(file: Path, settings: dict) -> List[Diagnostic]:
return linter_results
def applyFixesToFile(file, settings, full_body_check) -> None:
if not file.exists():
return

View File

@ -206,9 +206,9 @@ chardet==3.0.4 \
idna==2.8 \
--hash=sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407 \
--hash=sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c
attrs==21.2.0 \
--hash=sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1 \
--hash=sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb
attrs==21.3.0 \
--hash=sha256:8f7335278dedd26b58c38e006338242cc0977f06d51579b2b8b87b9b33bff66c \
--hash=sha256:50f3c9b216dc9021042f71b392859a773b904ce1a029077f58f6598272432045
requests==2.22.0 \
--hash=sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4 \
--hash=sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31
@ -222,9 +222,9 @@ constantly==15.1.0 \
hyperlink==21.0.0 \
--hash=sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b \
--hash=sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4
incremental==21.3.0 \
--hash=sha256:02f5de5aff48f6b9f665d99d48bfc7ec03b6e3943210de7cfc88856d755d6f57 \
--hash=sha256:92014aebc6a20b78a8084cdd5645eeaa7f74b8933f70fa3ada2cfbd1e3b54321
incremental==22.10.0 \
--hash=sha256:b864a1f30885ee72c5ac2835a761b8fe8aa9c28b9395cacf27286602688d3e51 \
--hash=sha256:912feeb5e0f7e0188e6f42241d2f450002e11bbc0937c65865045854c24c0bd0
zope.interface==5.4.0 \
--hash=sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3 \
--hash=sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54 \

View File

@ -1303,6 +1303,24 @@
}
}
},
"GenericPETCF": {
"package_info": {
"package_id": "GenericPETCF",
"package_type": "material",
"display_name": "Generic PETCF",
"description": "The generic PET-CF profile which other profiles can be based upon.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://github.com/Ultimaker/fdm_materials",
"author": {
"author_id": "Generic",
"display_name": "Generic",
"email": "materials@ultimaker.com",
"website": "https://github.com/Ultimaker/fdm_materials",
"description": "Professional 3D printing made accessible."
}
}
},
"GenericPETG": {
"package_info": {
"package_id": "GenericPETG",
@ -1657,14 +1675,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/abs",
"website": "https://ultimaker.com/materials/s-series-abs/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-abs/printing-guidelines"
}
}
},
@ -1676,14 +1694,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/breakaway",
"website": "https://ultimaker.com/materials/s-series-breakaway/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-breakaway/printing-guidelines"
}
}
},
@ -1695,14 +1713,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/abs",
"website": "https://ultimaker.com/materials/s-series-cpe/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-cpe/printing-guidelines"
}
}
},
@ -1714,14 +1732,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/cpe",
"website": "https://ultimaker.com/materials/s-series-cpe-plus/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-cpe-plus/printing-guidelines"
}
}
},
@ -1733,14 +1751,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/abs",
"website": "https://ultimaker.com/materials/s-series-nylon/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-nylon/printing-guidelines"
}
}
},
@ -1752,14 +1770,52 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/pc",
"website": "https://ultimaker.com/materials/s-series-pc/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-pc/printing-guidelines"
}
}
},
"UltimakerPETCF": {
"package_info": {
"package_id": "UltimakerPETCF",
"package_type": "material",
"display_name": "Ultimaker PETCF",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/s-series-pet-carbon-fiber/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-pet-cf/printing-guidelines"
}
}
},
"UltimakerPETG": {
"package_info": {
"package_id": "UltimakerPETG",
"package_type": "material",
"display_name": "Ultimaker PETG",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/s-series-petg/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-petg/printing-guidelines"
}
}
},
@ -1771,14 +1827,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/abs",
"website": "https://ultimaker.com/materials/s-series-pla/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-pla/printing-guidelines"
}
}
},
@ -1790,14 +1846,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/pp",
"website": "https://ultimaker.com/materials/s-series-pp/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-pp/printing-guidelines"
}
}
},
@ -1809,33 +1865,14 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/abs",
"website": "https://ultimaker.com/materials/s-series-pva/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
}
}
},
"UltimakerTPU": {
"package_info": {
"package_id": "UltimakerTPU",
"package_type": "material",
"display_name": "Ultimaker TPU 95A",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/tpu-95a",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-pva/printing-guidelines"
}
}
},
@ -1847,14 +1884,166 @@
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/products/materials/tough-pla",
"website": "https://ultimaker.com/materials/s-series-tough-pla/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/en/resources/troubleshooting/materials"
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-tough-pla/printing-guidelines"
}
}
},
"UltimakerTPU": {
"package_info": {
"package_id": "UltimakerTPU",
"package_type": "material",
"display_name": "Ultimaker TPU 95A",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.4.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/s-series-tpu-95a/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://ultimaker.com/in/cura/materials/ultimaker-tpu-95a/printing-guidelines"
}
}
},
"UltimakerPPSCF": {
"package_info": {
"package_id": "UltimakerPPSCF",
"package_type": "material",
"display_name": "Ultimaker PPS-CF",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "1.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/factor-series-pps-carbon-fiber/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-UltiMaker-PPS-CF"
}
}
},
"ULTIMAKERBASCFMETHOD": {
"package_info": {
"package_id": "ULTIMAKERBASCFMETHOD",
"package_type": "material",
"display_name": "Ultimaker ABS-CF",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-abs-carbon-fiber/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-ABS-CF"
}
}
},
"ULTIMAKERABSRMETHOD": {
"package_info": {
"package_id": "ULTIMAKERABSRMETHOD",
"package_type": "material",
"display_name": "Ultimaker ABS-R",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-abs-r/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-ABS-R"
}
}
},
"ULTIMAKERASAMETHOD": {
"package_info": {
"package_id": "ULTIMAKERASAMETHOD",
"package_type": "material",
"display_name": "Ultimaker ASA",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-asa/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-ASA"
}
}
},
"ULTIMAKERNYLON12CFMETHOD": {
"package_info": {
"package_id": "ULTIMAKERNYLON12CFMETHOD",
"package_type": "material",
"display_name": "Ultimaker Nylon12 Carbon Fiber",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-nylon-12-carbon-fiber/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-Nylon12-CF"
}
}
},
"ULTIMAKERRAPIDRINSEMETHOD": {
"package_info": {
"package_id": "ULTIMAKERRAPIDRINSEMETHOD",
"package_type": "material",
"display_name": "Ultimaker RapidRinse",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-rapidrinse/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-RapidRinse"
}
}
},
"ULTIMAKERSR30METHOD": {
"package_info": {
"package_id": "ULTIMAKERSR30METHOD",
"package_type": "material",
"display_name": "Ultimaker SR-30",
"description": "Example package for material and quality profiles for Ultimaker materials.",
"package_version": "2.0.0",
"sdk_version": "8.6.0",
"website": "https://ultimaker.com/materials/method-series-sr-30/",
"author": {
"author_id": "UltimakerPackages",
"display_name": "UltiMaker",
"email": "materials@ultimaker.com",
"website": "https://ultimaker.com",
"description": "Professional 3D printing made accessible.",
"support_website": "https://support.ultimaker.com/s/article/How-to-print-with-Method-SR-30"
}
}
},

1
resources/conandata.yml Normal file
View File

@ -0,0 +1 @@
version: "5.8.0"

62
resources/conanfile.py Normal file
View File

@ -0,0 +1,62 @@
import os
from conan import ConanFile
from conan.tools.files import copy, update_conandata
from conan.tools.scm import Version
from conan.errors import ConanInvalidConfiguration
required_conan_version = ">=1.58.0 <2.0.0"
class CuraResource(ConanFile):
name = "cura_resources"
license = ""
author = "UltiMaker"
url = "https://github.com/Ultimaker/cura"
description = "Cura Resources"
topics = ("conan", "cura")
settings = "os", "compiler", "build_type", "arch"
no_copy_source = True
@property
def _shared_resources(self):
return ["definitions", "extruders", "images", "intent", "meshes", "quality", "variants"]
def set_version(self):
if not self.version:
self.version = self.conan_data["version"]
def export(self):
copy(self, pattern="LICENSE*", src=os.path.join(self.recipe_folder, ".."), dst=self.export_folder,
keep_path=False)
update_conandata(self, {"version": self.version})
def export_sources(self):
for shared_resources in self._shared_resources:
copy(self, pattern="*", src=os.path.join(self.recipe_folder, shared_resources),
dst=os.path.join(self.export_sources_folder, shared_resources))
def validate(self):
if Version(self.version) <= Version("4"):
raise ConanInvalidConfiguration("Only versions 5+ are support")
def layout(self):
self.cpp.source.resdirs = self._shared_resources
self.cpp.package.resdirs = [f"res/{res}" for res in self._shared_resources]
def package(self):
copy(self, "*", os.path.join(self.export_sources_folder),
os.path.join(self.package_folder, "res"))
def package_info(self):
self.cpp_info.includedirs = []
self.runenv_info.append_path("CURA_RESOURCES", os.path.join(self.package_folder, "res"))
self.runenv_info.append_path("CURA_ENGINE_SEARCH_PATH", os.path.join(self.package_folder, "res", "definitions"))
self.runenv_info.append_path("CURA_ENGINE_SEARCH_PATH", os.path.join(self.package_folder, "res", "extruders"))
self.env_info.CURA_RESOURCES.append(os.path.join(self.package_folder, "res"))
self.env_info.CURA_ENGINE_SEARCH_PATH.append(os.path.join(self.package_folder, "res", "definitions"))
self.env_info.CURA_ENGINE_SEARCH_PATH.append(os.path.join(self.package_folder, "res", "definitions"))
def package_id(self):
self.info.clear()

View File

@ -67,13 +67,13 @@
"layer_height_0": { "value": "round(machine_nozzle_size / 1.5, 2)" },
"layer_start_x":
{
"default_value": 105.0,
"enabled": false
"enabled": false,
"value": 105.0
},
"layer_start_y":
{
"default_value": 27.0,
"enabled": false
"enabled": false,
"value": 27.0
},
"line_width": { "value": "round(machine_nozzle_size * 0.875, 2)" },
"machine_acceleration": { "default_value": 3000 },
@ -130,7 +130,6 @@
"machine_min_cool_heat_time_window": { "default_value": 15.0 },
"machine_name": { "default_value": "Mark2_for_Ultimaker2" },
"machine_nozzle_cool_down_speed": { "default_value": 1.5 },
"machine_nozzle_head_distance": { "default_value": 5 },
"machine_nozzle_heat_up_speed": { "default_value": 3.5 },
"machine_nozzle_size": { "default_value": 0.4 },
"machine_show_variants": { "default_value": true },

View File

@ -0,0 +1,246 @@
{
"version": 2,
"name": "AnkerMake M5C",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "just-trey",
"manufacturer": "AnkerMake",
"file_formats": "text/x-gcode",
"platform": "ankermake_m5c_platform.obj",
"has_machine_quality": true,
"machine_extruder_trains": { "0": "ankermake_m5c_extruder_0" },
"platform_texture": "ankermake_m5c.png",
"preferred_material": "generic_pla",
"preferred_quality_type": "normal"
},
"overrides":
{
"acceleration_enabled": { "value": true },
"acceleration_infill": { "value": 5000 },
"acceleration_layer_0": { "value": 2500 },
"acceleration_prime_tower": { "value": 5000 },
"acceleration_print": { "value": 5000 },
"acceleration_print_layer_0": { "value": 2500 },
"acceleration_roofing": { "value": 2500 },
"acceleration_skirt_brim": { "value": 2500 },
"acceleration_support": { "value": 5000 },
"acceleration_support_bottom": { "value": 5000 },
"acceleration_support_infill": { "value": 5000 },
"acceleration_support_interface": { "value": 5000 },
"acceleration_support_roof": { "value": 5000 },
"acceleration_topbottom": { "value": 2500 },
"acceleration_travel_layer_0": { "value": 2500 },
"acceleration_wall": { "value": 5000 },
"acceleration_wall_x": { "value": 5000 },
"adhesion_type": { "default_value": "skirt" },
"alternate_extra_perimeter": { "value": true },
"bottom_layers": { "value": 3 },
"bottom_skin_expand_distance": { "value": 0.84 },
"bottom_skin_preshrink": { "value": 0.84 },
"bottom_thickness": { "value": 0.8 },
"bridge_fan_speed_2": { "value": 100 },
"bridge_fan_speed_3": { "value": 100 },
"bridge_settings_enabled": { "value": true },
"bridge_skin_density_2": { "value": 80 },
"bridge_skin_material_flow": { "value": 100 },
"bridge_skin_material_flow_2": { "value": 80 },
"bridge_skin_speed": { "value": 20 },
"bridge_skin_speed_2": { "value": 50 },
"bridge_skin_speed_3": { "value": 50 },
"bridge_wall_material_flow": { "value": 100 },
"bridge_wall_speed": { "value": 20 },
"connect_infill_polygons": { "value": false },
"cool_fan_full_at_height": { "value": 0.14 },
"cool_min_layer_time": { "value": 6 },
"cool_min_speed": { "value": 30 },
"cross_infill_pocket_size": { "value": 8 },
"expand_skins_expand_distance": { "value": 0.84 },
"fill_outline_gaps": { "value": false },
"gantry_height": { "value": 25 },
"gradual_infill_step_height": { "value": 2 },
"infill_angles":
{
"value": [
90
]
},
"infill_extruder_nr": { "value": -1 },
"infill_line_distance": { "value": 8 },
"infill_material_flow": { "value": 90 },
"infill_pattern": { "value": "'lines' if infill_sparse_density >= 25 else 'grid'" },
"infill_sparse_density": { "value": 10 },
"infill_sparse_thickness": { "value": 0.25 },
"infill_wipe_dist": { "value": 0.1 },
"initial_bottom_layers": { "value": 3 },
"jerk_enabled": { "value": true },
"jerk_infill": { "value": 15 },
"jerk_layer_0": { "value": 15 },
"jerk_prime_tower": { "value": 15 },
"jerk_print": { "value": 15 },
"jerk_print_layer_0": { "value": 15 },
"jerk_roofing": { "value": 15 },
"jerk_skirt_brim": { "value": 15 },
"jerk_support": { "value": 15 },
"jerk_support_bottom": { "value": 15 },
"jerk_support_infill": { "value": 15 },
"jerk_support_interface": { "value": 15 },
"jerk_support_roof": { "value": 15 },
"jerk_topbottom": { "value": 15 },
"jerk_travel": { "value": 15 },
"jerk_travel_layer_0": { "value": 15 },
"jerk_wall": { "value": 15 },
"jerk_wall_0": { "value": 15 },
"jerk_wall_x": { "value": 15 },
"machine_buildplate_type": { "value": "glass" },
"machine_depth": { "value": 220 },
"machine_heated_bed": { "value": true },
"machine_height": { "value": 250 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 30 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "AnkerMake M5" },
"machine_shape": { "value": "rectangular" },
"machine_show_variants": { "value": false },
"machine_start_gcode": { "default_value": "M104 S{material_print_temperature_layer_0} ; set final nozzle temp\nM190 S{material_bed_temperature_layer_0} ; set and wait for nozzle temp to stabilize\nM109 S{material_print_temperature_layer_0} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime)" },
"machine_width": { "value": 220 },
"material_diameter": { "default_value": 1.75 },
"material_flow_layer_0": { "value": 120 },
"material_no_load_move_factor": { "value": 0.94 },
"minimum_interface_area": { "value": 10 },
"minimum_support_area": { "value": "2 if support_structure == 'normal' else 0" },
"retract_at_layer_change": { "value": true },
"retraction_amount": { "value": 0.8 },
"retraction_combing": { "value": "noskin" },
"retraction_combing_max_distance": { "value": 3 },
"retraction_extrusion_window": { "value": 0.8 },
"retraction_min_travel": { "value": 0.8 },
"retraction_prime_speed": { "value": 60 },
"retraction_retract_speed": { "value": 60 },
"retraction_speed": { "value": 60 },
"roofing_angles": { "value": [] },
"roofing_monotonic": { "value": false },
"roofing_pattern": { "value": "zigzag" },
"skin_material_flow": { "value": 97 },
"skin_monotonic": { "default_value": true },
"skirt_brim_speed":
{
"maximum_value_warning": "550",
"value": 50
},
"skirt_line_count": { "value": 3 },
"small_feature_max_length": { "value": 9.42 },
"small_hole_max_size": { "value": 3 },
"speed_infill":
{
"maximum_value_warning": "550",
"value": 270
},
"speed_layer_0":
{
"maximum_value_warning": "550",
"value": 50
},
"speed_prime_tower":
{
"maximum_value_warning": "550",
"value": 500
},
"speed_print":
{
"maximum_value_warning": "550",
"value": 500
},
"speed_print_layer_0":
{
"maximum_value_warning": "550",
"value": 50
},
"speed_roofing":
{
"maximum_value_warning": "550",
"value": 150
},
"speed_support":
{
"maximum_value_warning": "550",
"value": 250
},
"speed_support_bottom":
{
"maximum_value_warning": "550",
"value": 166.667
},
"speed_support_infill":
{
"maximum_value_warning": "550",
"value": 250
},
"speed_support_interface":
{
"maximum_value_warning": "550",
"value": 166.667
},
"speed_support_roof":
{
"maximum_value_warning": "550",
"value": 166.667
},
"speed_topbottom":
{
"maximum_value_warning": "550",
"value": 150
},
"speed_travel":
{
"maximum_value_warning": "550",
"value": 500
},
"speed_travel_layer_0":
{
"maximum_value_warning": "550",
"value": 150
},
"speed_wall":
{
"maximum_value_warning": "550",
"value": 250
},
"speed_wall_0":
{
"maximum_value_warning": "550",
"value": 150
},
"speed_wall_x":
{
"maximum_value_warning": "550",
"value": 250
},
"speed_wall_x_roofing": { "maximum_value_warning": "550" },
"support_bottom_distance": { "value": 0.2 },
"support_brim_enable": { "value": false },
"support_brim_line_count": { "value": 20 },
"support_brim_width": { "value": 8 },
"support_infill_angles": { "value": [] },
"support_infill_rate": { "value": 30 },
"support_initial_layer_line_distance": { "value": 1.333 },
"support_line_distance": { "value": 1.333 },
"support_offset": { "value": 2 },
"support_top_distance": { "value": 0.2 },
"support_xy_distance": { "value": 0.8 },
"support_xy_overrides_z": { "value": "xy_overrides_z" },
"top_layers": { "value": 4 },
"top_skin_expand_distance": { "value": 0.84 },
"top_skin_preshrink": { "value": 0.84 },
"travel_avoid_distance": { "value": 0.63 },
"wall_0_extruder_nr": { "value": -1 },
"wall_extruder_nr": { "value": -1 },
"wall_line_width_0": { "value": 0.44 },
"wall_overhang_angle": { "value": 45 },
"wall_overhang_speed_factor": { "value": 40 },
"wall_thickness": { "value": 0.84 },
"wall_x_extruder_nr": { "value": -1 },
"zig_zaggify_infill": { "value": true }
}
}

View File

@ -65,7 +65,6 @@
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 340 },
"machine_name": { "default_value": "Atom 3" },
"machine_nozzle_head_distance": { "default_value": 6 },
"machine_shape": { "default_value": "elliptic" },
"machine_show_variants": { "default_value": true },
"machine_start_gcode": { "default_value": ";MACHINE START CODE\nG21 ;metric values\nG90 ;absolute positioning\nG28 ;home\nG1 Z5 F9000\n;MACHINE START CODE" },

View File

@ -0,0 +1,18 @@
{
"version": 2,
"name": "Creality CR-M4",
"inherits": "creality_base",
"metadata":
{
"visible": true,
"quality_definition": "creality_base"
},
"overrides":
{
"gantry_height": { "value": 35 },
"machine_depth": { "default_value": 450 },
"machine_height": { "default_value": 470 },
"machine_name": { "default_value": "Creality CR-M4" },
"machine_width": { "default_value": 450 }
}
}

View File

@ -0,0 +1,59 @@
{
"version": 2,
"name": "Creality Ender-3 V3 KE",
"inherits": "creality_base",
"metadata":
{
"visible": true,
"manufacturer": "Creality3D",
"file_formats": "text/x-gcode",
"platform": "creality_ender3.3mf",
"first_start_actions": [ "MachineSettingsAction" ],
"has_machine_quality": true,
"has_materials": true,
"has_variants": true,
"machine_extruder_trains": { "0": "creality_base_extruder_0" },
"preferred_material": "generic_pla",
"preferred_quality_type": "standard",
"preferred_variant_name": "0.4mm Nozzle",
"quality_definition": "creality_base",
"variants_name": "Nozzle Size"
},
"overrides":
{
"gantry_height": { "value": 38 },
"machine_depth": { "default_value": 220 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z5 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X2 Y218 F3000 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"default_value": [
[-20, 10],
[10, 10],
[10, -10],
[-20, -10]
]
},
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 240 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 8000.0 },
"machine_max_acceleration_y": { "value": 8000.0 },
"machine_max_acceleration_z": { "value": 500.0 },
"machine_max_feedrate_e": { "value": 100 },
"machine_max_feedrate_x": { "value": 500 },
"machine_max_feedrate_y": { "value": 500 },
"machine_max_feedrate_z": { "value": 30 },
"machine_name": { "default_value": "Creality Ender-3 V3 KE" },
"machine_start_gcode": { "default_value": "M220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X-2.0 Y20 Z0.28 F5000.0 ;Move to start position\nM109 S{material_print_temperature_layer_0}\nG1 X-2.0 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X-1.7 Y145.0 Z0.28 F5000.0 ;Move to side a little\nG1 X-1.7 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 E-1 F1800 ;Retract a bit\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 E0 F1800" },
"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_max_distance": { "value": 5.0 },
"retraction_extrusion_window": { "value": "retraction_amount" },
"retraction_min_travel": { "value": 2.0 },
"retraction_speed": { "default_value": 40 },
"speed_layer_0": { "value": 100 },
"speed_print": { "value": 300 }
}
}

View File

@ -45,7 +45,7 @@
"machine_max_feedrate_y": { "value": 500 },
"machine_max_feedrate_z": { "value": 30 },
"machine_name": { "default_value": "Creality Ender-3 V3 SE" },
"machine_start_gcode": { "default_value": "M220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nM420 S1; Use saved mesh leveling data\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nM190 S{material_bed_temperature_layer_0} ; Set bed temperature and wait\nM109 S{material_print_temperature_layer_0} ; Set hotend temperature and wait\nG1 X10.1 Y145.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y145.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 E-1.0000 F1800 ;Retract a bit\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 E0.0000 F1800 \n" },
"machine_start_gcode": { "default_value": "M220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nM420 S1; Use saved mesh leveling data\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X-3 Y20 Z0.28 F5000.0 ;Move to start position\nM190 S{material_bed_temperature_layer_0} ; Set bed temperature and wait\nM109 S{material_print_temperature_layer_0} ; Set hotend temperature and wait\nG1 X-3 Y100.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X-2 Y100.0 Z0.28 F5000.0 ;Move to side a little\nG1 X-2 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 E-1.0000 F1800 ;Retract a bit\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 E0.0000 F1800 \n" },
"machine_width": { "default_value": 220 },
"retraction_amount": { "value": 0.8 },
"retraction_speed": { "default_value": 40 },

View File

@ -13,7 +13,7 @@
"cool_min_layer_time": { "value": 5 },
"gantry_height": { "value": 25 },
"machine_depth": { "default_value": 225 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" },
"machine_head_with_fans_polygon":
{
"default_value": [

View File

@ -0,0 +1,55 @@
{
"version": 2,
"name": "Creality K1 Max",
"inherits": "creality_base",
"metadata":
{
"visible": true,
"author": "Itay Grudev",
"manufacturer": "Creality3D",
"file_formats": "text/x-gcode",
"first_start_actions": [ "MachineSettingsAction" ],
"has_machine_quality": true,
"has_materials": true,
"has_variants": true,
"machine_extruder_trains": { "0": "creality_k1max_extruder_0" },
"preferred_material": "generic_pla",
"preferred_quality_type": "standard",
"preferred_variant_name": "0.4mm Nozzle",
"quality_definition": "creality_base",
"variants_name": "Nozzle Size"
},
"overrides":
{
"gantry_height": { "value": 45 },
"machine_depth": { "default_value": 300 },
"machine_end_gcode": { "default_value": "END_PRINT" },
"machine_head_with_fans_polygon":
{
"default_value": [
[-50, 40],
[-50, -62],
[25, 40],
[25, -62]
]
},
"machine_heated_bed": { "default_value": true },
"machine_height": { "default_value": 300 },
"machine_max_acceleration_e": { "value": 5000 },
"machine_max_acceleration_x": { "value": 20000.0 },
"machine_max_acceleration_y": { "value": 20000.0 },
"machine_max_acceleration_z": { "value": 500.0 },
"machine_max_feedrate_e": { "value": 100 },
"machine_max_feedrate_x": { "value": 800 },
"machine_max_feedrate_y": { "value": 800 },
"machine_max_feedrate_z": { "value": 30 },
"machine_max_jerk_e": { "value": 2.5 },
"machine_max_jerk_xy": { "value": 9 },
"machine_max_jerk_z": { "value": 2 },
"machine_name": { "default_value": "Creality K1 Max" },
"machine_start_gcode": { "default_value": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0}\n" },
"machine_width": { "default_value": 300 },
"retraction_amount": { "default_value": 0.5 },
"retraction_speed": { "default_value": 40 }
}
}

View File

@ -35,7 +35,6 @@
"machine_max_feedrate_y": { "default_value": 300 },
"machine_max_feedrate_z": { "default_value": 40 },
"machine_name": { "default_value": "Diytech 220" },
"machine_nozzle_head_distance": { "default_value": 3 },
"machine_nozzle_tip_outer_diameter": { "default_value": 1 },
"machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nG28\nG1 Z15 F200\nT0\nG92 E0\nG1 E16 F250\nG92 E0\n" },
"machine_use_extruder_offset_to_offset_coords": { "default_value": true },

View File

@ -52,13 +52,13 @@
"layer_height_0": { "value": "round(machine_nozzle_size / 1.5, 2)" },
"layer_start_x":
{
"default_value": 180.0,
"enabled": false
"enabled": false,
"value": 180.0
},
"layer_start_y":
{
"default_value": 160.0,
"enabled": false
"enabled": false,
"value": 160.0
},
"line_width": { "value": "round(machine_nozzle_size * 0.875, 2)" },
"machine_acceleration": { "default_value": 3000 },
@ -122,7 +122,6 @@
"machine_name": { "default_value": "dxu" },
"machine_nozzle_cool_down_speed": { "default_value": 1.5 },
"machine_nozzle_expansion_angle": { "default_value": 45 },
"machine_nozzle_head_distance": { "default_value": 5 },
"machine_nozzle_heat_up_speed": { "default_value": 3.5 },
"machine_nozzle_size": { "default_value": 0.4 },
"machine_show_variants": { "default_value": true },

View File

@ -0,0 +1,74 @@
{
"version": 2,
"name": "Eazao M500",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Eazao",
"manufacturer": "Eazao",
"file_formats": "text/x-gcode",
"has_machine_quality": false,
"has_materials": false,
"machine_extruder_trains": { "0": "eazao_m500_extruder_0" },
"preferred_quality_type": "normal"
},
"overrides":
{
"acceleration_print": { "value": 300 },
"acceleration_travel": { "value": 300 },
"adhesion_type": { "default_value": "'none'" },
"bottom_layers": { "value": 2 },
"cool_fan_enabled": { "value": false },
"infill_sparse_density": { "value": 0 },
"initial_bottom_layers": { "value": 2 },
"jerk_print": { "value": 10 },
"jerk_travel": { "value": "jerk_print" },
"jerk_travel_layer_0": { "value": "jerk_travel" },
"layer_height": { "value": 1.0 },
"layer_height_0": { "value": 1.0 },
"line_width": { "value": 3.0 },
"machine_acceleration": { "value": 300 },
"machine_center_is_zero": { "default_value": false },
"machine_depth": { "default_value": 320 },
"machine_end_gcode": { "default_value": "G92 Z0\nG1 F1500 Z10\nG28 X0 Y300 ;move X Y to min endstops\nM82\nM84 ;steppers off\n" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": false },
"machine_height": { "default_value": 520 },
"machine_max_acceleration_e": { "value": 500 },
"machine_max_acceleration_x": { "value": 500 },
"machine_max_acceleration_y": { "value": 500 },
"machine_max_acceleration_z": { "value": 50 },
"machine_max_feedrate_e": { "value": 25 },
"machine_max_feedrate_x": { "value": 100 },
"machine_max_feedrate_y": { "value": 100 },
"machine_max_feedrate_z": { "value": 5 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "Eazao M500" },
"machine_start_gcode": { "default_value": "G21 ;set units to millimeters\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home\nG1 Z25.0 F1500 ;move the platform down 15mm\nG92 E0 ;extruder reset\nG1 F300 E5\nG92 E0 ;The extruder is forced to zero again\nM302\nM163 S0 P0.85; Set Mix Factor\nM163 S1 P0.15; Set Mix Factor\nM164 S0" },
"machine_width": { "default_value": 320 },
"magic_spiralize": { "value": true },
"material_final_print_temperature": { "value": "0" },
"material_initial_print_temperature": { "value": "0" },
"material_print_temperature": { "value": "0" },
"material_print_temperature_layer_0": { "value": "0" },
"optimize_wall_printing_order": { "value": "True" },
"retraction_amount": { "value": 7 },
"retraction_combing": { "value": "'noskin'" },
"retraction_count_max": { "value": 100 },
"retraction_enable": { "value": false },
"retraction_extrusion_window": { "value": 10 },
"retraction_hop": { "value": 0.2 },
"speed_print": { "value": 20.0 },
"speed_travel": { "value": 20.0 },
"speed_wall": { "value": 20.0 },
"speed_wall_0": { "value": 20.0 },
"speed_wall_x": { "value": 20.0 },
"speed_z_hop": { "value": "machine_max_feedrate_z" },
"top_bottom_pattern": { "value": "concentric" },
"top_bottom_thickness": { "value": 3 },
"wall_thickness": { "value": 5.0 }
}
}

View File

@ -0,0 +1,74 @@
{
"version": 2,
"name": "Eazao M600",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Eazao",
"manufacturer": "Eazao",
"file_formats": "text/x-gcode",
"has_machine_quality": false,
"has_materials": false,
"machine_extruder_trains": { "0": "eazao_m600_extruder_0" },
"preferred_quality_type": "normal"
},
"overrides":
{
"acceleration_print": { "value": 300 },
"acceleration_travel": { "value": 300 },
"adhesion_type": { "default_value": "'none'" },
"bottom_layers": { "value": 2 },
"cool_fan_enabled": { "value": false },
"infill_sparse_density": { "value": 0 },
"initial_bottom_layers": { "value": 2 },
"jerk_print": { "value": 10 },
"jerk_travel": { "value": "jerk_print" },
"jerk_travel_layer_0": { "value": "jerk_travel" },
"layer_height": { "value": 1.0 },
"layer_height_0": { "value": 1.0 },
"line_width": { "value": 3.0 },
"machine_acceleration": { "value": 300 },
"machine_center_is_zero": { "default_value": false },
"machine_depth": { "default_value": 420 },
"machine_end_gcode": { "default_value": "G92 Z0\nG1 F1500 Z10\nG28 X0 Y400 ;move X Y to min endstops\nM82\nM84 ;steppers off\n" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": false },
"machine_height": { "default_value": 620 },
"machine_max_acceleration_e": { "value": 500 },
"machine_max_acceleration_x": { "value": 500 },
"machine_max_acceleration_y": { "value": 500 },
"machine_max_acceleration_z": { "value": 50 },
"machine_max_feedrate_e": { "value": 25 },
"machine_max_feedrate_x": { "value": 100 },
"machine_max_feedrate_y": { "value": 100 },
"machine_max_feedrate_z": { "value": 5 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "Eazao M600" },
"machine_start_gcode": { "default_value": "G21 ;set units to millimeters\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home\nG1 Z25.0 F1500 ;move the platform down 15mm\nG92 E0 ;extruder reset\nG1 F300 E5\nG92 E0 ;The extruder is forced to zero again\nM302\nM163 S0 P0.87; Set Mix Factor\nM163 S1 P0.13; Set Mix Factor\nM164 S0" },
"machine_width": { "default_value": 420 },
"magic_spiralize": { "value": true },
"material_final_print_temperature": { "value": "0" },
"material_initial_print_temperature": { "value": "0" },
"material_print_temperature": { "value": "0" },
"material_print_temperature_layer_0": { "value": "0" },
"optimize_wall_printing_order": { "value": "True" },
"retraction_amount": { "value": 7 },
"retraction_combing": { "value": "'noskin'" },
"retraction_count_max": { "value": 100 },
"retraction_enable": { "value": false },
"retraction_extrusion_window": { "value": 10 },
"retraction_hop": { "value": 0.2 },
"speed_print": { "value": 20.0 },
"speed_travel": { "value": 20.0 },
"speed_wall": { "value": 20.0 },
"speed_wall_0": { "value": 20.0 },
"speed_wall_x": { "value": 20.0 },
"speed_z_hop": { "value": "machine_max_feedrate_z" },
"top_bottom_pattern": { "value": "concentric" },
"top_bottom_thickness": { "value": 3 },
"wall_thickness": { "value": 5.0 }
}
}

View File

@ -0,0 +1,74 @@
{
"version": 2,
"name": "Eazao M700",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Eazao",
"manufacturer": "Eazao",
"file_formats": "text/x-gcode",
"has_machine_quality": false,
"has_materials": false,
"machine_extruder_trains": { "0": "eazao_m700_extruder_0" },
"preferred_quality_type": "normal"
},
"overrides":
{
"acceleration_print": { "value": 300 },
"acceleration_travel": { "value": 300 },
"adhesion_type": { "default_value": "none" },
"bottom_layers": { "value": 2 },
"cool_fan_enabled": { "value": false },
"infill_sparse_density": { "value": 0 },
"initial_bottom_layers": { "value": 2 },
"jerk_print": { "value": 10 },
"jerk_travel": { "value": "jerk_print" },
"jerk_travel_layer_0": { "value": "jerk_travel" },
"layer_height": { "value": 1.0 },
"layer_height_0": { "value": 1.0 },
"line_width": { "value": 3.0 },
"machine_acceleration": { "value": 300 },
"machine_center_is_zero": { "default_value": false },
"machine_depth": { "default_value": 520 },
"machine_end_gcode": { "default_value": "G92 Z0\nG1 F1500 Z10\nG28 X0 Y500 ;move X Y to min endstops\nM82\nM84 ;steppers off\n" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": false },
"machine_height": { "default_value": 720 },
"machine_max_acceleration_e": { "value": 500 },
"machine_max_acceleration_x": { "value": 500 },
"machine_max_acceleration_y": { "value": 500 },
"machine_max_acceleration_z": { "value": 50 },
"machine_max_feedrate_e": { "value": 25 },
"machine_max_feedrate_x": { "value": 100 },
"machine_max_feedrate_y": { "value": 100 },
"machine_max_feedrate_z": { "value": 5 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "Eazao M700" },
"machine_start_gcode": { "default_value": "G21 ;set units to millimeters\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home\nG1 Z25.0 F1500 ;move the platform down 15mm\nG92 E0 ;extruder reset\nG1 F300 E5\nG92 E0 ;The extruder is forced to zero again\nM302\nM163 S0 P0.87; Set Mix Factor\nM163 S1 P0.13; Set Mix Factor\nM164 S0" },
"machine_width": { "default_value": 520 },
"magic_spiralize": { "value": true },
"material_final_print_temperature": { "value": "0" },
"material_initial_print_temperature": { "value": "0" },
"material_print_temperature": { "value": "0" },
"material_print_temperature_layer_0": { "value": "0" },
"optimize_wall_printing_order": { "value": "True" },
"retraction_amount": { "value": 7 },
"retraction_combing": { "value": "'noskin'" },
"retraction_count_max": { "value": 100 },
"retraction_enable": { "value": false },
"retraction_extrusion_window": { "value": 10 },
"retraction_hop": { "value": 0.2 },
"speed_print": { "value": 20.0 },
"speed_travel": { "value": 20.0 },
"speed_wall": { "value": 20.0 },
"speed_wall_0": { "value": 20.0 },
"speed_wall_x": { "value": 20.0 },
"speed_z_hop": { "value": "machine_max_feedrate_z" },
"top_bottom_pattern": { "value": "concentric" },
"top_bottom_thickness": { "value": 3 },
"wall_thickness": { "value": 5.0 }
}
}

View File

@ -0,0 +1,74 @@
{
"version": 2,
"name": "Eazao Potter",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "Eazao",
"manufacturer": "Eazao",
"file_formats": "text/x-gcode",
"has_machine_quality": false,
"has_materials": false,
"machine_extruder_trains": { "0": "eazao_potter_extruder_0" },
"preferred_quality_type": "normal"
},
"overrides":
{
"acceleration_print": { "value": 300 },
"acceleration_travel": { "value": 300 },
"adhesion_type": { "default_value": "'none'" },
"bottom_layers": { "value": 3 },
"cool_fan_enabled": { "value": false },
"infill_sparse_density": { "value": 0 },
"initial_bottom_layers": { "value": 3 },
"jerk_print": { "value": 10 },
"jerk_travel": { "value": "jerk_print" },
"jerk_travel_layer_0": { "value": "jerk_travel" },
"layer_height": { "value": 1.0 },
"layer_height_0": { "value": 1.0 },
"line_width": { "value": 3.0 },
"machine_acceleration": { "value": 300 },
"machine_center_is_zero": { "default_value": false },
"machine_depth": { "default_value": 167 },
"machine_end_gcode": { "default_value": "G92 Z0 E0\nG1 F1500 Z10 E-2\nM82\nM84 ;steppers off\n" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": false },
"machine_height": { "default_value": 280 },
"machine_max_acceleration_e": { "value": 500 },
"machine_max_acceleration_x": { "value": 500 },
"machine_max_acceleration_y": { "value": 500 },
"machine_max_acceleration_z": { "value": 50 },
"machine_max_feedrate_e": { "value": 25 },
"machine_max_feedrate_x": { "value": 100 },
"machine_max_feedrate_y": { "value": 100 },
"machine_max_feedrate_z": { "value": 5 },
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "Eazao Potter" },
"machine_start_gcode": { "default_value": "G21 ;set units to millimeters\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home\nG1 Z25.0 F1500 ;move the platform down 25mm\nG92 E0 ;extruder reset\nG1 F1500 E2\nG92 E0 ;The extruder is forced to zero again\nM302\nM163 S0 P0.85; Set Mix Factor\nM163 S1 P0.15; Set Mix Factor\nM164 S0" },
"machine_width": { "default_value": 167 },
"magic_spiralize": { "value": true },
"material_final_print_temperature": { "value": "0" },
"material_initial_print_temperature": { "value": "0" },
"material_print_temperature": { "value": "0" },
"material_print_temperature_layer_0": { "value": "0" },
"optimize_wall_printing_order": { "value": "True" },
"retraction_amount": { "value": 7 },
"retraction_combing": { "value": "'noskin'" },
"retraction_count_max": { "value": 100 },
"retraction_enable": { "value": false },
"retraction_extrusion_window": { "value": 10 },
"retraction_hop": { "value": 0.2 },
"speed_print": { "value": 25.0 },
"speed_travel": { "value": 25.0 },
"speed_wall": { "value": 25.0 },
"speed_wall_0": { "value": 25.0 },
"speed_wall_x": { "value": 25.0 },
"speed_z_hop": { "value": "machine_max_feedrate_z" },
"top_bottom_pattern": { "value": "concentric" },
"top_bottom_thickness": { "value": 3 },
"wall_thickness": { "value": 3.0 }
}
}

View File

@ -9,9 +9,8 @@
"manufacturer": "Eazao",
"file_formats": "text/x-gcode",
"has_machine_quality": false,
"has_materials": true,
"has_materials": false,
"machine_extruder_trains": { "0": "eazao_zero_extruder_0" },
"preferred_material": "generic_pla",
"preferred_quality_type": "normal"
},
"overrides":
@ -20,11 +19,10 @@
"acceleration_print": { "value": 300 },
"acceleration_travel": { "value": 300 },
"adhesion_type": { "default_value": "none" },
"bottom_layers": { "value": 2 },
"bottom_layers": { "value": 3 },
"cool_fan_enabled": { "value": false },
"infill_sparse_density": { "value": 0 },
"initial_bottom_layers": { "value": 2 },
"jerk_enabled": { "value": false },
"initial_bottom_layers": { "value": 3 },
"jerk_print": { "value": 10 },
"jerk_travel": { "value": "jerk_print" },
"jerk_travel_layer_0": { "value": "jerk_travel" },
@ -34,7 +32,7 @@
"machine_acceleration": { "value": 300 },
"machine_center_is_zero": { "default_value": false },
"machine_depth": { "default_value": 150 },
"machine_end_gcode": { "default_value": "G92 E10\nG1 E-10 F300\nG28 X0 Y0 ;move X Y to min endstops\nM82\nM84 ;steppers off\n" },
"machine_end_gcode": { "default_value": "G92 Z0 E0\nG1 F1500 E-2\nG1 F1500 Z10\nG28 X0;move X Y to min endstops\nM82\nM84 ;steppers off\n" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_heated_bed": { "default_value": false },
"machine_height": { "default_value": 240 },
@ -49,9 +47,10 @@
"machine_max_jerk_e": { "value": 5 },
"machine_max_jerk_xy": { "value": 10 },
"machine_max_jerk_z": { "value": 0.3 },
"machine_name": { "default_value": "EAZAO Zero" },
"machine_start_gcode": { "default_value": "G21 \nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home \nG1 Z15.0 F1500 ;move the platform down 15mm\nG92 E0 \nG1 F300 E10\nG92 E0\nM302\nM163 S0 P0.9; Set Mix Factor\nM163 S1 P0.1; Set Mix Factor\nM164 S0\n" },
"machine_name": { "default_value": "Eazao Zero" },
"machine_start_gcode": { "default_value": "G21 \nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG28 ;Home \nG1 Z15.0 F1500 ;move the platform down 15mm\nG92 E0 \nG1 F1500 E2\nG92 E0\nM302\nM163 S0 P0.8; Set Mix Factor\nM163 S1 P0.2; Set Mix Factor\nM164 S0\n" },
"machine_width": { "default_value": 150 },
"magic_spiralize": { "value": true },
"material_final_print_temperature": { "value": "0" },
"material_initial_print_temperature": { "value": "0" },
"material_print_temperature": { "value": "0" },
@ -64,18 +63,10 @@
"retraction_enable": { "value": false },
"retraction_extrusion_window": { "value": 10 },
"retraction_hop": { "value": 0.2 },
"retraction_hop_enabled": { "value": false },
"retraction_speed": { "value": 25 },
"speed_print": { "value": 20.0 },
"speed_travel": { "value": 20.0 },
"speed_wall": { "value": 20.0 },
"speed_wall_0": { "value": 20.0 },
"speed_wall_x": { "value": 20.0 },
"speed_print": { "value": 25.0 },
"speed_z_hop": { "value": "machine_max_feedrate_z" },
"top_bottom_thickness": { "value": 0 },
"travel_avoid_other_parts": { "value": true },
"travel_avoid_supports": { "value": false },
"travel_retract_before_outer_wall": { "value": false },
"top_bottom_pattern": { "value": "concentric" },
"top_bottom_thickness": { "value": 3 },
"wall_thickness": { "value": 3.0 }
}
}

View File

@ -11,7 +11,7 @@
"overrides":
{
"machine_depth": { "default_value": 210 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"value": [

View File

@ -24,7 +24,7 @@
"brim_width": { "default_value": 5 },
"gantry_height": { "value": 30 },
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F1600 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F1600 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_head_with_fans_polygon":
{

View File

@ -29,7 +29,7 @@
"gantry_height": { "value": 30 },
"machine_always_write_active_tool": { "default_value": true },
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-80 Z0.2 F1600 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-80 Z0.2 F1600 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_extruder_count": { "default_value": 2 },
"machine_extruders_share_heater": { "default_value": true },
"machine_extruders_share_nozzle": { "default_value": true },

View File

@ -11,7 +11,7 @@
"overrides":
{
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"value": [

View File

@ -11,7 +11,7 @@
"overrides":
{
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"value": [

View File

@ -12,7 +12,7 @@
{
"infill_overlap": { "value": "0 if infill_sparse_density < 40.01 and infill_pattern != 'concentric' else -5" },
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-8 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-8 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"value": [

View File

@ -22,7 +22,7 @@
"initial_layer_line_width_factor": { "value": "100.0 if resolveOrValue('adhesion_type') == 'raft' else 125 if line_width < 0.5 else 110" },
"machine_acceleration": { "value": 5000 },
"machine_depth": { "default_value": 230 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth - 5} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z2 ;Raise Z more\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth - 5} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_head_with_fans_polygon":
{

View File

@ -11,7 +11,7 @@
"overrides":
{
"machine_depth": { "default_value": 235 },
"machine_end_gcode": { "default_value": "G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-10 X5 Y5 Z3 F3000 ;Retract\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" },
"machine_head_with_fans_polygon":
{
"value": [

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