Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b2ae48e4a | ||
|
|
8095904bc2 | ||
|
|
b7c86befe1 | ||
|
|
526a91d323 | ||
|
|
493896260e | ||
|
|
ca83497bb6 | ||
|
|
6a985744c4 |
@@ -1,146 +0,0 @@
|
||||
name: Daily OFL OTA Update
|
||||
|
||||
# This workflow is intended for creating and publishing the OrcaFilamentLibrary (OFL) OPC package to
|
||||
# https://github.com/OrcaSlicer/orcaslicer-profiles, which generates an OTA update.
|
||||
# This cronjob runs daily at 00:00 UTC every day and scans main plus every release/vX.Y.Z branch for
|
||||
# changes to resources/profiles/OrcaFilamentLibrary since that branch's own last successful run. Any
|
||||
# branch with no changes is skipped; each changed branch gets its own post_merge_profiles.yml dispatch.
|
||||
#
|
||||
# OFL has no dedicated FOLDER_MERGERS grant (it isn't merged through the PR merge-bot delegation
|
||||
# scheme), so post_merge_profiles.yml is dispatched with an explicit `vendor` input, which that
|
||||
# workflow trusts and uses to bypass the FOLDER_MERGERS check for this trigger. That same explicit-
|
||||
# vendor-dispatch path is also what makes post_merge_profiles.yml call the OTA auto-publish API after
|
||||
# uploading - see post_merge_profiles.yml for both sides of that contract.
|
||||
#
|
||||
# If at least one branch was dispatched this run, a final step clears OFL's pending-publish
|
||||
# table (POST /api/v1/ota/ofl/pending/clear) - the daily "published everything, reset" signal.
|
||||
# That table is populated only by this pipeline's own auto-publish calls.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
actions: write # list this workflow's past runs and dispatch post_merge_profiles.yml
|
||||
contents: read
|
||||
|
||||
env:
|
||||
VENDOR: OrcaFilamentLibrary
|
||||
|
||||
jobs:
|
||||
daily-job:
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
# Full history: the per-branch "since last successful run" check below
|
||||
# needs to look arbitrarily far back if a prior run failed or was skipped.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fetch all branches
|
||||
shell: bash
|
||||
run: git fetch origin '+refs/heads/*:refs/remotes/origin/*'
|
||||
|
||||
- name: Scan branches and publish changed OFL profiles
|
||||
id: scan
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
published_any=false
|
||||
|
||||
mapfile -t branches < <(
|
||||
gh api "repos/${{ github.repository }}/branches" --paginate --jq '.[].name' \
|
||||
| grep -E '^(main|release/v[0-9]+\.[0-9]+\.[0-9]+)$' | sort -u
|
||||
)
|
||||
|
||||
for branch in "${branches[@]}"; do
|
||||
echo "::group::$branch"
|
||||
|
||||
# post_merge_profiles.yml's own run history, not this workflow's: this
|
||||
# workflow only ever runs against main (schedule, or workflow_dispatch
|
||||
# --ref main), so its head branch never varies - filtering ITS history
|
||||
# by $branch would never match anything except main. post_merge_profiles.yml
|
||||
# genuinely runs per-branch (this dispatch below sets --ref "$branch"),
|
||||
# so its history is the real per-branch checkpoint. It also means a
|
||||
# failed publish naturally gets retried tomorrow: the checkpoint only
|
||||
# advances on a run that actually succeeded.
|
||||
# --method GET is required, not cosmetic: gh api defaults to POST
|
||||
# whenever -f fields are present unless a method is given
|
||||
# explicitly, and POST on this list-runs endpoint 404s - confirmed
|
||||
# on real Actions infrastructure, not just reasoned about.
|
||||
since="$(gh api --method GET "repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
|
||||
-f status=success -f branch="$branch" -f per_page=1 \
|
||||
--jq '.workflow_runs[0].run_started_at // empty')"
|
||||
|
||||
if [ -z "$since" ]; then
|
||||
echo "No prior successful run for $branch; treating OFL as changed."
|
||||
changed=true
|
||||
else
|
||||
changed_files="$(git log --since="$since" --name-only --pretty=format: "origin/$branch" -- \
|
||||
resources/profiles/OrcaFilamentLibrary resources/profiles/OrcaFilamentLibrary.json \
|
||||
| sed '/^$/d')"
|
||||
if [ -n "$changed_files" ]; then
|
||||
echo "OFL changed on $branch since $since:"
|
||||
echo "$changed_files"
|
||||
changed=true
|
||||
else
|
||||
echo "No OFL changes on $branch since $since."
|
||||
changed=false
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$changed" = true ]; then
|
||||
# Tolerate a per-branch failure (e.g. a pre-existing release branch
|
||||
# whose post_merge_profiles.yml predates the vendor/auto_publish
|
||||
# inputs) rather than aborting the whole scan under set -e.
|
||||
if gh workflow run post_merge_profiles.yml \
|
||||
--repo "${{ github.repository }}" \
|
||||
--ref "$branch" \
|
||||
-f vendor="$VENDOR" -f auto_publish=true; then
|
||||
published_any=true
|
||||
else
|
||||
echo "::warning::failed to dispatch post_merge_profiles.yml for $branch - its post_merge_profiles.yml at this ref may predate the vendor/auto_publish inputs"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
echo "published_any=$published_any" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Clear OFL pending queue
|
||||
# Only when this run actually kicked off at least one publish - the
|
||||
# daily reset is scoped to today's real activity, not called on a day
|
||||
# where every branch reported no changes. Note "published_any" reflects
|
||||
# a successful DISPATCH, not a confirmed live publish: gh workflow run
|
||||
# is fire-and-forget, so this workflow never learns whether the
|
||||
# dispatched post_merge_profiles.yml run actually reached its own
|
||||
# auto-publish call. Acceptable since the table is populated only by
|
||||
# our own auto-publish calls, not by anything else.
|
||||
if: steps.scan.outputs.published_any == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
|
||||
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
|
||||
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
|
||||
|
||||
resp_file="$RUNNER_TEMP/ota-pending-clear-response.json"
|
||||
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
|
||||
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending/clear" \
|
||||
-H "Authorization: Bearer $OTA_API_KEY")"
|
||||
body="$(cat "$resp_file")"
|
||||
echo "$body"
|
||||
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::OTA pending-clear call failed with HTTP $status"
|
||||
exit 1
|
||||
fi
|
||||
@@ -5,9 +5,8 @@
|
||||
# re-sliced on its own to see whether it changes the G-code
|
||||
# harness - the GUI-vs-CLI parity harness (metrics only, never fails)
|
||||
# Both test the latest successful build_all.yml Linux AppImage from main, with
|
||||
# sources checked out at the commit that build was made from; a manual run can
|
||||
# name another branch, or pin one build by its run id. Nothing here gates a
|
||||
# build or a PR.
|
||||
# sources checked out at the commit that build was made from. Nothing here
|
||||
# gates a build or a PR.
|
||||
name: Parity Nightly
|
||||
|
||||
on:
|
||||
@@ -21,13 +20,9 @@ on:
|
||||
required: false
|
||||
default: "main"
|
||||
build_branch:
|
||||
description: "branch whose newest successful build_all artifact to test (a PR build is the PR merged into its base; sources are checked out at the PR head)"
|
||||
description: "branch whose latest successful build_all artifact to test"
|
||||
required: false
|
||||
default: "main"
|
||||
build_run_id:
|
||||
description: "build_all run id to test instead of build_branch's newest (same PR caveat)"
|
||||
required: false
|
||||
default: ""
|
||||
fixtures:
|
||||
description: "harness fixture ids, space-separated (empty = all)"
|
||||
required: false
|
||||
@@ -55,53 +50,14 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ inputs.build_branch || 'main' }}
|
||||
RUN_ID: ${{ inputs.build_run_id }}
|
||||
SCHEDULED: ${{ github.event_name == 'schedule' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -n "$RUN_ID" ]; then
|
||||
[[ $RUN_ID =~ ^[0-9]+$ ]] || { echo "build_run_id must be a numeric run id, got '$RUN_ID'" >&2; exit 1; }
|
||||
# a pinned build is read directly, not through a search; it must come
|
||||
# from this repository, because the later jobs check out its commit here
|
||||
found=$(gh api "repos/$GH_REPO/actions/runs/$RUN_ID" --jq \
|
||||
'select(.path == ".github/workflows/build_all.yml" and .conclusion == "success"
|
||||
and .head_repository.full_name == env.GH_REPO)
|
||||
| "\(.id) \(.head_sha) \(.created_at)"')
|
||||
[ -n "$found" ] || { echo "run $RUN_ID is not a successful build_all run of $GH_REPO" >&2; exit 1; }
|
||||
else
|
||||
# GitHub serves filtered run listings (branch=, status=, head_sha=, ...)
|
||||
# from a search index that has returned weeks-old results, while the
|
||||
# unfiltered listing stays current, so list unfiltered and filter here.
|
||||
# The repository check keeps out fork PRs whose branch has the same
|
||||
# name. A feature branch is normally built only for its PR, and a PR
|
||||
# build compiles the PR merged into its base rather than head_sha, so
|
||||
# a build of the branch itself (push or dispatch) is preferred when
|
||||
# the same page has one.
|
||||
pick='([.workflow_runs[] | select(.head_branch == env.BRANCH and .conclusion == "success"
|
||||
and .head_repository.full_name == env.GH_REPO)]
|
||||
| map(select(.event != "pull_request"))[0] // .[0])
|
||||
| select(.) | "\(.id) \(.head_sha) \(.created_at)"'
|
||||
# a page of 100 runs spans about a day and a half; a manual run may
|
||||
# target a branch that last built weeks ago
|
||||
pages=3
|
||||
if [ "$SCHEDULED" != true ]; then pages=20; fi
|
||||
found=""
|
||||
for page in $(seq "$pages"); do
|
||||
found=$(gh api "repos/$GH_REPO/actions/workflows/build_all.yml/runs?per_page=100&page=$page" --jq "$pick")
|
||||
if [ -n "$found" ]; then break; fi
|
||||
done
|
||||
[ -n "$found" ] || { echo "no successful $BRANCH build among the last $((pages * 100)) build_all runs; pass build_run_id to test an older one" >&2; exit 1; }
|
||||
fi
|
||||
read -r run_id head_sha created <<< "$found"
|
||||
# the nightly fails rather than report on a stale build
|
||||
if [ "$SCHEDULED" = true ] && [ $(( $(date +%s) - $(date -d "$created" +%s) )) -gt 172800 ]; then
|
||||
echo "newest $BRANCH build $run_id is from $created, over 48 hours old" >&2
|
||||
exit 1
|
||||
fi
|
||||
printf 'run_id=%s\nhead_sha=%s\n' "$run_id" "$head_sha" >> "$GITHUB_OUTPUT"
|
||||
gh run list --workflow build_all.yml \
|
||||
--branch "${{ inputs.build_branch || 'main' }}" \
|
||||
--status success --limit 1 --json databaseId,headSha \
|
||||
--jq '"run_id=\(.[0].databaseId)\nhead_sha=\(.[0].headSha)"' \
|
||||
>> "$GITHUB_OUTPUT"
|
||||
cat "$GITHUB_OUTPUT"
|
||||
echo "Testing build [$run_id](https://github.com/$GH_REPO/actions/runs/$run_id) of \`$head_sha\`, built $created" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
effect:
|
||||
name: Override sweep effect stage (shard ${{ matrix.shard }})
|
||||
|
||||
@@ -8,19 +8,6 @@ name: Post-merge profiles
|
||||
# only then does it become a live OTA update - this workflow does none of that
|
||||
# last part (no changelog, no R2, no webhook).
|
||||
#
|
||||
# A workflow_dispatch carrying a `vendor` input (e.g. the daily OFL cron - OFL has
|
||||
# no FOLDER_MERGERS grant, since it isn't merged through the PR merge-bot delegation
|
||||
# scheme) publishes that vendor directly and skips the FOLDER_MERGERS check below.
|
||||
# workflow_dispatch is already a trusted, explicit trigger, unlike the automatic
|
||||
# push-diff path the FOLDER_MERGERS check exists to gate.
|
||||
#
|
||||
# Separately, an ordinary push whose diff touches an OrcaFilamentLibrary company
|
||||
# folder (resources/profiles/OrcaFilamentLibrary/filament/<Company>/**) records
|
||||
# that PR as pending via POST /api/v1/ota/ofl/pending, regardless of whether
|
||||
# OrcaFilamentLibrary as a whole is authorized to publish in this same run - a
|
||||
# partner's OTA Manager dashboard should see a merged PR immediately, well
|
||||
# before the daily cron actually builds and publishes it.
|
||||
#
|
||||
# Asset contract expected by OrcaCloud's release scanner:
|
||||
# ^(\d+\.\d+\.\d+)_([^_]+)_(\d+(?:\.\d+){3})_(\d{12})\.zip$
|
||||
# <orca_ver>_<vendor>_<profile_version>_<UTC yyyymmddHHMM>.zip (zip root: <vendor>.opc)
|
||||
@@ -30,39 +17,16 @@ name: Post-merge profiles
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
# once v2.5.0 stable is released, this will be removed, so nightly won't receive OTA updates.
|
||||
- main
|
||||
# release/vX.Y.Z point-release branches only, not the release/vX.Y working
|
||||
# branch profile PRs land on first - "v*.*.*" requires two literal dots,
|
||||
# which release/vX.Y (one dot) doesn't have.
|
||||
- release/v*.*.*
|
||||
- release/*
|
||||
paths:
|
||||
- 'resources/profiles/**'
|
||||
- '.github/workflows/post_merge_profiles.yml'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
vendor:
|
||||
description: >-
|
||||
Publish only this vendor, bypassing the FOLDER_MERGERS grant check.
|
||||
For trusted explicit dispatches only (e.g. the OFL nightly cron).
|
||||
Leave empty to fall back to diffing the triggering commit.
|
||||
required: false
|
||||
type: string
|
||||
auto_publish:
|
||||
description: >-
|
||||
After publishing, also call the OTA auto-publish API to go live
|
||||
immediately, skipping the human changelog/Publish step. Separate
|
||||
from `vendor` on purpose: a maintainer can dispatch with just
|
||||
`vendor` set to rebuild/republish an asset without it going live.
|
||||
Only the OFL nightly cron should set this to true.
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read # commits/{sha}/pulls lookup in the OFL-pending step
|
||||
|
||||
# One run per branch; let a run finish rather than cancel it, since it publishes.
|
||||
concurrency:
|
||||
@@ -102,38 +66,8 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
FOLDER_MERGERS: ${{ vars.FOLDER_MERGERS }}
|
||||
DISPATCH_VENDOR: ${{ github.event_name == 'workflow_dispatch' && inputs.vendor || '' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# A vendor has a manifest plus either a preset directory or a version
|
||||
# field; this drops non-vendor files such as blacklist.json. Shared by
|
||||
# both the explicit-dispatch path below and the push-diff path further
|
||||
# down, so the definition of "valid vendor" can't drift between them.
|
||||
is_valid_vendor() {
|
||||
local v="$1"
|
||||
local json="resources/profiles/$v.json"
|
||||
[ -f "$json" ] && { [ -d "resources/profiles/$v" ] || jq -e '.version' "$json" >/dev/null 2>&1; }
|
||||
}
|
||||
|
||||
# Explicit vendor dispatch (e.g. the OFL cron): trust the caller and
|
||||
# skip both the git-diff detection and the FOLDER_MERGERS check below.
|
||||
if [ -n "$DISPATCH_VENDOR" ]; then
|
||||
v="$DISPATCH_VENDOR"
|
||||
# Becomes part of the release asset filename and the OTA API's
|
||||
# payload; keep it to the same charset every real vendor name uses.
|
||||
if ! [[ "$v" =~ ^[A-Za-z0-9]+$ ]]; then
|
||||
echo "::error::vendor '$v' must be alphanumeric"
|
||||
exit 1
|
||||
fi
|
||||
if ! is_valid_vendor "$v"; then
|
||||
echo "::error::vendor '$v' has no resources/profiles/$v.json with a profile directory or version field"
|
||||
exit 1
|
||||
fi
|
||||
echo "vendors=$v" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
base='${{ github.event.before }}'
|
||||
head='${{ github.sha }}'
|
||||
# Zero SHA (branch created / force push) or manual dispatch: fall back
|
||||
@@ -141,10 +75,6 @@ jobs:
|
||||
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
|
||||
base="$head^"
|
||||
fi
|
||||
# Exposed so the OFL-pending step below can reuse this exact diff
|
||||
# range instead of re-deriving it (and drifting from this logic).
|
||||
echo "base=$base" >> "$GITHUB_OUTPUT"
|
||||
echo "head=$head" >> "$GITHUB_OUTPUT"
|
||||
mapfile -t candidates < <(
|
||||
git diff --name-only "$base" "$head" -- resources/profiles \
|
||||
| sed -nE 's#^resources/profiles/([^/]+)/.*#\1#p; s#^resources/profiles/([^/]+)\.json$#\1#p' \
|
||||
@@ -154,7 +84,10 @@ jobs:
|
||||
vendors=()
|
||||
for v in "${candidates[@]:-}"; do
|
||||
[ -n "$v" ] || continue
|
||||
if is_valid_vendor "$v"; then
|
||||
json="resources/profiles/$v.json"
|
||||
# A vendor has a manifest plus either a preset directory or a version
|
||||
# field; this drops non-vendor files such as blacklist.json.
|
||||
if [ -f "$json" ] && { [ -d "resources/profiles/$v" ] || jq -e '.version' "$json" >/dev/null 2>&1; }; then
|
||||
vendors+=("$v")
|
||||
fi
|
||||
done
|
||||
@@ -168,9 +101,6 @@ jobs:
|
||||
# sibling bundle JSON are covered by at least one FOLDER_MERGERS
|
||||
# grant. The account part is intentionally ignored here: this is a
|
||||
# post-merge safety check, not an authorization check for a command.
|
||||
# An ineligible vendor (e.g. OrcaFilamentLibrary, which has no grant)
|
||||
# is dropped on its own - it never blocks other vendors in the same
|
||||
# push from publishing.
|
||||
grants=()
|
||||
while IFS= read -r raw_line; do
|
||||
line="${raw_line#"${raw_line%%[![:space:]]*}"}"
|
||||
@@ -197,29 +127,23 @@ jobs:
|
||||
return 1
|
||||
}
|
||||
|
||||
authorized=()
|
||||
unauthorized=()
|
||||
for v in "${vendors[@]}"; do
|
||||
if is_granted "resources/profiles/$v" && is_granted "resources/profiles/$v.json"; then
|
||||
authorized+=("$v")
|
||||
else
|
||||
if ! is_granted "resources/profiles/$v" || ! is_granted "resources/profiles/$v.json"; then
|
||||
unauthorized+=("$v")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#unauthorized[@]}" -ne 0 ]; then
|
||||
echo "::warning::skipping vendor(s) with no FOLDER_MERGERS grant (no asset built or published for them this run): ${unauthorized[*]}"
|
||||
echo "vendors=" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "vendors=${authorized[*]}" >> "$GITHUB_OUTPUT"
|
||||
echo "vendors=${vendors[*]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Resolve Orca version
|
||||
id: orca
|
||||
# Unconditional: needed both by the vendor-publish pipeline below (only
|
||||
# when vendors is non-empty) and by the OFL-pending step at the end
|
||||
# (which runs whenever OFL itself changed, even if vendors ends up
|
||||
# empty because OFL has no FOLDER_MERGERS grant). Cheap and harmless
|
||||
# to always resolve - version.inc is present on every commit.
|
||||
if: steps.vendors.outputs.vendors != ''
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -318,132 +242,3 @@ jobs:
|
||||
echo "### Published to \`$repo\` release \`$RELEASE_TAG\`"
|
||||
for f in "$ASSET_DIR"/*.zip; do echo "- \`$(basename "$f")\`"; done
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Notify OTA auto-publish
|
||||
# Gated on auto_publish specifically, not just "vendor was dispatched":
|
||||
# a maintainer manually dispatching with vendor=OrcaFilamentLibrary (e.g.
|
||||
# to rebuild/republish an asset while debugging) must not silently go
|
||||
# live. Only a caller that explicitly opts in with auto_publish=true
|
||||
# (the OFL nightly cron) skips the human changelog/Publish step.
|
||||
if: >-
|
||||
steps.vendors.outputs.vendors != '' && github.event_name == 'workflow_dispatch'
|
||||
&& (inputs.auto_publish == true || inputs.auto_publish == 'true')
|
||||
shell: bash
|
||||
env:
|
||||
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
|
||||
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
|
||||
ASSET_DIR: ${{ steps.pkg.outputs.dir }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
|
||||
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
|
||||
|
||||
mapfile -t zip_files < <(cd "$ASSET_DIR" && ls -1 *.zip)
|
||||
filenames_json="$(printf '%s\n' "${zip_files[@]}" | jq -R . | jq -s .)"
|
||||
payload="$(jq -n --argjson filenames "$filenames_json" '{filenames: $filenames}')"
|
||||
|
||||
resp_file="$RUNNER_TEMP/ota-auto-publish-response.json"
|
||||
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
|
||||
"${OTA_API_BASE_URL%/}/api/v1/ota/auto-publish" \
|
||||
-H "Authorization: Bearer $OTA_API_KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "$payload")"
|
||||
body="$(cat "$resp_file")"
|
||||
echo "$body"
|
||||
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::OTA auto-publish call failed with HTTP $status"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# A 200 can still carry per-file "error" results (e.g. NOT_FOUND); the
|
||||
# asset is already safely published to the profiles release above, but
|
||||
# it never went live, so treat that as a failure worth surfacing loudly.
|
||||
error_count="$(jq '[.results[] | select(.status == "error")] | length' <<< "$body")"
|
||||
if [ "$error_count" != "0" ]; then
|
||||
jq -r '.results[] | select(.status == "error") | "::error::\(.filename): \(.code) - \(.message)"' <<< "$body"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Record OFL pending changes
|
||||
# A real merge, never the cron's explicit-vendor dispatch (that's
|
||||
# automation publishing, not a new merge to report). This covers two
|
||||
# trigger shapes: an ordinary push, and a vendor-less workflow_dispatch
|
||||
# - the latter is exactly what pr-merge-bot.yml's re-dispatch after a
|
||||
# successful /bot merge looks like (a GITHUB_TOKEN-authored merge fires
|
||||
# no push event at all, which is why that re-dispatch exists). Both
|
||||
# land in the same diff-fallback path in "Resolve changed vendors", so
|
||||
# base/head/orca_ver are already correctly populated either way - only
|
||||
# this condition needs widening.
|
||||
# Placed last in the job on purpose: a failure here must never block
|
||||
# the vendor-publish pipeline above, which a step failing earlier in
|
||||
# the job would do (subsequent steps without always() get skipped).
|
||||
if: >-
|
||||
github.event_name == 'push' ||
|
||||
(github.event_name == 'workflow_dispatch' && !inputs.vendor)
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
|
||||
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
base='${{ steps.vendors.outputs.base }}'
|
||||
head='${{ steps.vendors.outputs.head }}'
|
||||
orca_ver='${{ steps.orca.outputs.orca_ver }}'
|
||||
|
||||
# Only real vendor subdirectories under filament/, e.g.
|
||||
# .../filament/Qidi/x.json -> "Qidi". This naturally excludes loose
|
||||
# top-level files (.../filament/Generic PLA @System.json - no further
|
||||
# slash to match) and is further filtered below to drop "base", the
|
||||
# shared @base/@System inheritance folder, not a partner company.
|
||||
mapfile -t ofl_companies < <(
|
||||
git diff --name-only "$base" "$head" -- resources/profiles/OrcaFilamentLibrary/filament \
|
||||
| sed -nE 's#^resources/profiles/OrcaFilamentLibrary/filament/([^/]+)/.*#\1#p' \
|
||||
| grep -vx 'base' \
|
||||
| sort -u
|
||||
)
|
||||
|
||||
if [ "${#ofl_companies[@]}" -eq 0 ]; then
|
||||
echo "No OFL company folders changed in this push."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
|
||||
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
|
||||
|
||||
# The head commit's own merged PR, not a per-commit walk: this
|
||||
# assumes the ordinary one-PR-per-push shape every other merge path
|
||||
# in this repo already assumes (pr-merge-bot.yml's re-dispatch logic
|
||||
# does the same). A merge commit's parents don't matter here - this
|
||||
# API call works the same regardless of merge strategy.
|
||||
pr_json="$(gh api "repos/${{ github.repository }}/commits/$head/pulls" \
|
||||
--jq '[.[] | select(.merged_at != null)] | sort_by(.merged_at) | last // empty')"
|
||||
|
||||
if [ -z "$pr_json" ]; then
|
||||
echo "::warning::push $head touches OFL compan(y/ies) (${ofl_companies[*]}) but has no associated merged PR; skipping pending record(s)"
|
||||
exit 0
|
||||
fi
|
||||
pr_number="$(jq -r '.number' <<< "$pr_json")"
|
||||
pr_url="$(jq -r '.html_url' <<< "$pr_json")"
|
||||
pr_title="$(jq -r '.title' <<< "$pr_json")"
|
||||
|
||||
for company in "${ofl_companies[@]}"; do
|
||||
payload="$(jq -n --arg vendor "$company" --arg ver "$orca_ver" --argjson pr "$pr_number" \
|
||||
--arg url "$pr_url" --arg title "$pr_title" \
|
||||
'{vendor: $vendor, orcaSlicerVersion: $ver, prNumber: $pr, prUrl: $url, prTitle: $title}')"
|
||||
|
||||
resp_file="$RUNNER_TEMP/ofl-pending-$company.json"
|
||||
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
|
||||
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending" \
|
||||
-H "Authorization: Bearer $OTA_API_KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "$payload")"
|
||||
body="$(cat "$resp_file")"
|
||||
echo "$body"
|
||||
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "::error::OFL pending record failed for vendor=$company (PR #$pr_number): HTTP $status"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -70,9 +70,6 @@ if (POLICY CMP0092)
|
||||
cmake_policy(SET CMP0092 NEW)
|
||||
endif ()
|
||||
|
||||
# project() reads this, so set it first.
|
||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/ClangClShowIncludes.cmake")
|
||||
|
||||
project(OrcaSlicer)
|
||||
|
||||
# Backward compatibility for old CMake versions
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# ccache does not parse the -clang: arguments CMake uses for clang-cl's gcc-style
|
||||
# depfile, so a cache hit writes the object and no depfile, and Ninja then records
|
||||
# no headers for that object. ccache reproduces /showIncludes output on a hit.
|
||||
foreach (_lang C CXX)
|
||||
if (CMAKE_${_lang}_COMPILER_ID STREQUAL "Clang" AND
|
||||
CMAKE_${_lang}_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
set(CMAKE_DEPFILE_FLAGS_${_lang} "/showIncludes")
|
||||
set(CMAKE_${_lang}_DEPFILE_FORMAT msvc)
|
||||
endif ()
|
||||
endforeach ()
|
||||
@@ -38,14 +38,6 @@ if(POLICY CMP0135) # DOWNLOAD_EXTRACT_TIMESTAMP
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif()
|
||||
|
||||
# project() reads this, so set it first. scripts/flatpak/make_deps_tar.sh packs deps/
|
||||
# without cmake/, so the file is missing in a Flatpak build.
|
||||
set(_rules_override "${CMAKE_CURRENT_LIST_DIR}/../cmake/modules/ClangClShowIncludes.cmake")
|
||||
if (EXISTS "${_rules_override}")
|
||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${_rules_override}")
|
||||
endif ()
|
||||
unset(_rules_override)
|
||||
|
||||
project(OrcaSlicer-deps)
|
||||
|
||||
# Backward compatibility for old CMake versions
|
||||
@@ -228,7 +220,6 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
|
||||
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
||||
-DCMAKE_USER_MAKE_RULES_OVERRIDE:STRING=${CMAKE_USER_MAKE_RULES_OVERRIDE}
|
||||
-DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE}
|
||||
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
|
||||
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
|
||||
@@ -276,7 +267,6 @@ else()
|
||||
-DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH}
|
||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
||||
-DCMAKE_USER_MAKE_RULES_OVERRIDE:STRING=${CMAKE_USER_MAKE_RULES_OVERRIDE}
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
${_cmake_osx_arch}
|
||||
"${_configs_line}"
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
# Deferred Page Construction: High Level Design
|
||||
|
||||
## Why it exists
|
||||
|
||||
The main window is a notebook of tabs. When the first frame appears, one tab is on
|
||||
screen and the others are not; some of them are opened later in the session, some
|
||||
never, and some only exist for certain printers. Building a tab before the first frame
|
||||
adds its cost to every startup, whether or not the tab is used.
|
||||
|
||||
This subsystem builds a tab's panel the first time the tab is shown. It also builds the
|
||||
remaining tabs while the user is idle after startup, in units of tens of milliseconds,
|
||||
so a click that lands in the middle of one waits for that unit and no longer. Startup
|
||||
pays only for what the first frame shows, and the other tabs are usually built before
|
||||
anyone opens them.
|
||||
|
||||
## The parts
|
||||
|
||||
`src/slic3r/GUI/Lazy.hpp`, `LazyPage.hpp`, `StagedBuild.hpp` and `IdleScheduler.hpp/.cpp`
|
||||
(with its wx-free `PrebuildQueue.hpp`) are independent. A holder can hold anything a
|
||||
factory makes, a page is a holder with a placeholder widget, a staged object can live
|
||||
outside a holder, and the scheduler knows none of them; it runs tasks, which `MainFrame`
|
||||
makes from the holders.
|
||||
|
||||
### Lazy<T>: the holder
|
||||
|
||||
Holds an object that a factory makes on first use, under a name for the log and a place
|
||||
in the idle queue, both given by the owner. The header has no wx dependency; the busy
|
||||
cursor for an on-demand build lives in `Lazy.cpp` and is skipped when there is no app,
|
||||
so the holder is unit-tested.
|
||||
|
||||
- `get()` is null until the object is completely built; `built()` says the same.
|
||||
- `ensure()` builds whatever is left now, under a busy cursor, and returns the object, or
|
||||
null in the two cases below. A click on an unbuilt tab or a first open of a dialog goes
|
||||
through this, and it logs the units and time it took.
|
||||
- `build_step()` runs one unit of construction and returns true while more remain. The
|
||||
first unit is the factory call, and each later unit is one `StagedBuild` step if the
|
||||
type has them.
|
||||
- `when_built(fn)` runs `fn(object)` now if it exists, otherwise once it is built.
|
||||
- `pending()` says whether the idle prebuild has work here: not built, and the factory
|
||||
has not returned null. A null factory result is logged and the holder stays unbuilt.
|
||||
- A unit that pumps the event loop cannot re-enter the holder; a nested `build_step()`
|
||||
does nothing and a nested `ensure()` returns null.
|
||||
- After a unit throws, the holder and the scheduler still run the next one.
|
||||
|
||||
The holder does not own the object; its wx parent does, as for any window. A type with
|
||||
one instance in the app derives from `LazyInstance<T>`, which points at that instance's
|
||||
holder (the holder registers itself, and a recreated `MainFrame`'s holder replaces the
|
||||
old frame's) and gives the type the static entry points the rest of the app uses,
|
||||
`T::if_built()`, `T::ensure()` and `T::when_built(fn)`. They return null, or do
|
||||
nothing, while no holder exists, so a caller needs no `mainframe` check.
|
||||
|
||||
`built()` is an atomic flag, since a job worker reads it through the statics
|
||||
(`MainFrame::get_calibration_curr_tab()`); it is set after the object is complete.
|
||||
|
||||
`LazyBase` is the holder's type-free interface (`name()`, `built()`, `pending()`,
|
||||
`build_step()`, `prebuild_order()`) and is what the scheduler side sees.
|
||||
|
||||
### LazyPage<Panel>: the placeholder
|
||||
|
||||
A `wxPanel` placed in the parent in place of the real panel, and a `Lazy<Panel>` whose
|
||||
factory makes the panel inside it (by default `new Panel(parent)`). The notebook needs a
|
||||
page object for the tab to exist and for `show_device()` to insert and remove tabs by
|
||||
pointer, and the placeholder is that object. `MainFrame` creates every page once, named
|
||||
after its `TAB_ID_*`, and keeps it for the frame's life; `show_device()` only moves
|
||||
pages in and out of the book.
|
||||
|
||||
- `Show()` is forwarded to the panel, so a panel's own `Show()` override stays its
|
||||
activation hook (refresh timers, machine sync) and `SelectPageByName()` works
|
||||
unchanged. A show builds the panel unless the frame itself is still hidden, because
|
||||
the book selects its first page as it is inserted; `MainFrame::Show()` shows the
|
||||
current page again when the frame becomes visible, which builds it.
|
||||
- `in_book()` says whether the parent notebook currently lists the page, and
|
||||
`pending()` is that and not built, so a tab `show_device()` has taken out of the book
|
||||
is not prebuilt.
|
||||
- A panel built while its page is hidden stays hidden, and a completed panel gets the
|
||||
dark-mode pass the frame ran before it existed.
|
||||
|
||||
The Compare presets dialog is a holder without a page. `MainFrame` keeps a
|
||||
`Lazy<DiffPresetDialog>` whose factory constructs the dialog and binds its events, the
|
||||
dialog derives from `LazyInstance`, and its callers use
|
||||
`DiffPresetDialog::ensure()->show()` and `DiffPresetDialog::if_built()` like a tab's
|
||||
callers do. Saving a preset refreshes the dialog only while it is shown, since `show()`
|
||||
reloads the presets.
|
||||
|
||||
### StagedBuild: construction in units
|
||||
|
||||
A mixin for a panel whose constructor is too big to be one unit. The constructor builds
|
||||
the skeleton (sizers, and the parts other code may touch) and queues the rest with
|
||||
`add_build_step()`. `build_step()` runs one step, and `add_build_steps_of(child)`
|
||||
forwards a child's steps so a nested panel is spread the same way; the parent is built
|
||||
only once the child is, including steps the child queues later. Steps run in order, on
|
||||
the main thread. A `Lazy<T>` recognises a staged type at compile time and runs its steps
|
||||
one per unit.
|
||||
|
||||
Nothing may touch what a step builds before the last step has run. In practice:
|
||||
|
||||
- nothing paints the panel before it is complete, since a panel built at idle is hidden
|
||||
with its page and one built on demand finishes inside `ensure()` before the event loop
|
||||
runs again;
|
||||
- members created in steps are initialised to null in the header, so a partially built
|
||||
panel can be destroyed;
|
||||
- timers and event handlers that use step content check `built()` first
|
||||
(`MonitorPanel::update_all()`, `CalibrationPanel::update_all()`), and a child's steps
|
||||
are queued before anything in the constructor can fire such a handler;
|
||||
- a destructor that disconnects from step content checks `built()` first;
|
||||
- a constructor or step does not take focus while the panel is off screen
|
||||
(`IsShownOnScreen()` before `SetFocus()`), since it may run while the user is typing
|
||||
elsewhere;
|
||||
- where a step's widgets must keep their place in a sizer that later steps also fill, the
|
||||
constructor adds an empty slot sizer in that position and the step fills the slot
|
||||
(`StatusBasePanel`).
|
||||
|
||||
### IdleScheduler: when to build
|
||||
|
||||
A task is any `LazyBase`: a name for the log, `pending()`, `build_step()` and
|
||||
`prebuild_order()`. `PrebuildQueue` holds the tasks by order (equal order in the order
|
||||
added) and runs a slice, the units of the first pending task until it completes, the
|
||||
budget is spent on the clock it is given, or the interrupt predicate says input arrived.
|
||||
It has no wx dependency and is unit-tested with a fake clock. A task whose work is gone
|
||||
is passed over and stays in the queue, so a tab that `show_device()` removes and later
|
||||
re-inserts is pending again.
|
||||
|
||||
`IdleScheduler` drives the queue with the real clock, the app's input timestamp and the
|
||||
Windows queue check, and logs each unit and slice. Each slice is a timer message: a
|
||||
period of 250 ms while waiting for the user to go quiet, and a one-shot of zero after a
|
||||
slice that left work, so the event loop dispatches whatever it has queued (paint,
|
||||
timers, input) before the next slice runs. Chaining slices with `CallAfter` would not do
|
||||
this: wx drains pending events fully before the next native message, on every platform.
|
||||
On GTK the one-shot is 5 ms, because a due GLib timeout runs ahead of the redraw and
|
||||
idle sources that paint and deliver posted events.
|
||||
A slice runs only once the user has been idle for the quiet time, and the timer stops
|
||||
itself once no task is pending. The tick period, quiet time and slice length are
|
||||
constants in `IdleScheduler.cpp`. A unit cannot be interrupted once started, so the
|
||||
largest unit bounds click latency on Windows; on the other platforms a click also waits
|
||||
for the rest of the slice. A unit that pumps the event loop lets the timer fire inside
|
||||
its own slice, and that tick does nothing.
|
||||
|
||||
The tasks are made by their owners. `MainFrame::prebuild_pages_when_idle()` registers
|
||||
every `LazyPage` the frame created, in or out of the book (`pending()` is false for a
|
||||
page out of the book), the Compare presets holder, and the Prepare sidebar's settings
|
||||
page from `ParamsPanel::settings_page_prebuild()`, whose first unit selects the default
|
||||
tab if none is selected yet and whose later units build one option group each.
|
||||
`show_device()` only restarts the timer. `MainFrame` owns the scheduler because it owns
|
||||
everything the tasks build, and clearing the queue with the frame is what keeps a task
|
||||
from outliving its object.
|
||||
|
||||
Idle time comes from `GUI_App::FilterEvent`, which timestamps mouse and keyboard events
|
||||
(`wxEVT_CATEGORY_USER_INPUT` minus command events, which all claim that category), and
|
||||
`GUI_App::input_idle_ms()` reports it. On Windows a slice additionally refuses to start
|
||||
when the message queue holds keyboard, button, touch or pen input. Mouse moves are
|
||||
excluded because Windows synthesises one whenever a window appears under the cursor,
|
||||
which every unit does. Other platforms use the timestamp alone.
|
||||
|
||||
Once every registered page is built the timer is stopped and the subsystem costs
|
||||
nothing.
|
||||
|
||||
## Rules
|
||||
|
||||
**What builds before the first frame.** The Prepare tab's plater, because `post_init()`
|
||||
needs its GL canvas on screen to initialise OpenGL in every startup state, and the start
|
||||
page the user configured. Home is built by the first `MainFrame::Show()`, Prepare's
|
||||
settings page by selecting the tab. `post_init()` passes through the Prepare tab for GL
|
||||
init under `Freeze()` with `MainFrame::select_prepare_for_gl_init()`, which changes the
|
||||
selection without the page-changed event, so nothing else is built for that pass.
|
||||
|
||||
**Reaching a lazy object.** A caller uses the type's own statics. `T::if_built()` may
|
||||
return null and is for telling the object something it can live without (a rescale, a
|
||||
colour change, a status update). `T::ensure()` builds the object and is for navigating
|
||||
to it or for a caller that is about to show it. A caller that tells the object something
|
||||
it would not fetch for itself on construction uses `T::when_built()`, which keeps the
|
||||
message until the object exists. A panel that pulls its state when constructed (the Home
|
||||
page requests the recent list on load, the Device tab reads the device manager on show)
|
||||
is reached with `if_built()`; one that cannot pull gets `when_built()`.
|
||||
|
||||
**Unit size.** A unit cannot be interrupted, so it should stay within the slice length on
|
||||
a fast machine. A constructor above that is staged. A single widget above it is the
|
||||
floor unless the widget itself is split.
|
||||
|
||||
**Order.** Cheapest and most likely to be opened first, given where `MainFrame` creates
|
||||
each holder. The settings page is 0 (the Prepare tab's own content), Home 10, Device 20
|
||||
(a Bambu user's usual second stop), Calibration 30, Multi-device 40, the web Device
|
||||
view 50, Project 60 (a second WebView2 instance) and the Compare presets dialog 100;
|
||||
steps of ten so a new tab takes a value between its neighbours without renumbering
|
||||
them. `MainFrame::prebuild_pages_when_idle()` registers the tasks once, from
|
||||
`post_init()`.
|
||||
|
||||
**Never prebuilt.** A holder with a negative order, for a tab that few sessions open
|
||||
and that costs more to build unasked than it saves (the Design tab), and plugin-provided
|
||||
tabs, which are Python-side and not lazy pages.
|
||||
|
||||
## Adopting it
|
||||
|
||||
A lazy tab needs:
|
||||
|
||||
1. The panel derived from `LazyInstance<Panel>`, since a tab's panel has one instance.
|
||||
2. A `LazyPage<Panel>*` member in `MainFrame`, created once in `init_tabpanel()` with
|
||||
its `TAB_ID_*` as the name, its place by the order rule (and a factory if
|
||||
`new Panel(parent)` is not enough), added to `m_lazy_pages`, and used wherever the
|
||||
tab is added to or looked up in the notebook.
|
||||
3. Every use of the panel outside `MainFrame` going through one of the panel's statics,
|
||||
chosen by the rule above. When converting an existing member, `grep` for it; the
|
||||
compiler finds the rest.
|
||||
4. A constructor that copes with the frame already existing and the user being busy
|
||||
elsewhere, since `wxGetApp().mainframe` is set, the frame may be shown, and the user
|
||||
may be typing when a lazy panel is built: no `SetFocus()` while off screen, and
|
||||
whatever the constructor did through a `MainFrame` accessor before (a mode update, a
|
||||
deferred URL) done in the constructor itself.
|
||||
|
||||
To stage a heavy constructor, inherit `StagedBuild`, keep the skeleton in the constructor,
|
||||
move the rest into `add_build_step()` lambdas in the original order, and follow the
|
||||
staged-panel rules above. Measure the units. A step that is still one big widget has to
|
||||
be split inside that widget or accepted as the floor.
|
||||
|
||||
Verification is by log. `MainFrame::prebuild_pages_when_idle` lists the queue it
|
||||
registered, `IdleScheduler::tick` reports each completed task by name at info level and
|
||||
each slice and unit at debug level, and `Lazy::ensure` reports an object a user built
|
||||
on demand with the units and time it took. A run from the configured start
|
||||
page shows every registered page complete, in order, with no unit longer than intended.
|
||||
A click on a tab mid-prebuild shows the finished panel with an `ensure` line for what was
|
||||
left, and the slices resume for the remaining tasks once the user is idle again.
|
||||
@@ -0,0 +1,119 @@
|
||||
# G-code preview while dragging
|
||||
|
||||
The sliced preview draws every toolpath segment of the plate as an instanced box. On a large
|
||||
plate that is tens of millions of segments, and the frame is GPU-bound: the cost is the number of
|
||||
instances drawn, not anything the CPU does per frame. Dragging the camera over such a plate cannot
|
||||
keep up. The `preview_reduced_detail_mode` preference (*Graphics > G-code Preview*, off by
|
||||
default) lets the preview draw less while the user drags and put the full toolpaths back when they
|
||||
let go.
|
||||
|
||||
| Preference | Values | Effect |
|
||||
|---|---|---|
|
||||
| `preview_reduced_detail_mode` | `off`, `solid`, `layers`, `outer_walls`, `shell` | what is drawn while dragging |
|
||||
| `preview_reduced_detail_layer_stride` | 1–20 | one layer in every N is kept by the toolpath modes |
|
||||
|
||||
libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the
|
||||
preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging.
|
||||
The OpenGL ES path keeps a single set and ignores the preference.
|
||||
|
||||
## Two sets, one walk
|
||||
|
||||
`ViewerImpl::update_enabled_entities()` walks the visible vertex range once and fills two segment
|
||||
index buffers side by side: the **full** set and the **reduced** set (segments and options).
|
||||
Building them together is what makes switching free: starting or ending a drag is a buffer
|
||||
binding, never a rebuild. A change of mode or stride does rebuild. Nothing is built while the mode
|
||||
is off, and the reduced buffers are then uploaded empty so that the last set does not stay
|
||||
allocated.
|
||||
|
||||
Whatever the mode leaves out, the bottom and top layers of the visible range are kept whole: they
|
||||
are the faces the range cuts open, and the top is what the user is looking at.
|
||||
|
||||
### Modes
|
||||
|
||||
- `EndLayersOnly` (`solid` in the preference) keeps only the two end layers. `GCodeViewer` then
|
||||
draws the sliced objects and the prime tower as opaque solids, see below.
|
||||
- `LayersOnly` (`layers`) keeps every role of one layer in every stride.
|
||||
- `OuterWallsOnly` (`outer_walls`) keeps the outer and overhang perimeters of one layer in every
|
||||
stride. The prime tower and supports have other roles and are left out.
|
||||
- `ShellOnly` (`shell`) keeps what the shell extraction below marks as visible surface, of one
|
||||
layer in every stride, plus whatever a view from above or below sees of the skipped layers, so
|
||||
that a step does not vanish. It is the only mode that knows the prime tower's outside from its
|
||||
inside.
|
||||
|
||||
## The solid model
|
||||
|
||||
The preview already loads the sliced objects as shells for its translucent ghost.
|
||||
`GCodeViewer::render_solid_model()` draws those shells opaque, in their filament colors, with the
|
||||
`gouraud` shader, whose z range cuts them to the visible layer range. The two toolpath layers of
|
||||
the reduced set are drawn afterwards and cap the cut with what was really printed there. The
|
||||
shells hold only the objects, so while this mode is on the prime tower is added from its sliced
|
||||
mesh, positioned as the print placed it. It is added or removed on its own when the mode changes,
|
||||
without reloading the objects, keeps its opaque color so that it never appears among the
|
||||
translucent shells, and stays out of their bounding box. Supports have no mesh and are not shown,
|
||||
and `load_shells()` drops every non-model-part volume, so a negative volume is not cut out.
|
||||
|
||||
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
||||
only the end layers.
|
||||
|
||||
## Shell extraction
|
||||
|
||||
`ViewerImpl::update_shell_bitset()` classifies every extrusion segment once per load, on demand
|
||||
the first time the shell mode needs it, and records the result in four bit sets. It is purely
|
||||
geometric so that the wipe tower, whose every segment shares one role, works as well as the
|
||||
objects.
|
||||
|
||||
Each layer is rasterized into a coarse 2D **occupancy grid** over the print's footprint: cells
|
||||
are 0.5 mm, or coarser so that the grid is at most 1024 cells across. The footprint is then
|
||||
**closed** with a radius of 2.5 mm so that sparse infill and support read as the solid area they
|
||||
belong to, while holes wider than 5 mm stay open. The closing dilates each 8-connected component
|
||||
separately and leaves a cell that two components both reach empty, so the gap between two objects
|
||||
standing close together is never bridged and both of their facing walls stay on the shell;
|
||||
fragments under eight cells do not spread and are absorbed by whatever reaches them. A separable
|
||||
erosion shrinks the result back, and the raw cells are OR-ed in again so that a closing never
|
||||
loses one.
|
||||
|
||||
A cell is a **shell cell** when it is filled and any of its six neighbours, four in the layer,
|
||||
one below, one above, is not. A segment is on the shell when at least half of the cells it
|
||||
crosses are shell cells: walls run along the shell, infill only touches it at the ends. The
|
||||
interior infill roles and gap fill are excluded regardless, since short infill segments hugging a
|
||||
wall would otherwise pass by the thousand.
|
||||
|
||||
Two refinements keep sloped surfaces closed:
|
||||
|
||||
- **Near-wall segments.** The step between one layer's outer wall and the next is often
|
||||
narrower than a cell. An inner wall (`Perimeter`) segment whose midpoint lies within a line and
|
||||
a half of an outer or overhang perimeter of the same layer is kept as well. So is any segment,
|
||||
whatever its role, whose midpoint lies within that reach, or a cell if larger, of an outer wall
|
||||
of the layer above or below: that strip is the exposed band of the step, which the cell tests
|
||||
cannot see when it is narrower than a cell.
|
||||
- **Top and bottom visibility.** The same pass records the highest and lowest layer occupying
|
||||
each cell over the whole print. A segment whose layer is the topmost occupant of any cell it
|
||||
crosses is visible from above, and likewise from below with the lowest. These segments are kept
|
||||
even when their layer is skipped by the stride.
|
||||
|
||||
The layer range is split across up to eight `std::async` workers, each owning its grids. An
|
||||
allocation failure on a huge print falls back to marking every segment as shell, which leaves out
|
||||
only the hidden infill roles.
|
||||
|
||||
## Deciding that the user is dragging
|
||||
|
||||
`GLCanvas3D::_update_preview_interaction()` runs at the top of every preview frame, before the
|
||||
canvas decides whether to reuse its cached scene, so that the switch lands in that frame. Dragging
|
||||
is `GLCanvas3D::is_user_interacting()`, the same answer the scene cache reads: the camera, the
|
||||
navigator, a gizmo, the rectangle selection or either slider being held. A slider reports this from
|
||||
ImGui's active id rather than its dirty flag, which is raised and consumed inside one frame. A
|
||||
wheel step has no duration, so it holds the reduced set for a 150 ms settle time instead, and the
|
||||
frame that restores the toolpaths is scheduled for when that time runs out, since the render timer
|
||||
only wakes the idle loop. A drag cut short by focus or capture loss is ended explicitly, and a
|
||||
button release wakes the idle loop, because on some platforms nothing else would until the next
|
||||
input.
|
||||
|
||||
## Reused scene frames
|
||||
|
||||
`GLCanvas3D` keeps its last scene pass for frames that only rebuild the overlay (`SceneCache`). Its
|
||||
key covers the canvas size, the camera and hover state, not what the toolpath sets draw, so a frame
|
||||
that reuses the scene must never be one on which the set is switched.
|
||||
`_update_preview_interaction()` therefore reports whether the bound set changed, and a frame on
|
||||
which it did redraws the scene. The canvas neither captures nor reuses the scene while the user
|
||||
drags, so no reduced frame outlives a drag, and the frame that ends a wheel's settle time is
|
||||
requested as a full frame.
|
||||
@@ -76,10 +76,9 @@ and the count of errors the original parse hit.
|
||||
|
||||
Each entry is one preset **in source form**: what its JSON sub-file states and nothing
|
||||
that resolving it derives — the preset's own config diff, the name of the preset it
|
||||
inherits, the names of the presets it includes, and the parse metadata (name, sub-path,
|
||||
description, instantiation, setting and filament ids, renames). Non-instantiated base
|
||||
presets are stored too; the children that inherit from or include them cannot resolve
|
||||
without them.
|
||||
inherits, and the parse metadata (name, sub-path, description, instantiation, setting
|
||||
and filament ids, renames). Non-instantiated base presets are stored too; the children
|
||||
that inherit from them cannot resolve without them.
|
||||
|
||||
**The payload names its own keys.** The dictionary holds the distinct `opt_key`s the
|
||||
file uses, the `ConfigOptionType` each was written as, and the distinct enum *value
|
||||
@@ -162,9 +161,9 @@ cache nothing can invalidate is worse than no cache.
|
||||
|
||||
Vendors load in a fixed order, because filament inheritance crosses exactly one
|
||||
boundary: any vendor's filament may inherit from the shared Orca filament library,
|
||||
and nothing else reaches across vendors — an `include` is always vendor-local. The
|
||||
library therefore goes first, alone; every other vendor follows in parallel, resolving
|
||||
against it; and the results are merged in a stable order:
|
||||
and nothing else reaches across vendors. The library therefore goes first, alone;
|
||||
every other vendor follows in parallel, resolving against it; and the results are
|
||||
merged in a stable order:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -212,16 +211,13 @@ and its cache was never written back.
|
||||
|
||||
Serving from a cache is not a memory-image restore. The entries are deserialized and
|
||||
then installed one by one — inheritance resolved against the presets installed before
|
||||
them and the currently loaded filament library, includes layered in, configs flattened
|
||||
onto the collection defaults, validated and registered — by the same function the JSON
|
||||
path calls straight after parsing a sub-file. An `include` layers what the included
|
||||
base states, between the parent and the preset's own keys: the base's diff against the
|
||||
default, taken when the base itself was installed and before the per-variant padding
|
||||
`inherits` sees, so only what a template sets reaches the presets including it. The two
|
||||
paths share everything below the parse, which is what makes a cache-loaded bundle
|
||||
indistinguishable from a JSON-loaded one by construction rather than by test coverage.
|
||||
Installation also rebuilds each preset's file path from the local data directory, so a
|
||||
shipped cache never carries the generating machine's paths.
|
||||
them and the currently loaded filament library, configs flattened onto the collection
|
||||
defaults, validated and registered — by the same function the JSON path calls straight
|
||||
after parsing a sub-file. The two paths share everything below the parse, which is what
|
||||
makes a cache-loaded bundle indistinguishable from a JSON-loaded one by construction
|
||||
rather than by test coverage. Installation also rebuilds each preset's file path from
|
||||
the local data directory, so a shipped cache never carries the generating machine's
|
||||
paths.
|
||||
|
||||
App upgrades work because a cache normally survives one. Only a deliberate
|
||||
`CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 18 KiB |
@@ -3,7 +3,7 @@
|
||||
"name": "AliZ PA-CF @P1-X1",
|
||||
"inherits": "AliZ PA-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "cN0uesF0mZf3KSkj",
|
||||
"setting_id": "AliZ003_00",
|
||||
"instantiation": "true",
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "AliZ PETG @P1-X1",
|
||||
"inherits": "AliZ PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "cZp23cC4ong9q6dn",
|
||||
"setting_id": "AliZ001_00",
|
||||
"instantiation": "true",
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "AliZ PETG-CF @P1-X1",
|
||||
"inherits": "AliZ PETG-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "HWW5mejsB0SfEfWw",
|
||||
"setting_id": "AZ01-1_00",
|
||||
"instantiation": "true",
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "AliZ PETG-Metal @P1-X1",
|
||||
"inherits": "AliZ PETG-Metal @base",
|
||||
"from": "system",
|
||||
"setting_id": "JqD0blE4fxaNfXqB",
|
||||
"setting_id": "AZ01-2_00",
|
||||
"instantiation": "true",
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "AliZ PLA @P1-X1",
|
||||
"inherits": "AliZ PLA @base",
|
||||
"from": "system",
|
||||
"setting_id": "fRELapY8YiP4qrBW",
|
||||
"setting_id": "AliZ002_00",
|
||||
"instantiation": "true",
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ABS @BBL A1",
|
||||
"inherits": "BETA ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "dUGRrmxU89pRaJ2Z",
|
||||
"setting_id": "BABB00_07",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"20"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ABS @BBL H2D",
|
||||
"inherits": "BETA ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "oxZKTEMd8TpyoH39",
|
||||
"setting_id": "BABB00_11",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"65"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ABS @BBL P1P",
|
||||
"inherits": "BETA ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "MPYyfCKMsJZy8GaY",
|
||||
"setting_id": "BABB00_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"20"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ABS @BBL X1C",
|
||||
"inherits": "BETA ABS @base",
|
||||
"from": "system",
|
||||
"setting_id": "vMA7EhgNDT4MwMRr",
|
||||
"setting_id": "BABB00",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"60"
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "BETA ABS @base",
|
||||
"inherits": "fdm_filament_abs",
|
||||
"from": "system",
|
||||
"filament_id": "OFjUXn67",
|
||||
"instantiation": "false",
|
||||
"description": "When printing this filament, there's a risk of warping and low layer adhesion strength. To get better results, please refer to this wiki: Printing Tips for High Temp / Engineering materials.",
|
||||
"filament_cost": [
|
||||
"24.99"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"BETA"
|
||||
],
|
||||
"impact_strength_z": [
|
||||
"7.4"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ASA @BBL A1 0.4 nozzle",
|
||||
"inherits": "BETA ASA @base",
|
||||
"from": "system",
|
||||
"setting_id": "LevRG7t9mTlEseaB",
|
||||
"setting_id": "BASB00_09",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"60"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ASA @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA ASA @base",
|
||||
"from": "system",
|
||||
"setting_id": "3NBkWTXmqi8eRUhi",
|
||||
"setting_id": "BASB00_13",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"65"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA ASA @BBL X1C 0.4 nozzle",
|
||||
"inherits": "BETA ASA @base",
|
||||
"from": "system",
|
||||
"setting_id": "0nWTEZkH7NKXgCib",
|
||||
"setting_id": "BASB00_02",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"60"
|
||||
@@ -53,53 +53,5 @@
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"100"
|
||||
],
|
||||
"activate_air_filtration": [
|
||||
"1"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"100"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Filament gcode\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"200"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"8.0",
|
||||
"12",
|
||||
"8.0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"100"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"85"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"80",
|
||||
"65",
|
||||
"80"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"85"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA HIPS @BBL H2D",
|
||||
"inherits": "BETA HIPS @base",
|
||||
"from": "system",
|
||||
"setting_id": "9nN2uWxlYH1c507A",
|
||||
"setting_id": "BHIB00_06",
|
||||
"instantiation": "true",
|
||||
"filament_deretraction_speed": [
|
||||
"nil",
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA HIPS @BBL X1C",
|
||||
"inherits": "BETA HIPS @base",
|
||||
"from": "system",
|
||||
"setting_id": "mTcT8sCciWyX5enc",
|
||||
"setting_id": "BHIB00_00",
|
||||
"instantiation": "true",
|
||||
"filament_max_volumetric_speed": [
|
||||
"8",
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "BETA HIPS @base",
|
||||
"inherits": "fdm_filament_hips",
|
||||
"from": "system",
|
||||
"filament_id": "OFlmxlDG",
|
||||
"instantiation": "false",
|
||||
"filament_is_support": [
|
||||
"1"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"BETA"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PAHT-CF @BBL H2D",
|
||||
"inherits": "BETA PAHT-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "YbYCVQd5IR0sQR8S",
|
||||
"setting_id": "BPAB00_01",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"60"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PAHT-CF @BBL P1P",
|
||||
"inherits": "BETA PAHT-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "5O5fzmFrT3O1k1H0",
|
||||
"setting_id": "BPAB00_10",
|
||||
"instantiation": "true",
|
||||
"filament_flow_ratio": [
|
||||
"0.96",
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PAHT-CF @BBL X1C",
|
||||
"inherits": "BETA PAHT-CF @base",
|
||||
"from": "system",
|
||||
"setting_id": "gB5HLYTDCooFy2W5",
|
||||
"setting_id": "BPAB00",
|
||||
"instantiation": "true",
|
||||
"chamber_temperatures": [
|
||||
"60"
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "BETA PAHT-CF @base",
|
||||
"inherits": "fdm_filament_pa",
|
||||
"from": "system",
|
||||
"filament_id": "OF6qw3Wb",
|
||||
"instantiation": "false",
|
||||
"description": "When printing this filament, there's a risk of nozzle clogging, oozing, warping and low layer adhesion strength. To get better results, please refer to this wiki: Printing Tips for High Temp / Engineering materials.",
|
||||
"fan_cooling_layer_time": [
|
||||
"5"
|
||||
],
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"10"
|
||||
],
|
||||
"filament_cost": [
|
||||
"94.99"
|
||||
],
|
||||
"filament_density": [
|
||||
"1.06"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.96"
|
||||
],
|
||||
"filament_type": [
|
||||
"PA-CF"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"BETA"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"2"
|
||||
],
|
||||
"impact_strength_z": [
|
||||
"13.3"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"40"
|
||||
],
|
||||
"overhang_fan_threshold": [
|
||||
"0%"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"180"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PEBA 90A @BBL A1",
|
||||
"inherits": "BETA PEBA 90A @base",
|
||||
"from": "system",
|
||||
"setting_id": "A57Sbar56Ah1NiU7",
|
||||
"setting_id": "BPBB00_04",
|
||||
"instantiation": "true",
|
||||
"filament_density": [
|
||||
"1.22"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PEBA 90A @BBL A1M",
|
||||
"inherits": "BETA PEBA 90A @base",
|
||||
"from": "system",
|
||||
"setting_id": "OoLkWS147R0AasZc",
|
||||
"setting_id": "BPBB00_05",
|
||||
"instantiation": "true",
|
||||
"filament_density": [
|
||||
"1.22"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PEBA 90A @BBL H2D",
|
||||
"inherits": "BETA PEBA 90A @base",
|
||||
"from": "system",
|
||||
"setting_id": "91b8WTzvOky9wAVK",
|
||||
"setting_id": "BPBB00_02",
|
||||
"instantiation": "true",
|
||||
"additional_cooling_fan_speed": [
|
||||
"100"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PEBA 90A @BBL P1P",
|
||||
"inherits": "BETA PEBA 90A @base",
|
||||
"from": "system",
|
||||
"setting_id": "ydudEBLRmj1WSXqB",
|
||||
"setting_id": "BPBB00_01",
|
||||
"instantiation": "true",
|
||||
"filament_max_volumetric_speed": [
|
||||
"2.8",
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PEBA 90A @BBL X1C",
|
||||
"inherits": "BETA PEBA 90A @base",
|
||||
"from": "system",
|
||||
"setting_id": "F1hTal3M92tBjv2N",
|
||||
"setting_id": "BPBB00_00",
|
||||
"instantiation": "true",
|
||||
"filament_max_volumetric_speed": [
|
||||
"2.8",
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "BETA PEBA 90A @base",
|
||||
"inherits": "fdm_filament_tpu",
|
||||
"from": "system",
|
||||
"filament_id": "OFI2MKy7",
|
||||
"instantiation": "false",
|
||||
"description": "This filament is too soft and not compatible with the AMS. Printing it is of many requirements, and to get better printing quality, please refer to this wiki: TPU printing guide.",
|
||||
"filament_cost": [
|
||||
"59.99"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2.8"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"BETA"
|
||||
],
|
||||
"impact_strength_z": [
|
||||
"87.3"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"225"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"225"
|
||||
],
|
||||
"nozzle_temperature_range_high": [
|
||||
"240"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"14"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG @BBL A1",
|
||||
"inherits": "BETA PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "EpYzxZwVHvLaseku",
|
||||
"setting_id": "BPGB00_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "5V0hyhzFoDU8Tfwm",
|
||||
"setting_id": "BPGB00_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "KnKvUsD4rSpQU413",
|
||||
"setting_id": "BPGB00_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG @BBL X1C",
|
||||
"inherits": "BETA PETG @base",
|
||||
"from": "system",
|
||||
"setting_id": "uM6wHdt0N5ftZRge",
|
||||
"setting_id": "BPGB00_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Fluorescence @BBL A1",
|
||||
"inherits": "BETA PETG Fluorescence @base",
|
||||
"from": "system",
|
||||
"setting_id": "ZtyTtAWsZnLGsM0O",
|
||||
"setting_id": "BPGB02_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Fluorescence @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Fluorescence @base",
|
||||
"from": "system",
|
||||
"setting_id": "VUSunTT0OJa7iOSd",
|
||||
"setting_id": "BPGB02_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Fluorescence @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Fluorescence @base",
|
||||
"from": "system",
|
||||
"setting_id": "JHg4y0pxAOtoJSuw",
|
||||
"setting_id": "BPGB02_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Fluorescence @BBL X1C",
|
||||
"inherits": "BETA PETG Fluorescence @base",
|
||||
"from": "system",
|
||||
"setting_id": "pCetfFvyLLw3vS2j",
|
||||
"setting_id": "BPGB02_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glitter @BBL A1",
|
||||
"inherits": "BETA PETG Glitter @base",
|
||||
"from": "system",
|
||||
"setting_id": "cz5dC7OrXDlX1Ctj",
|
||||
"setting_id": "BPGB03_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glitter @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Glitter @base",
|
||||
"from": "system",
|
||||
"setting_id": "PozWnOfZ2l7HqTpx",
|
||||
"setting_id": "BPGB03_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glitter @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Glitter @base",
|
||||
"from": "system",
|
||||
"setting_id": "ijW4zvNAQ3DIK984",
|
||||
"setting_id": "BPGB03_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glitter @BBL X1C",
|
||||
"inherits": "BETA PETG Glitter @base",
|
||||
"from": "system",
|
||||
"setting_id": "UI220qldPoLY8VZN",
|
||||
"setting_id": "BPGB03_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glow @BBL A1",
|
||||
"inherits": "BETA PETG Glow @base",
|
||||
"from": "system",
|
||||
"setting_id": "LudMQeiSJOLw0Aoy",
|
||||
"setting_id": "BPGB04_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glow @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Glow @base",
|
||||
"from": "system",
|
||||
"setting_id": "cl1Rl9hgrahhhSd3",
|
||||
"setting_id": "BPGB04_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glow @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Glow @base",
|
||||
"from": "system",
|
||||
"setting_id": "3s9w3mH6M4OdbrUV",
|
||||
"setting_id": "BPGB04_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Glow @BBL X1C",
|
||||
"inherits": "BETA PETG Glow @base",
|
||||
"from": "system",
|
||||
"setting_id": "wv5FRKdnsHaIFA7L",
|
||||
"setting_id": "BPGB04_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Gradient @BBL A1",
|
||||
"inherits": "BETA PETG Gradient @base",
|
||||
"from": "system",
|
||||
"setting_id": "L3hZggzEZUgUv0Qv",
|
||||
"setting_id": "BPGB05_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Gradient @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Gradient @base",
|
||||
"from": "system",
|
||||
"setting_id": "zsITnsNFRG7IE6sR",
|
||||
"setting_id": "BPGB05_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Gradient @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Gradient @base",
|
||||
"from": "system",
|
||||
"setting_id": "GqmCuPg6baUxeoYS",
|
||||
"setting_id": "BPGB05_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Gradient @BBL X1C",
|
||||
"inherits": "BETA PETG Gradient @base",
|
||||
"from": "system",
|
||||
"setting_id": "oYJjUiaCI4UBZ0ur",
|
||||
"setting_id": "BPGB05_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG HF @BBL A1",
|
||||
"inherits": "BETA PETG HF @base",
|
||||
"from": "system",
|
||||
"setting_id": "T2Ea25pM2u9BM7Oa",
|
||||
"setting_id": "BPGB06_03",
|
||||
"instantiation": "true",
|
||||
"fan_cooling_layer_time": [
|
||||
"15"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG HF @BBL A1M",
|
||||
"inherits": "BETA PETG HF @base",
|
||||
"from": "system",
|
||||
"setting_id": "nO5R86dkDle1m5Pq",
|
||||
"setting_id": "BPGB06_06",
|
||||
"instantiation": "true",
|
||||
"fan_cooling_layer_time": [
|
||||
"15"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG HF @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG HF @base",
|
||||
"from": "system",
|
||||
"setting_id": "YOBguayM9PiwkpfT",
|
||||
"setting_id": "BPGB06_09",
|
||||
"instantiation": "true",
|
||||
"counter_coef_2": [
|
||||
"0.007"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG HF @BBL X1C",
|
||||
"inherits": "BETA PETG HF @base",
|
||||
"from": "system",
|
||||
"setting_id": "EG3h0IHVO1yyVNMs",
|
||||
"setting_id": "BPGB06_00",
|
||||
"instantiation": "true",
|
||||
"fan_cooling_layer_time": [
|
||||
"20"
|
||||
@@ -85,48 +85,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Heat Color Change @BBL A1",
|
||||
"inherits": "BETA PETG Heat Color Change @base",
|
||||
"from": "system",
|
||||
"setting_id": "ZhHASdXFywqbRuBf",
|
||||
"setting_id": "BPGB01_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Heat Color Change @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Heat Color Change @base",
|
||||
"from": "system",
|
||||
"setting_id": "YiFgzCJDAOW9YSjZ",
|
||||
"setting_id": "BPGB01_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Heat Color Change @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Heat Color Change @base",
|
||||
"from": "system",
|
||||
"setting_id": "DpWgpTiKdm70Xt1i",
|
||||
"setting_id": "BPGB01_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Heat Color Change @BBL X1C",
|
||||
"inherits": "BETA PETG Heat Color Change @base",
|
||||
"from": "system",
|
||||
"setting_id": "3lJe6ftSHQDkQZ1S",
|
||||
"setting_id": "BPGB01_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Marble @BBL A1",
|
||||
"inherits": "BETA PETG Marble @base",
|
||||
"from": "system",
|
||||
"setting_id": "tSCDuE4dE44PyIwb",
|
||||
"setting_id": "BPGB07_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Marble @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Marble @base",
|
||||
"from": "system",
|
||||
"setting_id": "n6dguOo5ZleD6Ec4",
|
||||
"setting_id": "BPGB07_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Marble @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Marble @base",
|
||||
"from": "system",
|
||||
"setting_id": "RxCTxdvym4iNOB5j",
|
||||
"setting_id": "BPGB07_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Marble @BBL X1C",
|
||||
"inherits": "BETA PETG Marble @base",
|
||||
"from": "system",
|
||||
"setting_id": "3BgsPAgilo1MgtOV",
|
||||
"setting_id": "BPGB07_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Matte @BBL A1",
|
||||
"inherits": "BETA PETG Matte @base",
|
||||
"from": "system",
|
||||
"setting_id": "zYkrzQk11C98R31F",
|
||||
"setting_id": "BPGB08_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Matte @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Matte @base",
|
||||
"from": "system",
|
||||
"setting_id": "rzX5oP4037Ex5Chh",
|
||||
"setting_id": "BPGB08_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Matte @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Matte @base",
|
||||
"from": "system",
|
||||
"setting_id": "sqz8Vluq7GMd3siv",
|
||||
"setting_id": "BPGB08_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Matte @BBL X1C",
|
||||
"inherits": "BETA PETG Matte @base",
|
||||
"from": "system",
|
||||
"setting_id": "NXgqYCqHeVCgFF3I",
|
||||
"setting_id": "BPGB08_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Metallic @BBL A1",
|
||||
"inherits": "BETA PETG Metallic @base",
|
||||
"from": "system",
|
||||
"setting_id": "1baUIJ3P8n6DcyRc",
|
||||
"setting_id": "BPGB09_06",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Metallic @BBL A1M 0.4 nozzle",
|
||||
"inherits": "BETA PETG Metallic @base",
|
||||
"from": "system",
|
||||
"setting_id": "LMsVDqsChBksLNqL",
|
||||
"setting_id": "BPGB09_03",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"50"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Metallic @BBL H2D 0.4 nozzle",
|
||||
"inherits": "BETA PETG Metallic @base",
|
||||
"from": "system",
|
||||
"setting_id": "giq8BW6qcwz91e4C",
|
||||
"setting_id": "BPGB09_09",
|
||||
"instantiation": "true",
|
||||
"fan_max_speed": [
|
||||
"30"
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "BETA PETG Metallic @BBL X1C",
|
||||
"inherits": "BETA PETG Metallic @base",
|
||||
"from": "system",
|
||||
"setting_id": "tZzoc2LdgWkpEiox",
|
||||
"setting_id": "BPGB09_01",
|
||||
"instantiation": "true",
|
||||
"fan_min_speed": [
|
||||
"20"
|
||||
@@ -92,48 +92,5 @@
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; filament start gcode\n{if (bed_temperature[current_extruder] >80)||(bed_temperature_initial_layer[current_extruder] >80)}M106 P3 S255\n{elsif (bed_temperature[current_extruder] >60)||(bed_temperature_initial_layer[current_extruder] >60)}M106 P3 S180\n{endif}\n\n{if activate_air_filtration[current_extruder] && support_air_filtration}\nM106 P3 S{during_print_exhaust_fan_speed_num[current_extruder]} \n{endif}"
|
||||
],
|
||||
"close_additional_fan_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"retraction_distances_when_ec": [
|
||||
"0"
|
||||
],
|
||||
"filament_end_gcode": [
|
||||
"; filament end gcode \n\n"
|
||||
],
|
||||
"volumetric_speed_coefficients": [
|
||||
"0 0 0 0 0 0"
|
||||
],
|
||||
"filament_adhesiveness_category": [
|
||||
"300"
|
||||
],
|
||||
"filament_dev_chamber_drying_bed_temperature": [
|
||||
"80"
|
||||
],
|
||||
"filament_dev_ams_drying_time": [
|
||||
"12",
|
||||
"12",
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"filament_dev_ams_drying_ams_limitations": [
|
||||
"1",
|
||||
"0"
|
||||
],
|
||||
"filament_dev_ams_drying_heat_distortion_temperature": [
|
||||
"75"
|
||||
],
|
||||
"filament_dev_drying_cooling_temperature": [
|
||||
"55"
|
||||
],
|
||||
"filament_dev_ams_drying_temperature": [
|
||||
"65",
|
||||
"65",
|
||||
"55",
|
||||
"55"
|
||||
],
|
||||
"filament_dev_drying_softening_temperature": [
|
||||
"60"
|
||||
]
|
||||
}
|
||||