Compare commits

..
Author SHA1 Message Date
SoftFever b205a8559e Merge branch 'main' into feat/snap-store-packaging 2026-06-20 13:16:01 +08:00
Alexandre Folle de Menezes 0335d76d30 Update and complement ptBR translation (#14302) 2026-06-20 13:15:28 +08:00
Mykola Nahirnyi 3e56d25f64 Allow presets without parent 2026-06-20 13:14:46 +08:00
Tobias Gloth 4c149b69eb can build boost, draco, opencv as debug with msvc (#13921)
* can build boost, draco, opencv as debug with msvc

* only forward build configuration for MSVC and in Debug mode
2026-06-20 12:39:08 +08:00
Terasit Juntarasombut 57cb60e20f l10n: Update Thai (th) localization after gettext refactor (#14288) 2026-06-19 20:40:09 -03:00
Surfoo 8ffe84b1dd i18n(fr): improve French localization quality and consistency. (#14293) 2026-06-19 17:09:39 -03:00
Ian BassiandWegerich f4268a0eec Adaptive Pressure Advance Validation (#14198)
Co-authored-by: Wegerich <23041237+Wegerich@users.noreply.github.com>
2026-06-19 15:46:00 -03:00
SoftFever 17e2adc283 CI(macOS): retry flaky hdiutil DMG creation; delete per-arch bundles only on success 2026-06-20 01:15:31 +08:00
Ian Bassi d87f7e462c Localization fixes (#14291)
* Restore text

* Restore english fuzzys

* Gettext

* Fuzzy for comment

* Gettext
2026-06-19 11:54:53 -03:00
SoftFever 617df79260 Add Snap Store packaging
Publish OrcaSlicer to the Snap Store (classic confinement) on channels
matching the release tiers: stable / candidate / beta / edge. The Linux
build (both arches) repackages the AppImage AppDir into a snap; nightly
builds go to edge, and tagged releases publish to the matching channel.

Stacked on the Linux ARM64 AppImage change.
2026-06-19 16:01:10 +08:00
SoftFever 0a6a42ecb5 Add Linux ARM64 (aarch64) AppImage build
Build the Linux AppImage for ARM64 (aarch64) alongside x86_64: the Linux CI
job now matrixes over both architectures, with arch-aware deps caching and
artifact/asset names (amd64 keeps its existing names). The aarch64 AppImage is
published to the nightly and release pages like the x86_64 one.

Run the unit-test suite on the aarch64 runner (faster GitHub arm runner); the
tests are built on that leg. Self-hosted keeps tests on the amd64 server.
2026-06-19 15:57:20 +08:00
Kiss LorandandRodrigo Faselli 762e474433 Skirt overhaul (#14130)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-06-19 10:43:46 +08:00
Rodrigo Faselli 115d6dde46 ENABLE_SMOOTH_NORMALS (#14080)
* ENABLE_SMOOTH_NORMALS

* Remove definition of macro L if defined

* Update GLModel.cpp

* suavizado ajustado en 5 grados

5 grados

3,5 grados

* Ajuste de brillo menos intenso

* opcion smooth normals

Update GLModel.cpp

test

test 3

* cleaning macros

* tooltip

* Apply suggestion from @RF47

* Apply suggestion from @RF47

* Apply suggestions from code review

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-06-19 09:42:12 +08:00
foXaCe 9da2975424 i18n(fr): complete French catalog after the localization refactor (#14277) 2026-06-18 16:19:24 -03:00
raistlin7447 7b3228d10d Cover the libnest2d nesting engine and fix an NfpPlacer crash (#14267)
* fix(libnest2d): skip the excluded-region alignment pass when there are none

NfpPlacer::finalAlign(), run from clearItems() and the destructor, always
ran the "find a best position inside the NFP of fixed items" pass even when
no items are fixed. With nothing to avoid, calcnfp() computes the inner-fit
NFP of the pile and can feed clipper a coordinate outside its allowed range.
On Linux/clang the value stays in range so it went unnoticed; on MSVC the
clipper "Coordinate outside allowed range" exception escapes the noexcept
destructor and aborts the process (exit 0xC0000409).

Build the excluded set up front and only run the pass when it is non-empty.
The block exists solely to keep the pile clear of fixed items (excluded
regions / wipe tower), so it is a no-op when there are none and the
wipe-tower behaviour is unchanged.

* test(libnest2d): remove dead nesting tests and split the suite by feature

Seven of the suite's hidden [.] test cases drove code paths Orca abandoned
at the BambuStudio fork: BottomLeftPlacer (used nowhere in src/) and the
stock default NfpPlacer backend, which returns zero bins in Orca. They have
been red since the fork and are never registered with ctest. Remove them.

Split the 1,000-line libnest2d_tests_main.cpp into per-feature files, per the
repo convention, sharing a header for the no-fit-polygon backend setup that
every translation unit must agree on (ODR):

  libnest2d_tests.cpp       Item and nest() basics
  test_geometry.cpp         geometry primitives
  test_nfp.cpp              no-fit-polygon machinery
  libnest2d_test_utils.hpp  shared includes and the NFP backend specialisation

Along the way: drop a debug exportSVG() helper that only wrote a file on test
failure (so the suite never leaves stray assets), convert the deprecated
Catch::Approx to WithinRel/WithinAbs matchers, and give the tests descriptive
names.

* test(libnest2d): add NfpPlacer unit tests

NfpPlacer is the placement engine the arranger drives, but the suite only
covered the geometry primitives. Add a fixture and five tests that exercise
pack()/accept() directly: a single item lands in the bin, an oversized item
is rejected, the first item is seeded for every starting point, many items
pack without overlap, and the rotation candidates are searched. This lifts
nfpplacer.hpp line coverage from 42% to 87% in the libnest2d suite.

* test(libslic3r): add arrangement::arrange() integration coverage

The libnest2d suite cannot reach Orca's real nesting entry point because it
does not link libslic3r. Add test_arrange.cpp driving arrangement::arrange():
items land on the bed and within bounds, do not overlap, are spaced by their
inflation, an oversized item stays unplaced, overflow spills onto virtual beds,
an empty input is a no-op, and the DONT_ALIGN and USER_DEFINED final-alignment
paths are exercised. A self-test guards the overlap check the other cases use.
2026-06-18 23:40:37 +08:00
Ian Bassi 2de58e557b Lozalization Update after refactor (#14272) 2026-06-18 09:22:31 -03:00
Ian Bassi 514ab02525 Localizations refactor (#14254) 2026-06-18 09:13:51 -03:00
Wegerich d7688a27d0 APA for overhangs - Prusa incompatibility warning (#14271)
* APA for overhangs - Prusa incompatibility warning

Added a sentence explicitly stating that APA for overhangs is not compatible with prusa printers

* whitespace
2026-06-18 11:46:51 +01:00
Wegerich c2965a1336 Clarify that "network plugin" now means *bambu* only (#14265)
Clarify that "network plugin" now means *bambu* only and doesn't refer to orca cloud

fully differentiate the two offerings to avoid confusion especially for non Bambu users
2026-06-18 15:10:56 +08:00
raistlin7447 a587859e84 Fix: show all print validation warnings instead of only the last (#14112) 2026-06-18 12:45:26 +08:00
SoftFever 3ffb9585d2 flatpak: OrcaSlicer was trying to migrate old configs from an io.github.orcaslicer.OrcaSlicer folder — a bundle ID used only in nightly builds between the 2.3.1 and 2.3.2 releases.
The correct old config folder, widely used in pre-2.3.2 releases, is io.github.softfever.OrcaSlicer.
2026-06-18 00:40:22 +08:00
Alexandre Folle de Menezes af854f3242 Improve and complement the pt-BR translation (#14252) 2026-06-17 23:57:11 +08:00
Noisyfox 9b60b9cd5d Fix wrong variant index is used on motion ability setting tab (#14253)
Fix wrong variant index is used on motion ability setting tab (OrcaSlicer/OrcaSlicer#13308)
2026-06-17 23:54:27 +08:00
SoftFever 927c5efb8c add Chinese translations 2026-06-17 20:53:01 +08:00
SoftFever 81951fddb5 Fix locale errors 2026-06-17 19:10:29 +08:00
Alexandre Folle de MenezesandSoftFever 4535f19501 Misc fixes to GUI strings (#14047)
* Degrees symbol don't need localization

* The Z when referring to the axis should be uppercase

* Fix the spelling of "GitHub" to camelcase

* Unify the casing of mouse button shortcuts

* Always use G-code with an hyphen

* Fix the spelling of "restricted"

* More grammar fixes

* add missing modifications

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-06-17 17:55:32 +08:00
Jakub HenclandSoftFever ee9a796f14 Improve Czech localization translations (#13666)
* Improve Czech localization translations

Updated and improved Czech translations for OrcaSlicer UI strings.

- Fixed untranslated entries
- Improved terminology consistency
- Adjusted wording to better match PrusaSlicer style
- Fixed plural forms and formatting placeholders
- Remaining untranslated strings will be completed later

* fix errors

* reformat

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-06-17 17:45:16 +08:00
Heiko Liebscher 3bce4bb197 feat: update German localization for various features and error messages (#14201) 2026-06-17 17:18:08 +08:00
NoisyfoxandSoftFever dcee299909 Allow use offline when logged in to Orca Cloud (#14235)
* Store user session information along with refresh token, to allow offline use once user is logged in

* Don't bother with avatar because we won't see it when offline anyway

* Fix offline Sync Presets freezing the UI on repeat clicks

Ignore restart_sync_user_preset() while a manual sync's progress dialog is on screen, so a second app-modal dialog can't stack on the first. Offline the dialog blocks on a long, uncancellable HTTP timeout; on macOS the global menu stays live while the window is disabled, so a second click otherwise wedges the app (force-quit only).

* Skip redundant user-secret re-write on startup

set_user_session() always re-encrypts and writes the secret to disk; on the startup restore path that just rewrites the bytes it was loaded from. Add a persist flag so the restore path skips it. Also drop an unused catch binding and a stray blank line.

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-06-17 17:15:09 +08:00
SoftFever 9eeb73b68b feat: Support 3MF as g-code (use_3mf option) (#14238)
* feat: add support for 3MF file format in printer configurations and export options

* fix file extension

* enable 3mf for X Max 4

* disable use_3mf for X Plus 4

* Fixed an issue where `label_object_enabled` was not properly propagated to 3mf

* enable exclude object for Max 4

* remove hardcoded use 3mf for flashforge, move them to the new printer profiles config
2026-06-17 15:39:40 +08:00
hamham999 7ab3174f7c Fixing the volumetric speed setting for some Creality filaments (#14237) 2026-06-16 18:16:01 -03:00
Vovodroid 5a6e31ac5b Allow drop of sunken objects (#14243) 2026-06-16 18:13:10 -03:00
Kiss Lorand e700113b39 Fix non-organic tree support base clearance above object surfaces (#14128) 2026-06-16 15:50:09 -03:00
Grant Harknessandgrant0013 5ed8f5ef25 perf(GCodeProcessor): stop recompiling std::regex on every g-code line (up to 2.9x faster slicing) (#14166)
perf(GCodeProcessor): stop recompiling std::regex on every g-code line

process_SET_VELOCITY_LIMIT() constructed three std::regex objects from
scratch on every call, and Klipper-flavor g-code contains
SET_VELOCITY_LIMIT on a large share of lines (8,834 of 103,549 lines for
a single 3DBenchy sliced for a Creality K2). perf attributes 6.4% of the
whole slicing run to this one function, almost all of it regex
compilation and the allocator traffic it generates.
process_SET_PRESSURE_ADVANCE() and the External_Purge_Tag handler had
the same per-call construction.

Hoist all five patterns to function-local static const std::regex so
they compile once. Generated g-code is byte-identical (modulo the
timestamp header); slicing a 16x Benchy plate for a K2 drops from
78.5s to 27.3s wall (2.9x) on a 16-core Linux box, single Benchy from
8.9s to 5.6s.

Co-authored-by: grant0013 <grant@harktech.co.uk>
2026-06-16 23:51:27 +08:00
raistlin7447 454335dba6 Correct tests/CLAUDE.md: testing framework is Catch2 v3, not v2 (#14231)
The testing guide stated OrcaSlicer uses Catch2 v2 and advised the v2
`<catch2/catch.hpp>` include, but the vendored framework is v3.11.0
(tests/catch2/) and every test file includes `<catch2/catch_all.hpp>`.

The wrong version drove several incorrect claims: that SKIP() is
unavailable (it is, v3.3.0+), that the string matcher is "Contains"
rather than "ContainsSubstring", and that thread-safe assertions,
multiple reporters, STATIC_CHECK and built-in sharding do not exist.

Correct all version statements, the example include, and the
former "Version-Specific Limitations" section to reflect v3.11.0.
2026-06-16 23:00:30 +08:00
Allyn Malventano b0c1887f40 fix: persist user-selected preview view mode after first load (#13625)
fix: apply smart preview defaults per extruder count session

- Track last extruder count (1=single, 2+=multi) instead of boolean flag
- Apply appropriate default (ColorPrint/FeatureType) when count changes
- User selections persist within same extruder count
- Symmetric behavior: both single and multi actively apply defaults
- Delete duplicate dead code block (uncommented TODO scaffolding)

Behavior:
- First slice (any type) → appropriate default
- User changes view → persists on re-slice
- Switch single→single or multi→multi → persists
- Switch single↔multi → appropriate default applies
2026-06-16 22:15:00 +08:00
raistlin7447 e6f917d7c5 Re-enable [OrcaCloudServiceAgent] headless tests now that the crash is fixed (#14236)
Re-enable [OrcaCloudServiceAgent] tests now that the headless crash is fixed

The two OrcaCloudServiceAgent display-name tests were tagged [NotWorking]
in #14175 because the agent constructor dereferenced a null wxTheApp when
run headless (no wxApp is created in the unit-test binary), crashing before
any assertion ran. That null dereference was fixed in 14d2dfdd4c, which
guards wxTheApp in compute_fallback_path() and skips file persistence when
no fallback path is available.

With the fix in place both tests build and pass headless, so drop the
[NotWorking] tag and the stale explanatory comments. Verified on Linux
clang-18 (the CI compiler), headless: 20 assertions in 2 test cases pass.

Closes #14193
2026-06-16 22:14:13 +08:00
SoftFever 14d2dfdd4c Fix test errors by guarding against null dereference of wxTheApp in OrcaCloudServiceAgent::compute_fallback_path() and skipping file persistence when no fallback path is available. 2026-06-15 14:29:30 +08:00
raistlin7447 b1e510fc99 Disable fff_print tests that fail only in CI (#14207)
* Disable fff_print tests that fail only in CI

Skirt height is honored, Scenario: Skirt and brim generation, and
Scenario: PrintGCode basic functionality slice geometry that makes clipper's
coordinate range check throw "Coordinate outside allowed range" in the Linux
CI environment, while the same tests pass in local builds. Tag them
[NotWorking] so the Unit Tests job (ctest -LE NotWorking) excludes them until
the underlying slicing issue is fixed in a follow-up PR.

* Trigger Build all workflow on tests/** changes

The push and pull_request path filters did not include tests/**, so a
test-only change never started the build and the Unit Tests job never ran.
Add tests/** to both filters so changes to the test suite are built and
exercised by CI.
2026-06-15 08:53:45 +08:00
yw4z 8c0b89bc8a Launch troubleshoot center with clicking to dev button (#14178)
* init

* show tooltip for dev mode
2026-06-14 18:55:40 +08:00
Mikhail f. Shiryaev 3e2373c86e Improve build cache (#14014)
* Improve build cache: use it in deps, add sccache

* Add a way to to set `ccache` or `sccache` using CMAKE_CCACHE
2026-06-14 18:35:56 +08:00
Ian Chua 2abfa512ef feat: notification where there is an update available for subscribed presets (#14121)
* feat: notification where there is an update available for subscribed presets

* add tracking set for updates available
2026-06-14 18:30:42 +08:00
raistlin7447 fe0eafc02b Fix Unit Tests CI job that silently ran zero tests (#14175)
Fix Unit Tests CI job silently running zero tests

scripts/run_unit_tests.sh selected tests with `ctest -L "Http|PlaceholderParser"`,
but catch_discover_tests() was called without ADD_TAGS_AS_LABELS, so Catch2 tags
were never registered as CTest labels. The -L filter matched nothing and the job
passed green while running no tests ("No tests were found!!!"). Tests have not run
in CI since PR #11485 added that -L line (2025-12-23).

Register tags as labels via a shared orcaslicer_discover_tests() wrapper in
tests/CMakeLists.txt (passing ADD_TAGS_AS_LABELS), routed through all five test
suites. Restore full-suite execution by replacing the narrow -L selection with a
`-LE NotWorking` exclusion, so all reliable tests gate PRs again (the suite ran in
full before #11485).

Tag the two OrcaCloudServiceAgent display-name tests [NotWorking]: their
constructor reaches wxStandardPaths::Get().GetUserDataDir(), which dereferences
the null wxTheApp in the headless test binary and segfaults on every platform.
Excluded until the agent can be constructed without the wx app context.

CI now runs 151 tests (was 0) and passes.
2026-06-14 17:44:20 +08:00
raistlin7447 5fafbb59fc Revive the disabled fff_print test suite (#14196)
* Fix null-deref and arranger bugs that gate headless slicing tests

export_gcode dereferenced a null result out-param, enum serialization
dereferenced a null keys_map, and get_arrange_polys left bed_idx unseeded so
the arranger dropped items. All only affect the headless test/CLI path.

* Fix the headless test harness and add G-code test helpers

Use the real arranger, fix temp-file handling with an RAII guard, and add
layers_with_role / max_z for inspecting sliced G-code.

* Re-enable the Model construction test

* Re-enable SupportMaterial tests and add an enforced-support test

* Re-enable and extend PrintObject layer-height and perimeter tests

* Re-enable Print skirt, brim, and solid-surface tests

* Re-enable and extend PrintGCode tests

Un-hide the basic scenario (dead-key fixes, reframes, trimmed trivia) and add
initial-layer-height, sequential-order, and null-result export tests.

* Re-enable and reframe the skirt/brim tests

Detect skirt/brim by G-code role comment instead of a sentinel speed, and
resolve the previously-unfinished skirt-enclosure test.

* Replace the stale lift()/unlift() test with a z_hop test

* Delete the stub and broken Flow tests
2026-06-14 17:42:53 +08:00
Myself abb4eddb9c Fix CrealityPrint uploads with non-ASCII filenames (#13833)
Fix Creality upload URLs for encoded filenames
2026-06-14 16:23:52 +08:00
yw4z 9bcee518f8 Filament list improvements (Filament counter & Auto unfold & Scroll to end of list on changes) (#14158)
* Update Plater.cpp

* filament counter
2026-06-14 12:04:34 +08:00
Michel (Mike) a.k.a VE2EXBandClaude Opus 4.8 0c4c04bcaf Fix Arch Linux dependency installation (gstreamermm, webkit2gtk, -Syy) (#14171)
Fix Arch Linux dependency installation

The arch dependency script listed packages that are no longer available
in current Arch/CachyOS repositories:

- gstreamermm: removed from official repos (AUR only) and not referenced
  anywhere in the OrcaSlicer build; the build uses plain gstreamer.
- webkit2gtk: replaced upstream by webkit2gtk-4.1.

Also switch the install command from `pacman -Syy` to `pacman -Syu` to
avoid the partial-upgrade pattern that Arch officially discourages on a
rolling-release distro.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 01:24:43 +08:00
yw4z 5f8b48473a Show printer name on titlebar of printer section while its folded (#14148) 2026-06-13 20:45:42 +08:00
Ian Bassi 6bb7903b97 Spanish update + Desc update (#14165) 2026-06-11 22:07:05 -03:00
foXaCe d641ca598a i18n(fr): fix untranslated UI strings, extract hardcoded ones, complete French catalog (#14164) 2026-06-11 17:10:31 -03:00
SoftFever 15f330641c Add Microsoft Store MSIX package build (#14142)
* docs: add MSIX Store build design spec

* docs: update MSIX spec (PFN deep link, .drc, Associate tab) and add implementation plan

* ci: add MSIX logo asset generator and generated assets

* ci: fix MSIX asset rendering edge bleed (PixelOffsetMode) and make output order deterministic

* ci: add MSIX AppxManifest template

* ci: add MSIX packaging script

* ci: make build_msix.ps1 stage-only exit dot-source safe

* ci: build MSIX Store package in Windows job

* ci: run MSIX pack after existing Windows uploads and keep it out of release downloads

* feat: add MSIX packaged-context detection helpers

* fix: resolve MSIX package APIs dynamically to keep Win7 loadable

* feat: suppress self-update in MSIX Store build

* feat: suppress runtime file associations in MSIX Store build

* feat: keep version check in MSIX build, point update dialog at the Store

The update check is notification-only (OrcaSlicer never auto-downloads),
so the Store build keeps checking for new versions instead of skipping
the check. What changes when packaged is the new-version dialog: the
Download button is hidden, the info text asks the user to update from
the Microsoft Store, and the hyperlink / wxID_YES action opens the Store
product page instead of the GitHub release page.

* docs: align spec verification plan with Store-redirect updater behavior

* feat: default MSIX identity to the reserved Partner Center values

* feat: render MSIX logos full-bleed from the gradient-circle SVG

* feat: point update dialog Download button at the Store in MSIX builds

* feat: link Associate tab to Windows Default Apps settings in MSIX builds

* docs: align spec with review-driven logo, dialog and Associate-tab changes

* clearn up
2026-06-11 23:56:16 +08:00
hamham999 d07cb462a8 Creality K series printer updated start gcode (#14118)
* edit startgcode for K series printers

* Update Creality K1 (0.4 nozzle).json
2026-06-11 14:06:25 +08:00
raistlin7447andSoftFever 93a82af6b4 Fix top row tile order in YOLO perfectionist flow calibration (#14132)
Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-06-10 18:24:46 -03:00
Noisyfox 06eefe7c1e Make sure mouse position is updated before hit test (#14139) 2026-06-11 02:47:33 +08:00
Ian Chua 847807adac fix: tombstone resolution for 409 status code with error code -3 (#14116)
* fix: tombstone resolution for 409 status code with error code -3

* fix: add resolution for undefined conflicts

* fix: generate setting id if it is empty for 409 tombstone

* fix: force push empty setting_id preset on 409 tombstone

* clearner solution
2026-06-10 22:23:12 +08:00
wujie 3be36bdea4 Fix Elegoo CC2 layer stats G-code and missing Giga preset renamed_from (#14136)
* Sync Elegoo profiles from ElegooSlicer

Update vendor Elegoo.json, filament/machine/process trees, and OrcaFilamentLibrary
Elegoo entries. Align machine default material names with existing filament preset names.

* feat: expose filament_name for G-code export filename format

Derive from filament_settings_id for the first active extruder and strip the suffix after @, matching ElegooSlicer so filename_format can use {filament_name}.

* chore: reorder Elegoo entries in OrcaFilamentLibrary

Group Elegoo @base profiles and bump library version to 02.03.02.62.

* sync OrcaFilamentLibrary.json with Elegoo filament profiles

* fix: clean up Elegoo process renamed_from for profile validation

Add single renamed_from only where preset names changed from legacy Orca
names; remove duplicate Rapid @System library entries that conflicted with
ECC2 vendor presets.

* fix(profiles): add missing Elegoo renamed_from for profile validation

CI custom-preset tests still inherit legacy Orca preset names that no
longer exist after the Elegoo bundle update. Add renamed_from on process,
Neptune 4 machines, OrcaFilamentLibrary filaments, and Giga profiles so
inherits resolve again, without changing print parameters.

* fix(profiles,elegoo): resolve renamed presets and CC2 SET_PRINT_STATS_INFO G-code

Resolve legacy preset names through renamed_from when validating presets and loading external projects. Add missing renamed_from aliases for Elegoo Giga process and OrcaFilamentLibrary filaments. Combine TOTAL_LAYER and CURRENT_LAYER in one SET_PRINT_STATS_INFO command on Centauri Carbon 2 (ECC2), Centauri (EC), and Centauri Carbon (ECC) 0.4 nozzle profiles.

* chore(profiles): bump Elegoo and OrcaFilamentLibrary profile versions

Refresh installed profile bundles after renamed_from aliases, CC2 SET_PRINT_STATS_INFO G-code, and Preset.cpp renamed preset resolution fixes.
2026-06-10 21:38:39 +08:00
Heiko Liebscher a70b903a19 Update German translations in OrcaSlicer_de.po (#14135) 2026-06-10 14:54:11 +08:00
Rodrigo Faselli 6d9ab28e2d Revert "skirt before brims (combine brim bugfix)" (#14131) 2026-06-09 20:25:34 -03:00
Rodrigo Faselli 38ea91a6bb skirt before brims (combine brim bugfix) (#14126) 2026-06-09 17:14:30 -03:00
370 changed files with 88901 additions and 190364 deletions
+22 -3
View File
@@ -14,6 +14,9 @@ on:
- 'resources/**'
- ".github/workflows/build_*.yml"
- 'scripts/flatpak/**'
- 'snap/**'
- 'scripts/msix/**'
- 'tests/**'
pull_request:
branches:
@@ -30,6 +33,9 @@ on:
- 'build_release_vs2022.bat'
- 'build_release_macos.sh'
- 'scripts/flatpak/**'
- 'snap/**'
- 'scripts/msix/**'
- 'tests/**'
schedule:
@@ -51,11 +57,23 @@ jobs:
build_linux:
strategy:
fail-fast: false
# Build both arches on every event (PRs included), through the same
# build_check_cache -> build_deps -> build_orca chain (AppImage + snap).
# aarch64 always uses the GitHub-hosted arm runner (there is no arm
# self-hosted server). amd64's empty arch is load-bearing: it keeps the
# historical 'linux-clang' deps cache key and the unsuffixed asset names.
matrix:
include:
- arch: ""
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
- arch: "aarch64"
os: ubuntu-24.04-arm
# Don't run scheduled builds on forks:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
uses: ./.github/workflows/build_check_cache.yml
with:
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
os: ${{ matrix.os }}
arch: ${{ matrix.arch }}
build-deps-only: ${{ inputs.build-deps-only || false }}
secrets: inherit
build_windows:
@@ -95,7 +113,9 @@ jobs:
secrets: inherit
unit_tests:
name: Unit Tests
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
# Tests are built on the aarch64 leg by default (faster GitHub arm runner),
# so run them there; self-hosted builds them on the amd64 server instead.
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
needs: build_linux
if: ${{ !cancelled() && success() }}
steps:
@@ -233,4 +253,3 @@ jobs:
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
asset_content_type: application/octet-stream
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
+4 -2
View File
@@ -33,8 +33,10 @@ jobs:
- name: set outputs
id: set_outputs
env:
# Keep macOS cache keys and paths architecture-specific.
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || 'linux-clang') }}
# Keep macOS/Linux cache keys architecture-specific. amd64 Linux passes
# no arch (key stays 'linux-clang', preserving the existing cache);
# aarch64 gets its own 'linux-clang-aarch64' key.
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || '/OrcaSlicer_dep' }}
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
run: |
+99 -27
View File
@@ -65,6 +65,15 @@ jobs:
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
# Per-arch Linux naming, computed once: amd64 keeps the historical
# unsuffixed names (arch_suffix empty); snap_arch is snapcraft's token.
# Unused on macOS/Windows.
if [ '${{ inputs.arch }}' = 'aarch64' ]; then
echo "arch_suffix=_aarch64" >> $GITHUB_ENV
echo "snap_arch=arm64" >> $GITHUB_ENV
else
echo "snap_arch=amd64" >> $GITHUB_ENV
fi
shell: bash
- name: Get the version and date on Windows
@@ -153,14 +162,6 @@ jobs:
run: |
./build_release_macos.sh -u -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a universal -t 10.15
- name: Delete intermediate per-arch artifacts
if: runner.os == 'macOS' && inputs.macos-combine-only
uses: geekyeggo/delete-artifact@v6
with:
name: |
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
# Thanks to RaySajuuk, it's working now
- name: Sign app and notary
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
@@ -171,6 +172,8 @@ jobs:
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
CERTIFICATE_ID: ${{ secrets.MACOS_CERTIFICATE_ID }}
run: |
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
source ${{ github.workspace }}/scripts/retry.sh
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
@@ -193,7 +196,7 @@ jobs:
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Create separate OrcaSlicer_profile_validator DMG if the app exists
@@ -202,7 +205,7 @@ jobs:
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
@@ -220,11 +223,13 @@ jobs:
if: github.ref != 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only
working-directory: ${{ github.workspace }}
run: |
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
source ${{ github.workspace }}/scripts/retry.sh
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
# Create separate OrcaSlicer_profile_validator DMG if the app exists
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
@@ -232,9 +237,19 @@ jobs:
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
fi
# Delete the per-arch bundles only after signing/DMG creation succeeded, so a
# failed run keeps them available for a re-run instead of forcing a full rebuild.
- name: Delete intermediate per-arch artifacts
if: success() && runner.os == 'macOS' && inputs.macos-combine-only
uses: geekyeggo/delete-artifact@v6
with:
name: |
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
- name: Upload artifacts mac
if: runner.os == 'macOS' && inputs.macos-combine-only
uses: actions/upload-artifact@v7
@@ -371,6 +386,25 @@ jobs:
asset_content_type: application/x-msdownload
max_releases: 1
- name: Build MSIX Store package Win
if: runner.os == 'Windows' && !vars.SELF_HOSTED
working-directory: ${{ github.workspace }}
shell: pwsh
run: |
./scripts/msix/build_msix.ps1 `
-InstallDir "${{ github.workspace }}/build/OrcaSlicer" `
-OutputPath "${{ github.workspace }}/build/OrcaSlicer_Windows_MSIX_${{ env.ver }}.msix" `
-IdentityName "${{ vars.ORCA_MSIX_IDENTITY_NAME || 'OrcaSlicer.OrcaSlicer' }}" `
-Publisher "${{ vars.ORCA_MSIX_PUBLISHER || 'CN=38F7EA55-C73B-4072-B3B2-C8E0EA15BB82' }}" `
-PublisherDisplayName "${{ vars.ORCA_MSIX_PUBLISHER_DISPLAY_NAME || 'OrcaSlicer' }}"
- name: Upload artifacts Win MSIX
if: runner.os == 'Windows' && !vars.SELF_HOSTED
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Windows_MSIX_${{ env.ver }}
path: ${{ github.workspace }}/build/OrcaSlicer_Windows_MSIX_${{ env.ver }}.msix
# Ubuntu
- name: Apt-Install Dependencies
if: runner.os == 'Linux' && !vars.SELF_HOSTED
@@ -382,16 +416,54 @@ jobs:
if: runner.os == 'Linux'
shell: bash
run: |
./build_linux.sh -istrlL
# Build + tar the unit tests (-t) only on the leg that runs them: the
# aarch64 leg by default (faster GitHub arm runner), or amd64 when using
# self-hosted runners (no arm self-hosted server). unit_tests downloads
# this tarball. The profile validator is built with -s, so amd64 keeps it.
tests=${{ (!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64') }}
if $tests; then flags=-istrlL; else flags=-isrlL; fi
./build_linux.sh "$flags"
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
chmod +x ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
tar -cvpf build_tests.tar build/tests
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
chmod +x "$appimage"
if $tests; then tar -cvpf build_tests.tar build/tests; fi
# Build the snap right here, reusing the AppDir we just produced
# (build/package): the compiled binary + bundled libs + resources are
# repackaged as-is, for both amd64 and aarch64. Skipped on PRs (snapcraft
# adds several minutes per arch). The snap is Store-only; it is never
# attached to a GitHub release.
- name: Free disk space for snap build
if: runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request'
run: sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /opt/hostedtoolcache || true
- name: Build snap
if: runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request'
id: snapbuild
uses: snapcore/action-build@v1
- name: Upload snap artifact
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request' }}
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer-Linux-snap_${{ env.ver }}_${{ env.snap_arch }}.snap
path: ${{ steps.snapbuild.outputs.snap }}
retention-days: 5
if-no-files-found: error
# Nightly -> Snap Store 'edge'. NOTE: classic confinement means the Store
# holds uploads for manual review until classic is granted (snap/README.md).
- name: Publish snap to edge (nightly)
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
with:
snap: ${{ steps.snapbuild.outputs.snap }}
release: edge
# Use tar because upload-artifacts won't always preserve directory structure
# and doesn't preserve file permissions
- name: Upload Test Artifact
if: runner.os == 'Linux'
if: runner.os == 'Linux' && ((!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64'))
uses: actions/upload-artifact@v7
with:
name: ${{ github.sha }}-tests
@@ -401,7 +473,7 @@ jobs:
if-no-files-found: error
- name: Run external slicer regression tests
if: runner.os == 'Linux'
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
timeout-minutes: 20
shell: bash
run: |
@@ -411,7 +483,7 @@ jobs:
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
- name: Build orca_custom_preset_tests
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64'
working-directory: ${{ github.workspace }}/build/src/Release
shell: bash
run: |
@@ -423,11 +495,11 @@ jobs:
if: ${{ ! env.ACT && runner.os == 'Linux' }}
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
- name: Upload OrcaSlicer_profile_validator Ubuntu
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED }}
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
@@ -439,12 +511,12 @@ jobs:
with:
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
release_id: 137995723
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.AppImage
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_nightly.AppImage
asset_content_type: application/octet-stream
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Deploy Ubuntu release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: rickstaa/action-create-tag@v1
with:
tag: "nightly-builds"
@@ -453,7 +525,7 @@ jobs:
message: "nightly-builds"
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
@@ -464,7 +536,7 @@ jobs:
max_releases: 1
- name: Deploy orca_custom_preset_tests
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
uses: WebFreak001/deploy-nightly@v3.2.0
with:
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
+33 -1
View File
@@ -73,11 +73,13 @@ jobs:
- name: Download release artifacts from build run
run: |
# Windows_V* (not Windows_*) keeps the MSIX Store artifact out: it goes to Partner Center, not GitHub releases.
gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --dir artifacts \
-p 'OrcaSlicer_Windows_*' \
-p 'OrcaSlicer_Windows_V*' \
-p 'OrcaSlicer_Mac_universal_*' \
-p 'OrcaSlicer_Linux_ubuntu_*' \
-p 'OrcaSlicer-Linux-flatpak_*' \
-p 'OrcaSlicer-Linux-snap_*' \
-p 'PDB'
echo "Downloaded artifact folders:"
ls -1 artifacts
@@ -100,6 +102,9 @@ jobs:
find artifacts -type f -name '*.AppImage' -exec cp -v {} upload/ \;
# Flatpak bundles (x86_64 + aarch64).
find artifacts -type f -name '*.flatpak' -exec cp -v {} upload/ \;
# Snaps are intentionally NOT copied here: they go to the Snap Store
# only (see the "Publish snaps to the Snap Store" step), not to the
# GitHub release.
# Windows debug symbols (PDB archive, for developers).
find artifacts -type f -name 'Debug_PDB_*.7z' -exec cp -v {} upload/ \;
@@ -125,3 +130,30 @@ jobs:
gh release upload "$TAG" upload/* --repo "$GITHUB_REPOSITORY" --clobber
echo "Uploaded to draft release: $TAG"
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name'
- name: Publish snaps to the Snap Store
# Snaps ship to the Store only (not as release assets). Channel comes from
# the tag suffix; nightly -> edge is handled in the build workflows.
# NOTE: classic confinement means the Store holds uploads for manual
# review until classic is granted (see snap/README.md).
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
run: |
set -euo pipefail
mapfile -t snaps < <(find artifacts -type f -name '*.snap')
if [ ${#snaps[@]} -eq 0 ]; then
echo "::warning::No .snap artifacts in run $RUN_ID; skipping Snap Store publish."
exit 0
fi
case "$TAG" in
*-rc*) channel=candidate ;;
*-beta*|*-alpha*) channel=beta ;;
*) channel=stable ;;
esac
echo "Releasing $TAG to Snap Store channel: $channel"
sudo snap install snapcraft --classic
for s in "${snaps[@]}"; do
echo "::group::snapcraft upload $s --release=$channel"
snapcraft upload "$s" --release="$channel"
echo "::endgroup::"
done
+1 -2
View File
@@ -45,5 +45,4 @@ test.js
.clangd
internal_docs/
*.flatpak
/flatpak-repo/
*.pyc
/flatpak-repo/
+24
View File
@@ -159,8 +159,32 @@ flatpak run com.orcaslicer.OrcaSlicer
It can also be installed through graphical software managers (KDE Discover, GNOME Software, etc.) when Flathub is enabled. Search for **OrcaSlicer** in your software center.
### Snap Store
OrcaSlicer is available from the Snap Store:
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/orcaslicer)
```shell
sudo snap install orcaslicer --classic
```
Use a different channel for pre-releases or bleeding-edge builds:
```shell
sudo snap install orcaslicer --classic --candidate # release candidates (vX.Y.Z-rc)
sudo snap install orcaslicer --classic --beta # alpha / beta pre-releases
sudo snap install orcaslicer --classic --edge # nightly builds
```
The snap uses classic confinement, so it has full hardware and filesystem access just like the
AppImage — including USB/serial printers, removable media, network shares, and 3D mice
(3Dconnexion SpaceMouse via `spacenavd`). No extra setup is required after install.
### AppImage
AppImages are published for both **x86_64** and **aarch64** (ARM64). Pick the file matching your CPU — the ARM64 build has `aarch64` in its name (e.g. `OrcaSlicer_Linux_AppImage_Ubuntu2404_aarch64_*.AppImage`).
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
2. Double click the downloaded file to run it.
+17 -6
View File
@@ -504,13 +504,24 @@ if [[ -n "${USE_LLD}" ]] ; then
fi
fi
# Auto-detect ccache for faster rebuilds
export CMAKE_CCACHE_ARGS=()
if command -v ccache >/dev/null 2>&1 ; then
echo "ccache found at $(command -v ccache), enabling compiler caching..."
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache)
CMAKE_CCACHE=${CMAKE_CCACHE:-}
if [ -n "$CMAKE_CCACHE" ]; then
echo "Checking ${CMAKE_CCACHE} environment variable for compiler cache program..."
CMAKE_CCACHE=$(command -v "${CMAKE_CCACHE}") || {
echo "CMAKE_CCACHE environment variable is set to '${CMAKE_CCACHE}' but it was not found in PATH."
CMAKE_CCACHE=""
}
elif command -v sccache >/dev/null 2>&1 ; then
CMAKE_CCACHE=$(command -v sccache)
elif command -v ccache >/dev/null 2>&1 ; then
CMAKE_CCACHE=$(command -v ccache)
fi
if [ -n "${CMAKE_CCACHE}" ] ; then
echo "${CMAKE_CCACHE} found, enabling compiler caching..."
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_CCACHE}" -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CCACHE}")
else
echo "Note: ccache not found. Install ccache for faster rebuilds."
echo "Note: ccache or sccache are not found. Install either of them for faster rebuilds."
fi
if [[ -n "${BUILD_DEPS}" ]] ; then
@@ -525,7 +536,7 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
BUILD_ARGS+=(-DCMAKE_BUILD_TYPE="${BUILD_CONFIG}")
fi
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" "${CMAKE_CCACHE_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
print_and_run cmake --build deps/$BUILD_DIR -j1
fi
+6
View File
@@ -10,7 +10,13 @@ if (APPLE AND CMAKE_OSX_ARCHITECTURES)
set(_context_arch_line "-DBOOST_CONTEXT_ARCHITECTURE:STRING=${CMAKE_OSX_ARCHITECTURES}")
endif ()
set(_options "")
if (MSVC AND DEP_DEBUG)
set(_options "FORWARD_CONFIG")
endif ()
orcaslicer_add_cmake_project(Boost
${_options}
URL "https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz"
URL_HASH SHA256=4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95
LIST_SEPARATOR |
+18 -6
View File
@@ -155,17 +155,25 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
endif ()
function(orcaslicer_add_cmake_project projectname)
cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
if (_is_multi OR MSVC)
if (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
if (P_ARGS_FORWARD_CONFIG)
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
elseif (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
set(_configs_line "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
else ()
set(_configs_line "")
endif ()
endif ()
if (P_ARGS_FORWARD_CONFIG)
set(_target_config "$<CONFIG>")
else()
set(_target_config "Release")
endif()
if (MSVC)
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
else()
@@ -196,6 +204,8 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
-DCMAKE_DEBUG_POSTFIX:STRING=d
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
-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_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE}
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
@@ -206,8 +216,8 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
${DEP_CMAKE_OPTS}
${P_ARGS_CMAKE_ARGS}
${P_ARGS_UNPARSED_ARGUMENTS}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
)
if (FLATPAK)
@@ -241,14 +251,16 @@ else()
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
-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}
-DBUILD_SHARED_LIBS:BOOL=OFF
${_cmake_osx_arch}
"${_configs_line}"
${DEP_CMAKE_OPTS}
${P_ARGS_CMAKE_ARGS}
${P_ARGS_UNPARSED_ARGUMENTS}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
)
endif()
+6
View File
@@ -1,4 +1,10 @@
set(_options "")
if (MSVC AND DEP_DEBUG)
set(_options "FORWARD_CONFIG")
endif ()
orcaslicer_add_cmake_project(Draco
${_options}
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
)
+5
View File
@@ -1,7 +1,11 @@
if (MSVC)
set(_use_IPP "-DWITH_IPP=ON")
if (DEP_DEBUG)
set(_options "FORWARD_CONFIG")
endif ()
else ()
set(_use_IPP "-DWITH_IPP=OFF")
set(_options "")
endif ()
if (IN_GIT_REPO)
@@ -9,6 +13,7 @@ if (IN_GIT_REPO)
endif ()
orcaslicer_add_cmake_project(OpenCV
${_options}
URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz
URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
@@ -1123,18 +1123,17 @@ private:
std::vector<RawShape> objs,excludes;
for (const Item &item : items_) {
if (item.isFixed()) continue;
objs.push_back(item.transformedShape());
if (item.isFixed())
excludes.push_back(item.transformedShape());
else
objs.push_back(item.transformedShape());
}
if (objs.empty())
return;
// Without fixed items this inner-fit NFP can exceed clipper's range and crash MSVC.
if (!excludes.empty())
{ // find a best position inside NFP of fixed items (excluded regions), so the center of pile is cloest to bed center
RawShape objs_convex_hull = sl::convexHull(objs);
for (const Item &item : items_) {
if (item.isFixed()) {
excludes.push_back(item.transformedShape());
}
}
auto nfps = calcnfp(objs_convex_hull, excludes, bbin, Lvl<MaxNfpLevel::value>());
if (nfps.empty()) {
-668
View File
@@ -1,668 +0,0 @@
# OrcaSlicer UI Automation Protocol (v1.0.0)
OrcaSlicer ships an **opt-in, localhost-only JSON-RPC server** that lets external
scripts introspect, drive, and screenshot the running OrcaSlicer GUI. It is built
for end-to-end testing and automation: a script can enumerate the live widget
tree, click buttons, type text, send keyboard shortcuts, wait for UI state, query
high-level application state, load models/projects into the running instance,
switch the active view/tab, and capture window images (the on-screen capture
includes the 3D viewport).
This document is the protocol reference. It describes activation, the transport,
the JSON-RPC envelope, every method, the unified node shape, the target/locator
model, error codes, the set of instrumented automation ids, ImGui specifics,
platform caveats, a quick-start snippet, and planned future work.
---
## 1. Overview & activation
The automation server is **OFF by default**. It is enabled with two
command-line flags:
| Flag | Meaning |
|---|---|
| `--automation-server` | Enable the automation server. |
| `--automation-server-port=PORT` | Override the listening port. Optional; default is **13619**. |
Example:
```bash
OrcaSlicer --automation-server --automation-server-port=13619 model.stl
```
The server binds to **`127.0.0.1` only** (the loopback interface). It is never
exposed on an external network interface.
**Security note (v1):** there is **no authentication token** in v1. The localhost
bind is the *only* security boundary. Any process able to run code on the machine
can connect to the port and drive the GUI — including injecting mouse and keyboard
input — while the server is enabled. The feature is intended for testing and
automation environments, not for production or shared/multi-user machines.
When the server is enabled, OrcaSlicer emits a `warning`-level log line at startup
to make the active input-injection surface obvious in logs, for example:
```
UI automation server ENABLED ... input injection is active
```
---
## 2. Transport
The server speaks **HTTP/1.1** over the loopback TCP socket:
| Request | Response |
|---|---|
| `POST /jsonrpc` with a JSON-RPC 2.0 request body | A JSON-RPC 2.0 response with `Content-Type: application/json`. |
| `GET /` | A plain-text health page: `OrcaSlicer automation server v1.0.0` (`Content-Type: text/plain`). |
| Anything else | HTTP `404 Not Found`. |
The server is **single-client / serialized** in v1: it handles one request at a
time on its own dedicated I/O thread. Connections are not kept alive; each request
is answered and the socket is closed. Clients should issue requests sequentially.
---
## 3. JSON-RPC envelope
The protocol follows **JSON-RPC 2.0**.
**Request:**
```json
{ "jsonrpc": "2.0", "id": <id>, "method": "<method>", "params": { ... } }
```
- `params` may be omitted; the server treats a missing `params` as an empty object.
**Success response:**
```json
{ "jsonrpc": "2.0", "id": <id>, "result": { ... } }
```
**Error response:**
```json
{ "jsonrpc": "2.0", "id": <id>, "error": { "code": <int>, "message": "<string>" } }
```
The request `id` is echoed back in the response. When the request has no `id`, or
when the request body cannot be parsed as JSON, the response `id` is `null`.
---
## 4. Methods
There are 12 methods. Capabilities advertised by `automation.version` list the 11
callable feature methods (every method except `automation.version` itself).
### `automation.version`
Returns server identity and the list of supported methods. Takes no parameters.
**Result:**
```json
{
"version": "1.0.0",
"protocol": "2.0",
"capabilities": [
"tree.dump", "tree.find", "widget.get", "input.click", "input.type",
"input.key", "sync.wait_for", "app.state", "screenshot.window", "file.open",
"view.select"
]
}
```
### `tree.dump`
Snapshot the live UI tree as a single root node with nested children.
**Params (all optional):**
| Param | Type | Default | Meaning |
|---|---|---|---|
| `root` | string (id or path) | full tree | Root the dump at the node with this id/path. |
| `max_depth` | int | `-1` | Maximum depth to descend. `-1` = unlimited. |
| `visible_only` | bool | `false` | When true, omit non-visible nodes. |
| `include_imgui` | bool | `true` | When true, include ImGui items. |
**Result:** the serialized root [node](#5-unified-node-shape), with `children`
included.
### `tree.find`
Find all nodes matching a [target predicate](#6-target--locator).
**Params:** a target predicate — any combination of `name`, `class`, `label`,
`value`, `backend` (provided fields are ANDed). The params object is the target
itself (it is *not* wrapped in a `target` key for this method).
**Result:** a **flat JSON array** of matching nodes. The nodes in this array are
returned **without** their `children` (use `widget.get`/`tree.dump` to descend).
### `widget.get`
Fetch a single node by [target](#6-target--locator).
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `target` | object | yes | Target spec (id / path / predicate). |
**Result:** a single [node](#5-unified-node-shape), with its `children` included.
**Errors:** `1001` if the target is **not found** *or* **ambiguous** (more than one
match).
### `input.click`
Click a resolved, actionable node.
**Params:**
| Param | Type | Default | Meaning |
|---|---|---|---|
| `target` | object | required | Target spec; must resolve to exactly one node. |
| `button` | string | `"left"` | `"left"`, `"right"`, or `"middle"`. |
| `double` | bool | `false` | Double-click when true. |
| `modifiers` | array of string | `[]` | Held modifiers: any of `"ctrl"`, `"shift"`, `"alt"`, `"cmd"` (`"meta"` is accepted as an alias of `"cmd"`). |
**Result:** `{ "ok": true }`.
**Errors:** `1001` not found / ambiguous; `1002` if the target is disabled or
hidden (not actionable). The click path raises and focuses the target's top-level
window before injecting the click.
### `input.type`
Type text into the currently focused control.
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `text` | string | yes | The text to type. |
| `target` | object | no | If given, this node is clicked first (to focus it) before typing. |
**Result:** `{ "ok": true }`.
**Errors:** if `target` is supplied, the same actionability errors as
`input.click` apply (`1001` / `1002`).
### `input.key`
Send a key chord (a key plus optional modifiers) to the focused window.
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `keys` | string or array | yes | Either a `"+"`-joined string like `"ctrl+s"`, or an array like `["ctrl", "s"]`. The last token is the key; earlier tokens are modifiers. |
**Result:** `{ "ok": true }`.
**Key names must be lowercase.** Recognized key names include `"enter"`, `"tab"`,
`"esc"`, `"space"`, `"delete"`, `"backspace"`, `"f5"` (and other function keys),
and single characters (e.g. `"s"`, `"a"`). Recognized modifiers are `"ctrl"`,
`"shift"`, `"alt"`, `"cmd"` (with `"meta"` as an alias for `"cmd"`).
**Unrecognized or uppercase key names are silently ignored** — no error is
returned, the key simply does not fire. Use lowercase names exclusively.
### `sync.wait_for`
Poll the UI until a target node reaches a desired state, or time out. This is the
preferred way to synchronize with asynchronous UI changes (it replaces fragile
fixed sleeps). Internally it repeatedly refreshes and dumps the tree, re-resolves
the target, and evaluates the requested state until it is satisfied.
**Params:**
| Param | Type | Default | Meaning |
|---|---|---|---|
| `target` | object | required | Target spec. |
| `state` | string | required | One of `"exists"`, `"visible"`, `"enabled"`, `"value"`. |
| `value` | string | — | Required when `state` is `"value"`; the expected value to match. |
| `timeout_ms` | int | `5000` | Maximum time to wait, in milliseconds. |
| `poll_ms` | int | `100` | Poll interval, in milliseconds (minimum 1). |
State semantics:
- `exists` — the target resolves to a node.
- `visible` — the node exists and is visible.
- `enabled` — the node exists and is **both enabled and visible**.
- `value` — the node has a value and that value equals the supplied `value`.
**Result:** `{ "ok": true, "elapsed_ms": <int> }`.
**Errors:** `1003` on timeout (the state was not reached within `timeout_ms`).
### `app.state`
Return a high-level application-state snapshot. Takes no parameters.
**Result:**
```json
{
"active_tab": "<string>",
"project_loaded": <bool>,
"slicing": <bool>,
"slice_progress": <int>,
"foreground": <bool>,
"modal_dialog": "<string>"
}
```
| Field | Meaning |
|---|---|
| `active_tab` | The active top-level tab/page. |
| `project_loaded` | Whether a project/model is currently loaded. |
| `slicing` | Whether slicing is currently in progress. |
| `slice_progress` | Slicing progress (`-1` when unknown). |
| `foreground` | Whether the main window is in the foreground. |
| `modal_dialog` | Present only when a modal dialog is active; identifies it. Omitted otherwise. |
### `screenshot.window`
Capture a window as a PNG, exactly as it appears on screen.
**Params:**
| Param | Type | Default | Meaning |
|---|---|---|---|
| `target` | object | main frame | If given, capture this window; otherwise capture the main frame. |
**Result:** `{ "png_base64": "<base64 PNG>", "width": <int>, "height": <int> }`.
**Errors:** `1005` on screenshot failure; `1001` if a supplied `target` is not
found or ambiguous.
**How it works:** the window's on-screen rectangle is read back from the
DWM-composited desktop framebuffer (`wxScreenDC`), so the capture includes every
native child control, the OpenGL 3D viewport, and ImGui overlays — it is a faithful
image of what the user sees. (Capturing the parent window's own client DC instead
would clip out child HWNDs and the GL surface, leaving them black; that is why this
method reads from the screen.)
**Caveats:**
- The window must be **visible and unobscured**. Because the source is the on-screen
framebuffer, any overlapping window occludes the captured region. The backend
raises the target window before capturing.
- **HiDPI:** the reported `width`/`height` come from the window's logical client size,
while the screen framebuffer is in physical pixels. On per-monitor-DPI displays the
two can differ; the capture may be cropped or scaled relative to the logical size.
- Because the capture is the live on-screen image, the 3D content reflects the
**current view**: the model in the 3D editor, or the gcode toolpaths in Preview
after a slice. There is no separate offscreen 3D-render method — the window
capture already includes whatever the GL canvas is showing.
### `file.open`
Load one or more files into the **already-running** instance at runtime, by calling
`Plater::load_files(...)` directly on the GUI thread. This is the supported way to add
or swap a model without relaunching the process. Loading is **synchronous**: when the
call returns `ok: true`, `app.state().project_loaded` is already `true` (no polling
race).
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `paths` | string or array of strings | yes | One or more **absolute** file paths. A bare string is accepted and treated as a one-element list. Paths are read from the **host (server) filesystem** — client and server are localhost-only. |
`.3mf` files are routed as projects and meshes as models automatically, based on file
content (the same default strategy as drag-drop); there is no `as_project` flag in v1.
**Result:** `{ "ok": true, "loaded": <int> }`, where `loaded` is the number of objects
added to the scene (`load_files(...).size()`).
**Errors:**
- `-32602` (invalid params) — `paths` is missing, is not a string/array, contains a
non-string entry, or yields no non-empty path.
- `1007` (load failed) — `load_files` returned empty or threw (file not found, parse
error, or unsupported format).
- `1004` (GUI busy) — the GUI-thread marshal timed out. An extremely large model can
exceed the marshal timeout and surface here; documented, not mitigated in v1.
### `view.select`
Switch the main window to a top-level view/tab at runtime. Useful to put the UI in a
known state before other actions — e.g. switch to **Prepare** before loading a model,
or to **Preview** after slicing.
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `view` | string | yes | The target view. One of: `home`, `prepare` (3D editor), `preview` (sliced G-code), `device`, `multi_device`, `project`, `calibration`. |
**Result:** `{ "ok": true, "view": <string>, "index": <int> }`, where `index` is the
resulting tab index (it can vary with layout, since some tabs — e.g. `multi_device`
are only present in certain configurations).
**Errors:**
- `-32602` (invalid params) — `view` is missing, is not a string, or is empty.
- `1001` (not found) — the view name is unknown, or that view is not available in the
current layout (for example `prepare`/`preview` in G-code-viewer mode, or
`multi_device` when multi-device is disabled).
- `1004` (GUI busy) — the GUI-thread marshal timed out.
---
## 5. Unified node shape
Both wx widgets and ImGui items are reported with the same node schema:
```json
{
"backend": "wx" | "imgui",
"id": "<string>",
"path": "<string>",
"class": "<string>",
"label": "<string>",
"rect": { "x": <int>, "y": <int>, "w": <int>, "h": <int> },
"enabled": <bool>,
"visible": <bool>,
"value": "<string>",
"children": [ <node>, ... ]
}
```
| Field | Meaning |
|---|---|
| `backend` | `"wx"` for native wxWidgets controls, `"imgui"` for immediate-mode ImGui items. |
| `id` | The automation id when one is set, otherwise a derived id. For ImGui items the `path` doubles as the `id`. |
| `path` | Positional path, e.g. `"MainFrame/Panel[2]/Button[0]"`. For ImGui items: `"ImGui/<window>/<label>"`. |
| `class` | wx class name, or the ImGui item type. |
| `label` | The control's label/caption. May include an ImGui `##`-id suffix for ImGui items. |
| `rect` | Bounding rectangle in **screen coordinates**. |
| `enabled` | Whether the control is enabled. |
| `visible` | Whether the control is visible. |
| `value` | The control's value (text/choice/check/slider, etc.). **Omitted entirely** when the control has no applicable value. |
| `children` | Child nodes. **wx only**, and present only when children are included (e.g. `tree.dump`, `widget.get`). ImGui items are flat (no children) and are listed under their window. |
Notes:
- The `value` key is **omitted** (not `null`) when the control has no value.
- `children` is present only for wx nodes when children are requested; ImGui nodes
never carry `children`.
---
## 6. Target / locator
Most methods accept a **target** object that identifies one or more nodes. A
target may specify:
| Field | Meaning |
|---|---|
| `id` | Exact automation id. |
| `path` | Exact positional path. |
| `name` | Predicate: matches either the node's `id` **or** its `label`. |
| `class` | Predicate: exact class name. |
| `label` | Predicate: exact label. |
| `value` | Predicate: node has a value and it equals this string. |
| `backend` | Predicate: `"wx"` or `"imgui"`. |
**Resolution order:** **`id``path` → predicate.**
- If `id` is present, only `id` is used (exact match).
- Else if `path` is present, only `path` is used (exact match).
- Else the predicate fields (`name`, `class`, `label`, `value`, `backend`) are
used, and all provided predicate fields are **ANDed** together.
Action methods (`input.click`, `input.type` with a target, `widget.get`, and
single-target `screenshot.window`) require a **unique** match. If the target
resolves to zero matches or more than one match, the call fails with error `1001`
(not found / ambiguous). `tree.find` is the exception: it returns *all* matches as
an array and never errors on ambiguity.
---
## 7. Error codes
Standard JSON-RPC codes:
| Code | Meaning |
|---|---|
| `-32700` | Parse error — the request body was not valid JSON. |
| `-32600` | Invalid request — missing/invalid `method`. |
| `-32601` | Method not found — unknown method name. |
| `-32602` | Invalid params — missing/invalid parameters for the method. |
Application-specific codes:
| Code | Meaning |
|---|---|
| `1001` | Widget/target not found **or** ambiguous (more than one match). |
| `1002` | Not actionable — the target is disabled or hidden. |
| `1003` | Wait timeout — `sync.wait_for` did not reach the requested state in time. |
| `1004` | GUI thread busy / timeout — a backend call could not be marshaled onto the GUI thread in time (wedged GUI). |
| `1005` | Screenshot failed. |
| `1006` | Disabled. |
| `1007` | Load failed — `file.open`'s `load_files` returned empty or threw (not found, parse error, unsupported format). |
---
## 8. Automation-id naming conventions & instrumented ids
Stable automation ids follow these prefix conventions:
| Prefix | Used for |
|---|---|
| `btn_` | Buttons |
| `combo_` | Preset combo boxes |
| `tab_` | Tabs |
| `canvas_` | Canvases |
| `dlg_` | Dialog buttons |
### Instrumented ids (as-built in v1)
The following controls currently carry stable automation ids:
| id | Control | Note |
|---|---|---|
| `btn_slice` | Slice-plate button | |
| `btn_export` | Print / Export button | Multi-purpose: the action (Print plate / Export G-code / Send) depends on the current mode. |
| `tab_device` | Device / Monitor tab (`MonitorPanel`) | |
| `combo_printer` | Printer preset combo (sidebar) | |
| `combo_filament` | Filament preset combo (sidebar) | First filament row only; extra multi-material rows are not instrumented. |
| `canvas_3d` | 3D editor GL canvas | |
### Controls NOT instrumented in v1
Several controls are intentionally **not** instrumented in v1 because they have no
stable `wxWindow` target to attach an id to:
- **`combo_process`** — process settings are not a sidebar combo box in the current
OrcaSlicer layout, so there is no combo control to instrument.
- **`btn_add`** — the add/import-object control is a `GLToolbar` item rendered
*inside* the GL canvas, not a `wxWindow`.
- **`tab_prepare` / `tab_preview`** — the Prepare and Preview notebook pages are
both backed by the **same** window, and the per-tab buttons are private; there is
no distinct stable window to target.
For controls that are not instrumented, scripts should fall back to class / label /
path lookup (for wx controls) or ImGui-item lookup (for ImGui controls).
---
## 9. ImGui notes
ImGui is **immediate-mode**: an item is addressable only while it is being drawn in
the current frame. The automation backend records ImGui items each frame, and a
`refresh_ui` is forced before every read or action so that the latest frame's items
are captured.
Consequences and conventions:
- Use [`sync.wait_for`](#syncwait_for) to wait for a transient gizmo or panel item
to appear before acting on it.
- ImGui items are reported with `backend: "imgui"`, a `path` of the form
`ImGui/<window>/<label>`, and that **path doubles as the item's `id`** in v1.
- ImGui items are **flat** — they have no `children` and are listed under their
window.
- Labels may include ImGui `##`-id suffixes (the part after `##` that ImGui uses to
disambiguate identically labeled widgets).
- Raw `ImGui::` gizmos that are *not* routed through the instrumented
`ImGuiWrapper` widgets (for example some Emboss / SVG / Text gizmo controls) are
only covered at the **window level** in v1; their individual sub-items are not
enumerated.
---
## 10. Platform & display caveats
- **Input requires a focused, visible window.** OS-level input injection uses
`wxUIActionSimulator`, which requires a focused, visible window. The click path
raises and focuses the target's top-level window first.
- **Linux CI needs a display.** There must be an X display available; wrap test
runs with `xvfb-run` (for example, `xvfb-run -a python example_slice.py ...`).
- **Input is asynchronous.** Do **not** rely on fixed sleeps. Use
[`sync.wait_for`](#syncwait_for) — for example, wait for `btn_export` to become
`enabled` after slicing completes — rather than sleeping for a guessed duration.
- **`screenshot.window` reads the screen.** It captures the on-screen, DWM-composited
framebuffer, so the target window must be visible and unobscured, and the result is
in physical pixels (see HiDPI caveat under [`screenshot.window`](#screenshotwindow)).
The capture includes the GL 3D viewport as currently shown (model or toolpaths).
- **Single-client / serialized.** v1 handles one request at a time; issue requests
sequentially from a single client.
- **GUI-thread marshaling.** Every backend call is marshaled onto the GUI thread
with a timeout. A wedged or unresponsive GUI returns error `1004`.
---
## 11. Quick start
Using the reference client in `tools/automation/orca_automation.py`:
```python
from orca_automation import OrcaClient
orca = OrcaClient(port=13619)
print(orca.version()) # {'version': '1.0.0', ...}
orca.select_view("prepare") # switch to the 3D editor
orca.open(r"C:\models\part.stl") # load a model at runtime (synchronous)
orca.click({"id": "btn_slice"}) # start slicing the plate
orca.wait_for({"id": "btn_export"}, # wait until slicing finishes
state="enabled", timeout_ms=180000)
orca.select_view("preview") # switch to the sliced G-code preview
png = orca.screenshot() # on-screen capture (incl. 3D view)
with open("window.png", "wb") as f:
f.write(png)
```
For a full, runnable end-to-end example — launching OrcaSlicer with the automation
flags, loading a model, slicing, waiting for completion, and saving a window PNG —
see `tools/automation/example_slice.py`.
---
## 12. Future work
Planned enhancements beyond v1:
- **Authentication token** plus a Preferences toggle to enable/disable the server
from the GUI.
- **WebSocket push events** for real-time UI/state notifications (instead of
polling).
- **Per-item ImGui gizmo instrumentation** so individual gizmo sub-controls (Emboss
/ SVG / Text, etc.) are addressable, not just at the window level.
- **More widget ids** — the process combo, the add/import button, and the
Prepare/Preview tabs once they expose stable windows.
- An **MCP wrapper** to expose the automation surface to model-context tooling.
---
## Verification (v1)
This section records the final regression gate for the v1 feature: confirmation
that the protocol core is covered by unit tests, that the existing test suites
are unaffected, and that the **disabled path (automation OFF, the default) is a
true no-op** — zero new threads, zero socket binds, zero allocations, and zero
behavior change.
### Unit-suite results (Release, Windows / MSVC, Ninja Multi-Config)
| Suite | Result |
|---|---|
| `automation` (protocol core) | **32 / 32 passed** |
| `libslic3r` (most affected by the additive `PrintConfig.cpp` CLI options) | **99 / 99 passed** |
| `fff_print` | **14 / 14 passed** |
| `libnest2d` | **14 / 14 passed** |
| `sla_print` | **21 / 21 passed** |
| `slic3rutils` | 3 / 5 passed — 2 pre-existing `[OrcaCloudServiceAgent]` SEGFAULTs, **unrelated to automation** (see note) |
> The two `slic3rutils` failures are `Orca cloud flat/nested session resolves
> display name consistently`. They exercise `Slic3r::OrcaCloudServiceAgent`, which
> the automation branch does **not** touch (verified via `git diff --stat
> main...HEAD` — no change to `src/slic3r/Utils/OrcaCloudServiceAgent.*` or
> `tests/slic3rutils/*`). They are pre-existing and not a regression introduced by
> this feature.
### Static disabled-path audit (the core regression guarantee)
Verified by code reading that with no `--automation-server` flag:
- **Flag defaults off.** `m_automation_port` defaults to `0`
(`src/slic3r/GUI/GUI_App.hpp:249`); `is_automation_enabled()` returns
`m_automation_port > 0` (`GUI_App.hpp:386`) → `false` by default.
- **No server / thread / socket.** `post_init()` calls
`start_automation_server()` **only** when
`init_params->automation_port > 0` (`src/slic3r/GUI/GUI_App.cpp:737-740`), and
`start_automation_server()` itself early-returns when `m_automation_port <= 0`
(`GUI_App.cpp:7097`). The backend / dispatcher / beast server objects are
constructed nowhere else → no `orca_automation` thread and no localhost bind
when the flag is absent.
- **Recording hooks short-circuit.** `ImGuiWrapper::automation_record_last_item`
has as its **first statement** `if (!wxGetApp().is_automation_enabled())
return;` (`src/slic3r/GUI/ImGuiWrapper.cpp:576-577`) — a single bool check, no
`ImGuiItemRecord` allocation and no `ImGuiItemTable` access on the disabled
path. In `ImGuiWrapper::render()` the window-enumeration loop and
`swap_frame()` are fully wrapped in `if (wxGetApp().is_automation_enabled())`
(`ImGuiWrapper.cpp:599-611`); when off, `render()` is its original
`ImGui::Render()` + `render_draw_data()` plus one bool check.
- **Instrumentation is inert.** The ~7 `set_automation_id(...)` calls
(`MainFrame.cpp:1330,1389,1841,1842`; `Plater.cpp:1772,2172,5068`) only store a
pointer into a static registry and bind a `wxEVT_DESTROY` pruning handler
(`src/slic3r/GUI/Automation/AutomationRegistry.cpp:24-36`). The registry is
**read** only via `window_for_automation_id` / `automation_id_of`, which are
called solely by the backend while the server is running → harmless when off.
- **CLI options are purely additive.** `automation_server` (coBool, default
`false`) and `automation_server_port` (coInt, default `13619`) are new `add()`
entries appended after `enable_timelapse`
(`src/libslic3r/PrintConfig.cpp:10794-10805`); no existing option is changed.
`GUI_InitParams::automation_port` defaults to `0`
(`src/slic3r/GUI/GUI_Init.hpp:37`) and is set only when `--automation-server`
is supplied (`src/OrcaSlicer.cpp:1345-1348`).
**Conclusion:** with automation OFF (the default), the feature allocates nothing
and changes nothing — the only added cost on any hot path is a single boolean
comparison.
### Deferred manual runtime checks (require a display / Xvfb)
These need a live GUI and cannot be run headlessly in CI; they are the manual
acceptance steps:
1. Launch **without** `--automation-server``curl http://127.0.0.1:13619/`
fails to connect (no listener); no `orca_automation` thread exists.
2. Launch **with** `--automation-server --automation-server-port=13619`
`GET /` returns the health text; `POST /jsonrpc {"method":"automation.version"}`
returns version / protocol / capabilities; `widget.get {"target":{"id":"btn_slice"}}`
returns a node with a sensible screen rect.
3. Interactive sanity: open a gizmo / move sliders with automation OFF → no
visual or behavior change.
See `tools/automation/example_slice.py` for the runnable end-to-end path.
@@ -1,608 +0,0 @@
# `file.open` Automation Method Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add a `file.open` JSON-RPC automation method that loads one or more files into an already-running OrcaSlicer instance by calling `Plater::load_files(...)` synchronously on the GUI thread.
**Architecture:** Follows the existing `screenshot.window` / `app.state` method pattern. A new pure-virtual `open_files(paths)` is added to the wx-free `IUiBackend` interface; `WxUiBackend` implements it via the existing `run_on_gui(...)` GUI-thread marshal calling `Plater::load_files`; the `JsonRpcDispatcher` gains a `file.open` route, a param-parsing helper, and a new `kErrLoadFailed = 1007` error code. The unit-testable surface (dispatcher + param validation + routing) is driven against `MockUiBackend`.
**Tech Stack:** C++17, nlohmann::json, Catch2 v2 (`catch_all.hpp` / `Catch2WithMain`), wxWidgets, CMake + Ninja Multi-Config. Python 3 reference client (stdlib only).
---
## Design-spec note (resolve before coding)
The design spec's error table reads `1002 | kInvalidParams | paths missing/empty…`, but in the codebase `kInvalidParams` is the standard JSON-RPC code **`-32602`**, while `1002` is `kErrNotActionable`. The spec's **Constant column (`kInvalidParams`) is authoritative** and matches every other param-validation path in the dispatcher (e.g. `m_input_type` throws `kInvalidParams` for a bad `text`). This plan therefore validates `file.open` params with **`kInvalidParams` (-32602)**, exactly like the existing handlers, and the tests assert `== kInvalidParams`. The literal "1002" in the spec is a typo; do not emit code 1002 for param errors.
## File Structure
| File | Change | Responsibility |
|---|---|---|
| `src/slic3r/GUI/Automation/IUiBackend.hpp` | modify | Add pure-virtual `int open_files(paths)` to the backend abstraction (stays wx-free). |
| `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp` | modify | Add `kErrLoadFailed = 1007` constant + `m_file_open` declaration. |
| `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp` | modify | Add `parse_paths` helper, `m_file_open` body, dispatch route, capabilities entry. |
| `src/slic3r/GUI/Automation/WxUiBackend.hpp` | modify | Declare `open_files` override. |
| `src/slic3r/GUI/Automation/WxUiBackend.cpp` | modify | Implement `open_files` via `run_on_gui``Plater::load_files`. |
| `tests/automation/MockUiBackend.hpp` | modify | `open_files` override: record paths + return-count + fail knob. |
| `tests/automation/test_dispatcher.cpp` | modify | Catch2 tests for routing, string/array, validation, failure, capabilities. |
| `tools/automation/orca_automation.py` | modify | `open(self, paths)` client wrapper. |
| `tools/automation/example_slice.py` | modify | Launch without a model arg, then `orca.open([model])`. |
| `doc/automation.md` | modify | Document the method, capabilities, error `1007`. |
**Build/test layout:** Ninja Multi-Config in `build/`. The unit suite target is `automation_tests`; its sources (`tests/automation/CMakeLists.txt`) compile `JsonRpcDispatcher.cpp` + `MockUiBackend` but **not** `WxUiBackend.cpp`. So dispatcher/mock changes are fully unit-testable headlessly; `WxUiBackend.cpp` is verified by the full app build only.
---
## Task 1: Extend the backend abstraction (interface + mock + error code)
Adds the `open_files` contract so tests can be written. Adding a pure virtual to `IUiBackend` forces every implementation to provide it — in the unit-test target that is only `MockUiBackend`, so this task keeps the `automation_tests` build green.
**Files:**
- Modify: `src/slic3r/GUI/Automation/IUiBackend.hpp`
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp:19`
- Modify: `tests/automation/MockUiBackend.hpp`
- [ ] **Step 1: Add the error constant**
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp`, after the existing `kErrDisabled` line (currently line 19), add:
```cpp
constexpr int kErrDisabled = 1006;
constexpr int kErrLoadFailed = 1007; // file.open: load_files returned empty / threw
```
- [ ] **Step 2: Add the pure-virtual `open_files` to the interface**
In `src/slic3r/GUI/Automation/IUiBackend.hpp`, inside `class IUiBackend`, immediately after the `screenshot_window` pure virtual (currently line 97), add:
```cpp
// Load one or more files (absolute paths) into the running instance on the GUI
// thread. Returns the number of objects added to the scene (load_files(...).size()).
// Throws AutomationError(kErrLoadFailed) when nothing loads. Header stays wx-free:
// the concrete LoadStrategy is chosen inside WxUiBackend, not exposed here.
virtual int open_files(const std::vector<std::string>& paths) = 0;
```
- [ ] **Step 3: Implement `open_files` in the mock with record + knobs**
In `tests/automation/MockUiBackend.hpp`: add an include for the error constant near the top (after the `IUiBackend.hpp` include on line 2):
```cpp
#include "slic3r/GUI/Automation/IUiBackend.hpp"
#include "slic3r/GUI/Automation/JsonRpcDispatcher.hpp" // kErrLoadFailed
```
Add recorded-call + canned-output members. After the `screenshot_window_count` recorded field (line 20) add:
```cpp
int screenshot_window_count = 0;
std::vector<std::vector<std::string>> opened_paths; // paths of each open_files()
```
After the `click_result` canned field (line 26) add:
```cpp
bool click_result = true;
int open_return_count = 0; // value open_files() returns
bool open_should_fail = false; // when true, open_files() throws kErrLoadFailed
```
Add the override next to the other overrides, after `screenshot_window` (lines 49-51):
```cpp
PngImage screenshot_window(const UiNode*) override {
++screenshot_window_count; return canned_png;
}
int open_files(const std::vector<std::string>& paths) override {
opened_paths.push_back(paths);
if (open_should_fail)
throw AutomationError(kErrLoadFailed, "mock load failed");
return open_return_count;
}
```
- [ ] **Step 4: Build the unit-test target to confirm it still compiles**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests`
Expected: build succeeds (the new pure virtual is satisfied by the mock; no behavior change yet).
- [ ] **Step 5: Commit**
```bash
git add src/slic3r/GUI/Automation/IUiBackend.hpp src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp tests/automation/MockUiBackend.hpp
git commit -m "feat(automation): add open_files to backend interface + kErrLoadFailed (1007)"
```
---
## Task 2: `file.open` dispatcher handler (parse, route, validate, fail)
Implements the full JSON-RPC handler against the mock: param parsing (string or array), validation, routing to `open_files`, and `kErrLoadFailed` propagation.
**Files:**
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp:49` (declaration)
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp`
- Test: `tests/automation/test_dispatcher.cpp`
- [ ] **Step 1: Write the failing happy-path test (array of paths)**
Append to `tests/automation/test_dispatcher.cpp`:
```cpp
TEST_CASE("file.open with an array of paths routes to backend", "[automation][rpc]") {
MockUiBackend mock;
mock.open_return_count = 3;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",1},{"method","file.open"},
{"params",{{"paths", json::array({"C:/abs/a.stl","C:/abs/b.stl"})}}}});
CHECK(resp.at("result").at("ok") == true);
CHECK(resp.at("result").at("loaded") == 3);
REQUIRE(mock.opened_paths.size() == 1);
REQUIRE(mock.opened_paths[0].size() == 2);
CHECK(mock.opened_paths[0][0] == "C:/abs/a.stl");
CHECK(mock.opened_paths[0][1] == "C:/abs/b.stl");
}
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open with an array of paths routes to backend"`
Expected: FAIL — `file.open` is an unknown method, so the response carries `error.code == -32601` and has no `result` (the `resp.at("result")` access throws). (If the exe path differs on your machine, locate it with `find build -iname automation_tests.exe`.)
- [ ] **Step 3: Declare the handler**
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp`, after the `m_screenshot_window` declaration (currently line 49) add:
```cpp
nlohmann::json m_screenshot_window(const nlohmann::json& params);
nlohmann::json m_file_open(const nlohmann::json& params);
```
- [ ] **Step 4: Add the `parse_paths` helper and `m_file_open` body**
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp`, add a `parse_paths` helper. Place it in the anonymous namespace that also holds `parse_keys` — insert it right before that namespace's closing `} // namespace` (currently line 130):
```cpp
// "paths" may be a single string ("C:/a.stl") or an array of strings. Returns the
// non-empty absolute paths; throws kInvalidParams when paths is missing, not a
// string/array, contains a non-string entry, or yields no non-empty path.
std::vector<std::string> parse_paths(const nlohmann::json& params) {
if (!params.is_object() || !params.contains("paths"))
throw AutomationError(kInvalidParams, "file.open requires 'paths'");
const auto& p = params.at("paths");
std::vector<std::string> out;
if (p.is_string()) {
out.push_back(p.get<std::string>());
} else if (p.is_array()) {
for (const auto& e : p) {
if (!e.is_string())
throw AutomationError(kInvalidParams, "'paths' entries must be strings");
out.push_back(e.get<std::string>());
}
} else {
throw AutomationError(kInvalidParams, "'paths' must be a string or array");
}
out.erase(std::remove_if(out.begin(), out.end(),
[](const std::string& s) { return s.empty(); }),
out.end());
if (out.empty())
throw AutomationError(kInvalidParams, "'paths' is empty");
return out;
}
```
(`<algorithm>` for `std::remove_if` is already included at the top of the file, line 4.)
Add the handler body next to the other handlers. After `m_screenshot_window` (currently ends line 343, just before the final `}}} // namespace`), add:
```cpp
nlohmann::json JsonRpcDispatcher::m_file_open(const nlohmann::json& params) {
const std::vector<std::string> paths = parse_paths(params);
const int loaded = m_backend.open_files(paths);
return { {"ok", true}, {"loaded", loaded} };
}
```
- [ ] **Step 5: Add the dispatch route**
In `JsonRpcDispatcher::dispatch`, after the `screenshot.window` route (currently line 195) add:
```cpp
if (method == "screenshot.window") return make_result(id, m_screenshot_window(params));
if (method == "file.open") return make_result(id, m_file_open(params));
```
- [ ] **Step 6: Run the happy-path test to verify it passes**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open with an array of paths routes to backend"`
Expected: PASS — 1 test case, all assertions passed.
- [ ] **Step 7: Add the remaining handler tests (string, validation, failure)**
Append to `tests/automation/test_dispatcher.cpp`:
```cpp
TEST_CASE("file.open accepts a bare string path", "[automation][rpc]") {
MockUiBackend mock;
mock.open_return_count = 1;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",2},{"method","file.open"},
{"params",{{"paths","C:/abs/a.stl"}}}});
CHECK(resp.at("result").at("loaded") == 1);
REQUIRE(mock.opened_paths.size() == 1);
REQUIRE(mock.opened_paths[0].size() == 1);
CHECK(mock.opened_paths[0][0] == "C:/abs/a.stl");
}
TEST_CASE("file.open with missing paths -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",3},{"method","file.open"},
{"params", json::object()}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.opened_paths.empty());
}
TEST_CASE("file.open with empty paths array -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",4},{"method","file.open"},
{"params",{{"paths", json::array()}}}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.opened_paths.empty());
}
TEST_CASE("file.open with a non-string entry -> invalid params", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",5},{"method","file.open"},
{"params",{{"paths", json::array({"C:/a.stl", 42})}}}});
CHECK(resp.at("error").at("code") == kInvalidParams);
CHECK(mock.opened_paths.empty());
}
TEST_CASE("file.open backend load failure -> 1007", "[automation][rpc]") {
MockUiBackend mock;
mock.open_should_fail = true;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",6},{"method","file.open"},
{"params",{{"paths","C:/abs/a.stl"}}}});
CHECK(resp.at("error").at("code") == kErrLoadFailed);
}
```
- [ ] **Step 8: Run all file.open tests to verify they pass**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open*"`
Expected: PASS — 6 test cases, all assertions passed.
- [ ] **Step 9: Commit**
```bash
git add src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp tests/automation/test_dispatcher.cpp
git commit -m "feat(automation): add file.open dispatcher handler with validation + tests"
```
---
## Task 3: Advertise `file.open` in `automation.version` capabilities
**Files:**
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp:166-172`
- Test: `tests/automation/test_dispatcher.cpp`
- [ ] **Step 1: Write the failing capabilities test**
Append to `tests/automation/test_dispatcher.cpp`:
```cpp
TEST_CASE("automation.version capabilities include file.open", "[automation][rpc]") {
MockUiBackend mock;
JsonRpcDispatcher d(mock);
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",1},{"method","automation.version"}});
const auto& caps = resp.at("result").at("capabilities");
bool found = false;
for (const auto& c : caps) if (c == "file.open") found = true;
CHECK(found);
}
```
- [ ] **Step 2: Run the test to verify it fails**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "automation.version capabilities include file.open"`
Expected: FAIL — `CHECK(found)` is false; `file.open` is not yet in the capabilities array.
- [ ] **Step 3: Add `file.open` to the capabilities array**
In `JsonRpcDispatcher::m_version` (currently lines 166-172), add `"file.open"` to the array:
```cpp
nlohmann::json JsonRpcDispatcher::m_version(const nlohmann::json&) {
return { {"version", kAutomationVersion},
{"protocol", "2.0"},
{"capabilities", nlohmann::json::array({
"tree.dump","tree.find","widget.get","input.click","input.type",
"input.key","sync.wait_for","app.state","screenshot.window",
"file.open" })} };
}
```
- [ ] **Step 4: Run the test to verify it passes**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "automation.version capabilities include file.open"`
Expected: PASS.
- [ ] **Step 5: Run the whole automation suite to confirm no regressions**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe --order rand --warn NoAssertions`
Expected: PASS — all cases green (the pre-existing ~32 plus the 7 new `file.open`/capabilities cases ≈ 39).
- [ ] **Step 6: Commit**
```bash
git add src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp tests/automation/test_dispatcher.cpp
git commit -m "feat(automation): advertise file.open in automation.version capabilities"
```
---
## Task 4: Implement `WxUiBackend::open_files` (real GUI-thread load)
Not covered by the headless unit suite (`WxUiBackend.cpp` is excluded from `automation_tests`); verified by the full app build + the manual runtime check in Task 8.
**Files:**
- Modify: `src/slic3r/GUI/Automation/WxUiBackend.hpp:21`
- Modify: `src/slic3r/GUI/Automation/WxUiBackend.cpp`
- [ ] **Step 1: Declare the override**
In `src/slic3r/GUI/Automation/WxUiBackend.hpp`, after the `screenshot_window` declaration (line 21) add:
```cpp
PngImage screenshot_window(const UiNode* target) override;
int open_files(const std::vector<std::string>& paths) override;
```
- [ ] **Step 2: Implement `open_files`**
In `src/slic3r/GUI/Automation/WxUiBackend.cpp`, add the implementation just before the final `}}} // namespace Slic3r::GUI::Automation` (currently line 306):
```cpp
int WxUiBackend::open_files(const std::vector<std::string>& paths) {
return run_on_gui(m_gui_timeout_ms, [&]() -> int {
Plater* plater = wxGetApp().plater();
if (plater == nullptr)
throw AutomationError(kErrLoadFailed, "no plater to load into");
// Default strategy matches drag-drop / Plater::load_files's own default: it
// routes .3mf as a project and meshes as models based on file content, so no
// as_project flag is needed in v1. ask_multi=false: never prompt.
const LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig;
std::vector<size_t> loaded;
try {
loaded = plater->load_files(paths, strategy, /*ask_multi=*/false);
} catch (const std::exception& e) {
throw AutomationError(kErrLoadFailed,
std::string("load_files failed: ") + e.what());
}
if (loaded.empty())
throw AutomationError(kErrLoadFailed, "load_files loaded nothing");
return static_cast<int>(loaded.size());
});
}
```
Notes for the implementer:
- `LoadStrategy` and its `operator|` (namespace `Slic3r`, from `libslic3r/Format/bbs_3mf.hpp`) are already in scope: `WxUiBackend.cpp` includes `Plater.hpp` (line 7), which transitively pulls in the enum, and this translation unit lives in `Slic3r::GUI::Automation` so unqualified `LoadStrategy` resolves via the enclosing `Slic3r` namespace. No new include is required.
- `Plater::load_files(const std::vector<std::string>&, LoadStrategy, bool)` is the existing string overload (`Plater.hpp:379`) — no `boost::filesystem::path` conversion needed.
- `kErrLoadFailed` comes from `JsonRpcDispatcher.hpp`, already included at line 4.
- An `AutomationError` thrown inside the `run_on_gui` lambda is captured by the helper's `set_exception` and rethrown from `fut.get()`, so the 1007 code propagates to the dispatcher unchanged.
- [ ] **Step 3: Build the full app to verify it compiles and links**
Run: `cmake --build build --config RelWithDebInfo --target OrcaSlicer`
Expected: build succeeds (no missing-symbol / pure-virtual errors; `WxUiBackend` is now concrete).
- [ ] **Step 4: Commit**
```bash
git add src/slic3r/GUI/Automation/WxUiBackend.hpp src/slic3r/GUI/Automation/WxUiBackend.cpp
git commit -m "feat(automation): implement WxUiBackend::open_files via Plater::load_files"
```
---
## Task 5: Python client wrapper `OrcaClient.open`
**Files:**
- Modify: `tools/automation/orca_automation.py:80-82`
- [ ] **Step 1: Add the `open` method**
In `tools/automation/orca_automation.py`, after the `key` method (ends line 82), add:
```python
def key(self, keys) -> dict:
# keys: "ctrl+s" or ["ctrl", "s"]
return self._call("input.key", {"keys": keys})
def open(self, paths) -> dict:
"""Load one or more files into the running instance at runtime.
`paths` is a single absolute path string or a list of them. Paths are read
from the host filesystem by the server (localhost-only). Returns
{"ok": True, "loaded": <count>}. Raises OrcaError 1007 on load failure."""
if isinstance(paths, str):
paths = [paths]
return self._call("file.open", {"paths": list(paths)})
```
- [ ] **Step 2: Smoke-test the wrapper's normalization offline (no server needed)**
Run:
```bash
python -c "import sys; sys.path.insert(0, 'tools/automation'); import orca_automation as m; c = m.OrcaClient.__new__(m.OrcaClient); c._call = lambda meth, params=None: (meth, params); print(c.open('C:/a.stl')); print(c.open(['C:/a.stl','C:/b.stl']))"
```
Expected output:
```
('file.open', {'paths': ['C:/a.stl']})
('file.open', {'paths': ['C:/a.stl', 'C:/b.stl']})
```
- [ ] **Step 3: Commit**
```bash
git add tools/automation/orca_automation.py
git commit -m "feat(automation): add OrcaClient.open() wrapper for file.open"
```
---
## Task 6: Update `example_slice.py` to load at runtime via `file.open`
**Files:**
- Modify: `tools/automation/example_slice.py:26-52`
- [ ] **Step 1: Launch without the model arg, then call `open`**
In `tools/automation/example_slice.py`, change the `subprocess.Popen` call (lines 26-31) to drop the trailing model positional:
```python
proc = subprocess.Popen([
args.orca,
"--automation-server",
f"--automation-server-port={args.port}",
])
```
Then replace the project-load wait block (currently lines 46-51) so the model is loaded at runtime via `file.open` instead of relying on a launch-time positional:
```python
# Load the model into the already-running instance, then wait until the
# project reports loaded. file.open is synchronous, so project_loaded is
# already true on return; the wait is a belt-and-suspenders guard.
orca.open([args.model])
deadline = time.time() + 30
while time.time() < deadline:
if orca.app_state().get("project_loaded"):
break
time.sleep(0.5)
```
- [ ] **Step 2: Byte-compile the script to confirm no syntax errors**
Run: `python -m py_compile tools/automation/example_slice.py`
Expected: no output, exit code 0.
- [ ] **Step 3: Commit**
```bash
git add tools/automation/example_slice.py
git commit -m "docs(automation): example_slice.py loads model at runtime via file.open"
```
---
## Task 7: Document `file.open` in `doc/automation.md`
**Files:**
- Modify: `doc/automation.md` (capabilities example §4 line 111-114; new method subsection after `screenshot.window`; error table §7)
- [ ] **Step 1: Add `file.open` to the capabilities example**
In `doc/automation.md`, update the `automation.version` result example (lines 111-114) to include `file.open`:
```json
"capabilities": [
"tree.dump", "tree.find", "widget.get", "input.click", "input.type",
"input.key", "sync.wait_for", "app.state", "screenshot.window", "file.open"
]
```
The §4 prose count is already written for this: "There are 11 methods … the 10 callable feature methods" now matches exactly (10 capability entries + `automation.version` = 11). Leave that sentence unchanged.
- [ ] **Step 2: Add the `file.open` method subsection**
In `doc/automation.md`, immediately after the `screenshot.window` method subsection (it ends just before the `---` on line 303) and before that `---`, insert:
```markdown
### `file.open`
Load one or more files into the **already-running** instance at runtime, by calling
`Plater::load_files(...)` directly on the GUI thread. This is the supported way to add
or swap a model without relaunching the process. Loading is **synchronous**: when the
call returns `ok: true`, `app.state().project_loaded` is already `true` (no polling
race).
**Params:**
| Param | Type | Required | Meaning |
|---|---|---|---|
| `paths` | string or array of strings | yes | One or more **absolute** file paths. A bare string is accepted and treated as a one-element list. Paths are read from the **host (server) filesystem** — client and server are localhost-only. |
`.3mf` files are routed as projects and meshes as models automatically, based on file
content (the same default strategy as drag-drop); there is no `as_project` flag in v1.
**Result:** `{ "ok": true, "loaded": <int> }`, where `loaded` is the number of objects
added to the scene (`load_files(...).size()`).
**Errors:**
- `-32602` (invalid params) — `paths` is missing, is not a string/array, contains a
non-string entry, or yields no non-empty path.
- `1007` (load failed) — `load_files` returned empty or threw (file not found, parse
error, or unsupported format).
- `1004` (GUI busy) — the GUI-thread marshal timed out. An extremely large model can
exceed the marshal timeout and surface here; documented, not mitigated in v1.
```
- [ ] **Step 3: Add the `1007` row to the error-code table**
In `doc/automation.md` §7, in the application-specific codes table, after the `1006` row (line 395) add:
```markdown
| `1006` | Disabled. |
| `1007` | Load failed — `file.open`'s `load_files` returned empty or threw (not found, parse error, unsupported format). |
```
- [ ] **Step 4: Commit**
```bash
git add doc/automation.md
git commit -m "docs(automation): document file.open method and error 1007"
```
---
## Final verification
- [ ] **Step 1: Full automation unit suite green**
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe --order rand --warn NoAssertions`
Expected: PASS — all cases (pre-existing ~32 + 7 new) green, no `NoAssertions` warnings on the new cases.
- [ ] **Step 2: Full app builds**
Run: `cmake --build build --config RelWithDebInfo --target ALL_BUILD -- -m`
Expected: build succeeds.
- [ ] **Step 3: Manual runtime check (requires a display)**
Launch with `--automation-server` and **no** model arg, then from a Python shell:
```python
from orca_automation import OrcaClient
orca = OrcaClient(port=13619)
print(orca.open(["C:/abs/path/cube.stl"])) # -> {'ok': True, 'loaded': 1}
print(orca.app_state()["project_loaded"]) # -> True
open("window.png","wb").write(orca.screenshot()) # PNG shows the loaded model
```
Expected: `loaded >= 1`, `project_loaded == True`, screenshot shows the model.
- [ ] **Step 4: Gating check (automation OFF is a no-op)**
Confirm by reading: with no `--automation-server` flag, the server/backend/dispatcher are never constructed (`GUI_App.cpp` `start_automation_server()` early-return), so `file.open` is unreachable. No new hot-path cost beyond the existing single bool check. (See `doc/automation.md` §Verification — disabled-path audit; this feature adds no new gating surface.)
---
## Backward compatibility
Additive only: one new method (`file.open`), one new error code (`1007`), one new capabilities entry, and one new backend interface method. No existing method, profile, project-file handling, or default behavior changes. The method is reachable only when `--automation-server` is passed.
File diff suppressed because it is too large Load Diff
@@ -1,117 +0,0 @@
# Design — `file.open` automation method (runtime model loading)
**Date:** 2026-06-03
**Branch:** `feature/automation`
**Status:** Approved for implementation
---
## Problem
Today a model can be loaded into OrcaSlicer **only at process launch**: the model path
is passed as a CLI positional arg and OrcaSlicer's normal startup file-loading ingests
it. The JSON-RPC automation protocol has **no** load method, so swapping or adding a
model in an already-running instance requires a fresh process launch.
Driving the native File→Import dialog via `input.click` is not a viable substitute: the
OS file picker is not a `wxWindow`, so it never appears in the `tree.dump` hierarchy
(`WxUiBackend::dump_tree` walks `wxGetApp().mainframe` children only), and `input.click`
can only target nodes resolved from that tree (no raw-coordinate click). Blind typing via
`input.type` is mechanically possible but unobservable: the native picker is not a
`wxDialog`, so `app.state().modal_dialog` and `sync.wait_for` cannot gate on it, leaving
only sleep-and-hope timing. A direct API method is the clean fix.
## Goal
Add a `file.open` JSON-RPC method that loads one or more files into a running instance by
calling `Plater::load_files(...)` directly on the GUI thread. Out of scope: any
dialog-driving mechanism (intercept hook or true OS-level drive) — explicitly deferred.
---
## Protocol
- **Method:** `file.open`
- **Params:** `{ "paths": ["C:/abs/a.stl", ...] }`
- A bare string is also accepted: `{ "paths": "C:/abs/a.stl" }`.
- Paths must be **absolute**. The server reads them from the host filesystem
(client/server are localhost-only).
- **Result:** `{ "ok": true, "loaded": <count> }`
- `count` is `load_files(...).size()` — the number of objects added to the scene.
- **Errors:**
| Code | Constant | Condition |
|------|----------|-----------|
| -32602 | `kInvalidParams` | `paths` missing/empty, a non-string entry, or no non-empty path |
| 1004 | `kErrGuiBusy` | GUI-thread marshal timed out (`m_gui_timeout_ms`) |
| 1007 | `kErrLoadFailed` | `load_files` returned empty / threw (not found, parse error, unsupported format) — **new code** |
## Semantics — synchronous
`Plater::load_files` runs and completes on the GUI thread. The backend marshals via the
existing `run_on_gui(m_gui_timeout_ms, …)` helper and returns only after the load
finishes. Consequently, when `file.open` returns `ok:true`, `app.state().project_loaded`
is already `true` — there is no polling race.
Rejected alternative — async "fire-and-poll-`project_loaded`": adds client complexity and
loses a definitive per-call error result, with no benefit since loading is synchronous.
**Caveat:** an extremely large model could exceed `m_gui_timeout_ms` and surface as
`1004 kErrGuiBusy`. Documented; not mitigated in v1.
## Load strategy (v1 minimal)
Pass the default `LoadStrategy::LoadModel | LoadStrategy::LoadConfig` (identical to
drag-drop / `Plater::load_files`'s default) with `ask_multi = false`. This already routes
`.3mf` files as projects and meshes as models based on file content, so **no `as_project`
flag is needed in v1**. A future `{ "as_project": bool }` flag remains possible but is not
implemented now.
---
## Components / files to touch
Follows the existing `screenshot_window` / `app_state` method pattern.
1. **`src/slic3r/GUI/Automation/IUiBackend.hpp`** — add pure-virtual
`int open_files(const std::vector<std::string>& paths)` returning the loaded count,
throwing `AutomationError` on failure. Header stays wx-free (no `LoadStrategy` leak).
2. **`src/slic3r/GUI/Automation/WxUiBackend.{hpp,cpp}`** — implement `open_files`:
`run_on_gui(m_gui_timeout_ms, …)``wxGetApp().plater()->load_files(paths, default_strategy, false)`;
throw `kErrLoadFailed` if the returned vector is empty.
3. **`src/slic3r/GUI/Automation/JsonRpcDispatcher.{hpp,cpp}`** —
- add `constexpr int kErrLoadFailed = 1007;`
- declare + define `m_file_open(params)` (param parsing/validation; accept string or
array; require ≥1 non-empty string path)
- add dispatch route `if (method == "file.open") return make_result(id, m_file_open(params));`
- add `"file.open"` to the capabilities array in `m_version`.
4. **`tests/automation/MockUiBackend.hpp`** — `open_files` override recording the paths
vector + a configurable return-count (and a throw/fail knob).
5. **`tests/automation/test_dispatcher.cpp`** — Catch2 v2 tests:
- array of paths → routes to backend, returns `loaded` count
- bare-string path → normalized to one path
- missing/empty `paths``-32602` (`kInvalidParams`)
- backend load failure → `1007`
- `automation.version` capabilities array includes `"file.open"`
6. **`tools/automation/orca_automation.py`** — `open(self, paths)` wrapper (normalize
`str``[str]`, send `file.open`).
7. **`tools/automation/example_slice.py`** — launch **without** a model arg, then
`orca.open([model])`, then wait for `project_loaded`.
8. **`doc/automation.md`** — document method (params/result/errors), add to the
capabilities list, method index, and error table (`1007`).
---
## Testing / verification
- **Build (Windows):** `cmake --build . --config RelWithDebInfo --target ALL_BUILD -- -m`.
- **Unit:** `automation` Catch2 suite green including new tests (≈31 → ≈34 cases).
- **Manual:** launch with `--automation-server` and **no** model arg → call `file.open`
→ confirm `app.state().project_loaded` flips `true` and `screenshot.window` shows the
model.
- **Gating:** unchanged — the server only runs under `--automation-server`, so the method
is a no-op (unreachable) when automation is disabled.
## Backward compatibility
Additive only: a new method, a new error code, and a new capabilities entry. No change to
existing methods, profiles, project-file handling, or default behavior.
@@ -1,314 +0,0 @@
# OrcaSlicer UI Automation — Design Spec
**Date:** 2026-06-03
**Status:** Approved design, pending implementation plan
**Topic:** Add an opt-in, externally-controllable UI automation interface to OrcaSlicer for automated GUI testing and future AI-agent control.
---
## 1. Overview
Add a localhost JSON-RPC server to a running OrcaSlicer GUI instance that lets an
**external** script (or AI agent) drive and observe the real GUI. "Driving" is done
the way a user would — simulated mouse/keyboard via `wxUIActionSimulator` — while
"observing" reads the live widget state and captures screenshots.
The interface must cover **three UI technologies** present in OrcaSlicer:
1. Native **wxWidgets** widgets (`wxWindow` hierarchy).
2. The **OpenGL 3D viewport** (`GLCanvas3D`) — screenshots via the existing
framebuffer/thumbnail path.
3. **Dear ImGui** immediate-mode controls (gizmo panels, in-canvas overlays,
notifications) — recorded as they are drawn, because there is no persistent tree.
## 2. Goals / Non-Goals
### Goals
- Let an external, language-agnostic client connect to a running instance and:
introspect the UI, locate widgets by stable name, perform input actions, wait on
conditions, and capture screenshots (including the 3D view as a separate image).
- Be safe by default: disabled unless explicitly enabled; bound to `127.0.0.1` only.
- Be testable in CI without a display (pure-logic units behind a mock backend).
- Ship a reference Python client, a runnable end-to-end example, protocol docs, and
C++ unit tests.
### Non-Goals (v1)
- No headless/offscreen automation — OS input injection needs a focused, visible
window (Linux CI requires a display, e.g. Xvfb).
- No auth token in v1 (documented future hardening; localhost-only is the boundary).
- No per-item coverage of raw-`ImGui::` gizmos (Emboss/SVG/Text). They get
window-level coverage; per-item is future work.
- No new scripting language embedded in the app; control is purely external over JSON-RPC.
- We do **not** modify the existing auth `HttpServer`.
## 3. Background — existing infrastructure (verified)
- **`wxUIActionSimulator`** is compiled into the wx build and already used
(`src/slic3r/GUI/GUI_ObjectList.cpp:211`). Cross-platform simulated input is available.
- **`GLCanvas3D::render_thumbnail()`** (`src/slic3r/GUI/GLCanvas3D.cpp:2210+`) →
`render_thumbnail_framebuffer()` (`:6352`) renders the 3D scene into a
`ThumbnailData` (RGBA) via an FBO + `glReadPixels`. `debug_output_thumbnail()`
(`:6099`) shows the `ThumbnailData → wxImage → PNG` conversion. This is the
separate-3D-screenshot path. `Plater::generate_thumbnail()`/`generate_thumbnails()`
wrap it.
- **`ImGuiWrapper`** (`src/slic3r/GUI/ImGuiWrapper.hpp`) is the chokepoint for nearly
all ImGui controls: `button`/`bbl_button`, `checkbox`/`bbl_checkbox`, `combo`,
`slider_float`, `input_double`, `radio_button`, `menu_item_with_icon`, plus
`begin`/`end` for windows. `imgui_internal.h` is in-tree, exposing
`ImGui::GetCurrentContext()->Windows`, item rects, and hovered/active id.
- **`HttpServer`** (`src/slic3r/GUI/HttpServer.{hpp,cpp}`, boost::beast, port 13618)
is used for cloud auth. **It cannot serve a POST body**`session::read_body()`
reads and discards the body and never replies (`HttpServer.cpp:57-65`). It is
effectively GET-only. We will **not** reuse or modify it.
- **`OtherInstanceMessageHandler`** (`src/slic3r/GUI/InstanceCheck.{hpp,cpp}`) is the
template for "start a localhost listener once the MainFrame exists, post events into
the GUI." Useful as a structural reference.
- **CLI args** are parsed in `src/OrcaSlicer.cpp` (`CLI::setup`/`CLI::run`) and flow
into the GUI run params consumed by `GUI_App::OnInit()`.
## 4. Architecture
```
External script / AI agent ──► Python client (orca_automation.py)
│ HTTP POST /jsonrpc (JSON-RPC 2.0) on 127.0.0.1:<port>
AutomationServer (dedicated boost::beast listener; own thread;
│ started only when --automation-server is set)
│ parse JSON-RPC envelope
JsonRpcDispatcher (pure logic — method registry; unit-testable)
│ marshal each call to the GUI thread via wxGetApp().CallAfter
│ + std::promise/future with a per-request timeout
IUiBackend (interface) ──► WxUiBackend (real, GUI thread) / MockBackend (tests)
├─ Introspection : walk wxWindow tree + read ImGui item table → unified JSON
├─ Locator : resolve automation-id / predicate → wx widget or ImGui item
├─ Actions : raise window, then wxUIActionSimulator click/type/key
├─ Sync : wait_for (poll condition) + app.state snapshot
└─ Screenshots : wx widget → wxDC→PNG ; 3D view → render_thumbnail()→PNG
```
### Components (new files unless noted)
All new code lives under `src/slic3r/GUI/Automation/`.
| Component | Responsibility |
|---|---|
| `AutomationServer.{hpp,cpp}` | Dedicated boost::beast HTTP listener with **POST + body** support; one `POST /jsonrpc` endpoint; returns `application/json`. Localhost-only. Own thread. |
| `JsonRpcDispatcher.{hpp,cpp}` | Parse JSON-RPC 2.0; route `method` → handler; build result/error. Depends only on `IUiBackend`. No wx/ImGui includes → unit-testable. |
| `IUiBackend.hpp` | Abstract interface: `dump_tree`, `find`, `get_widget`, `click`, `type`, `key`, `wait_for`, `app_state`, `screenshot_window`, `screenshot_viewport3d`. Uses plain structs (no wx types) so tests can mock it. |
| `WxUiBackend.{hpp,cpp}` | Real implementation. Runs on GUI thread. Walks `wxWindow` tree, reads the ImGui item table, drives `wxUIActionSimulator`, captures screenshots. |
| `MockUiBackend.{hpp,cpp}` (tests) | Deterministic fake tree + recorded actions for unit tests. |
| `AutomationRegistry.{hpp,cpp}` | Process-wide `wxWindow* → automation_id` map + reverse lookup; `set_automation_id(win, "id")` helper. Header is dependency-light so widget-construction code can call the helper unconditionally (it is a cheap no-op-safe registration). |
| `WidgetSerializer.{hpp,cpp}` | `wxWindow` → JSON node (name/id, class, label, screen-rect, enabled, shown, value via RTTI). |
| `ImGuiItemTable.{hpp,cpp}` | Per-frame recorder of ImGui items + live-window enumeration. Populated from `ImGuiWrapper`; read on GUI thread. |
### Touch points in existing files
- `src/slic3r/GUI/ImGuiWrapper.cpp` — add recording hooks inside the wrapped widget
methods and `begin`/`end`, **guarded by an `is_automation_enabled()` flag** so there
is zero overhead and zero behavior change when automation is off.
- `src/slic3r/GUI/GUI_App.{hpp,cpp}` — own the `AutomationServer`; start it in
`OnInit()` only when the flag is set; stop it on exit. Expose
`is_automation_enabled()`.
- `src/OrcaSlicer.cpp` — parse `--automation-server[=PORT]`; pass through GUI run params.
- A handful of widget-construction sites (Slice/Export buttons, preset combos, main
tabs, common dialog OK/Cancel, the 3D canvas) — add `set_automation_id(...)` calls
(~15-20 widgets in v1).
- CMake: add the new `Automation/` sources to the GUI target; add the unit-test target.
## 5. Transport & Protocol
- **Transport:** HTTP/1.1 on `127.0.0.1:<port>` (default **13619**, adjacent to the
auth server's 13618). Single endpoint: `POST /jsonrpc`, body is a JSON-RPC 2.0
request, response is a JSON-RPC 2.0 result/error. `GET /` returns a tiny health/version page.
- **Protocol:** JSON-RPC 2.0. `id`, `method`, `params`. Batch not required in v1.
### v1 methods
| Method | Params | Result |
|---|---|---|
| `automation.version` | — | `{version, protocol, capabilities[]}` |
| `tree.dump` | `{root?, max_depth?, visible_only?, include_imgui?}` | tree of nodes (wx + imgui) |
| `tree.find` | `{name?, class?, label?, value?, backend?}` | `[node...]` matches |
| `widget.get` | `{target}` | single node detail |
| `input.click` | `{target, button?=left, double?=false, modifiers?[]}` | `{ok}` |
| `input.type` | `{target?, text}` | `{ok}` |
| `input.key` | `{keys}` e.g. `"ctrl+s"` or `["ctrl","s"]` | `{ok}` |
| `sync.wait_for` | `{target, state: exists\|visible\|enabled\|value, value?, timeout_ms?=5000, poll_ms?=100}` | `{ok, elapsed_ms}` |
| `app.state` | — | `{active_tab, project_loaded, slicing, slice_progress, modal_dialog?, foreground}` |
| `screenshot.window` | `{target?}` (default main frame) | `{png_base64, width, height}` |
| `screenshot.viewport3d` | `{plate?, width?, height?}` | `{png_base64, width, height}` |
### Node shape (unified for wx and ImGui)
```json
{
"backend": "wx" | "imgui",
"id": "btn_slice", // automation id if set, else derived path id
"path": "MainFrame/.../btn_slice", // stable-ish positional path
"class": "Button", // wx class name or imgui item type
"label": "Slice plate",
"rect": { "x": 100, "y": 200, "w": 120, "h": 32 }, // screen coords
"enabled": true,
"visible": true,
"value": "PLA", // when applicable (text/choice/check/slider)
"children": [ ... ] // wx only; imgui items are flat under their window
}
```
### Error model (JSON-RPC `error.code`)
- `-32700` parse error, `-32601` method not found, `-32602` invalid params (standard).
- Application codes: `1001` widget/target not found, `1002` target not actionable
(disabled/hidden), `1003` wait timeout, `1004` GUI thread busy/timeout,
`1005` screenshot failed, `1006` automation feature disabled.
## 6. Threading model
- `AutomationServer` runs on its own thread and accepts connections; the dispatcher
parses on that thread.
- **Every** call touching wx/ImGui/GL is marshaled to the GUI thread with
`wxGetApp().CallAfter([...]{ ... })`; the server thread blocks on a `std::future`
with a per-request timeout (default 5 s; `wait_for` uses its own larger budget).
Timeout → error `1004`.
- This is mandatory: wx widgets, the ImGui context, and the GL context are not
thread-safe and are owned by the GUI thread.
- `CallAfter` is serviced even while modal dialogs run (nested event loop), so
automation can interact with dialogs.
## 7. Widget locator & automation IDs (wxWidgets)
- **Stable IDs:** `set_automation_id(window, "btn_slice")` registers the widget in
`AutomationRegistry`. Stored in a side map keyed by `wxWindow*` (not via `SetName`,
to avoid any coupling with wx's name-based lookups). Registration is removed on
widget destruction (bind to `wxEVT_DESTROY` or prune lazily on lookup).
- **Derived IDs:** for un-instrumented widgets, `WidgetSerializer` derives a positional
`path` (e.g. `MainFrame/Panel[2]/Button[0]`) so an AI agent can still target anything.
Named IDs are the preferred, stable path.
- **Locator resolution order:** exact automation id → exact path → predicate match
(name/class/label/value). Ambiguous matches return the list via `tree.find`; action
methods require a unique match or error `1001`.
- **v1 instrumented widgets (~15-20):** Slice/Export buttons, printer & filament preset
combos, the main tab buttons (`tp3DEditor`/`tpPreview`/`tpMonitor`/…), Add/Import,
common dialog OK/Cancel/Yes/No, the `GLCanvas3D` itself.
## 8. ImGui coverage (v1 = wrapper items + window introspection)
- **Item recording:** inside each `ImGuiWrapper` wrapped method, when
`is_automation_enabled()` is true, append the just-drawn item to a per-frame
`ImGuiItemTable` entry: `{window_name, label/id, type, rect, enabled, value}`.
Item rect comes from `ImGui::GetItemRectMin/Max()` (ImGui display coords) mapped to
**screen** coords via the `GLCanvas3D` client origin (`ClientToScreen`) and DPI scale.
- **Window enumeration:** via `imgui_internal.h`, enumerate `GetCurrentContext()->Windows`
for window name, rect, visibility, plus the global hovered/active item id.
- **Double-buffering:** the table is swapped at frame end (`ImGuiWrapper::render`) so
readers see a complete frame. Reads happen on the GUI thread (after marshaling), same
thread as rendering, so a simple front/back swap suffices.
- **Freshness:** because items exist only while drawn, before an ImGui tree read or
action the backend forces a canvas refresh and flushes events so the latest frame is
captured. `sync.wait_for` can poll for an ImGui item to appear (e.g. after opening a
gizmo).
- **Actions:** an ImGui target resolves to its recorded screen rect; `input.click`/
`input.type` use `wxUIActionSimulator` on that rect — identical action path to wx,
different rect source. Typing into an ImGui input works because simulated keystrokes
flow through the existing `ImGuiWrapper::update_key_data` bridge once the field is
focused by a click.
- **Limitation (documented):** raw-`ImGui::` gizmos (Emboss, SVG, Text) are covered at
the **window** level only in v1; per-item instrumentation is future work.
## 9. Screenshots
- **`screenshot.window`:** capture a `wxWindow` (default: main frame) via
`wxClientDC`/`wxWindowDC``wxBitmap``wxImage` → PNG → base64. Works for native
widgets but **not** for the GL canvas region (returns black there) — hence the
separate 3D method.
- **`screenshot.viewport3d`:** reuse `GLCanvas3D::render_thumbnail()` (FBO +
`glReadPixels`) → `ThumbnailData``wxImage` (per `debug_output_thumbnail`) → PNG →
base64. Optional `plate`, `width`, `height` params. Runs on the GUI thread with the GL
context current.
## 10. Activation & security
- **Off by default.** Enabled by CLI flag `--automation-server[=PORT]` (default port
13619). (An app-config/Preferences toggle may be added later; v1 is flag-only.)
- **Bind `127.0.0.1` only.** No external interface.
- **No token in v1** (per decision); documented as a recommended future hardening,
along with an optional `--automation-token`.
- When disabled: no listener, no thread, and all `ImGuiWrapper` recording hooks are
skipped — **zero** runtime overhead and **zero** behavior change. This satisfies the
project's "features gated by options must not affect existing behavior when disabled"
constraint.
## 11. Testability
- `JsonRpcDispatcher` depends only on `IUiBackend` and has **no** wx/ImGui/GL includes.
- **C++ unit tests (Catch2), display-free, run in CI:**
- JSON-RPC envelope parse/validate/dispatch (good + malformed input, error codes).
- Method routing and param validation for every v1 method against `MockUiBackend`.
- `WidgetSerializer` node shape (fed a synthetic node model, not real wx widgets).
- Locator resolution: exact id, path, predicate, ambiguity, not-found.
- The only piece needing a real GUI is `WxUiBackend`; it is exercised by the manual
end-to-end example, not by CI unit tests.
## 12. Deliverables
- **C++:** the `Automation/` components, `ImGuiWrapper` recording hooks, widget
instrumentation, CLI flag plumbing, `GUI_App` lifecycle, CMake wiring.
- **`tools/automation/orca_automation.py`:** reference Python client wrapping the
JSON-RPC calls (`connect`, `version`, `dump_tree`, `find`, `click`, `type`, `key`,
`wait_for`, `app_state`, `screenshot`, `screenshot_3d`).
- **`tools/automation/example_slice.py`:** runnable end-to-end flow — launch OrcaSlicer
with the flag, load a model, click Slice, `wait_for` completion, save a 3D-preview PNG.
Doubles as a manual smoke test.
- **`doc/automation.md`:** protocol reference (methods, params, results, error codes),
node shape, automation-id naming conventions, ImGui notes, platform/display caveats.
- **`tests/`:** Catch2 unit-test target for the dispatch/serialize/locator logic.
## 13. New / changed file inventory
**New**
- `src/slic3r/GUI/Automation/AutomationServer.{hpp,cpp}`
- `src/slic3r/GUI/Automation/JsonRpcDispatcher.{hpp,cpp}`
- `src/slic3r/GUI/Automation/IUiBackend.hpp`
- `src/slic3r/GUI/Automation/WxUiBackend.{hpp,cpp}`
- `src/slic3r/GUI/Automation/AutomationRegistry.{hpp,cpp}`
- `src/slic3r/GUI/Automation/WidgetSerializer.{hpp,cpp}`
- `src/slic3r/GUI/Automation/ImGuiItemTable.{hpp,cpp}`
- `tools/automation/orca_automation.py`
- `tools/automation/example_slice.py`
- `doc/automation.md`
- `tests/automation/` (Catch2 target) + `MockUiBackend.{hpp,cpp}`
**Changed**
- `src/slic3r/GUI/ImGuiWrapper.cpp` (guarded recording hooks)
- `src/slic3r/GUI/GUI_App.{hpp,cpp}` (server lifecycle, `is_automation_enabled()`)
- `src/OrcaSlicer.cpp` (CLI flag)
- ~15-20 widget-construction sites (`set_automation_id`)
- `src/slic3r/GUI/CMakeLists.txt` + `tests/CMakeLists.txt`
## 14. Known constraints & limitations
- OS input injection requires the OrcaSlicer window **focused and visible**; the backend
raises/focuses the main window before injecting. Linux CI needs a display (Xvfb).
- Input is asynchronous at the OS level; correctness relies on `sync.wait_for` rather
than fixed sleeps.
- ImGui items are only addressable while their host panel is drawn.
- Raw-`ImGui::` gizmos: window-level only in v1.
- Single-client assumption in v1 (serialized request handling); no concurrent sessions
contract.
## 15. Future work (out of scope for v1)
- Optional auth token + Preferences toggle.
- WebSocket channel for server-push events (slice progress, dialog-appeared).
- Per-item instrumentation for raw-`ImGui::` gizmos.
- An MCP server wrapping the JSON-RPC client for direct AI-agent integration.
- Optional integration of Dear ImGui Test Engine for deterministic ImGui interaction.
## 16. Verification plan
- **CI:** Catch2 unit tests (dispatch/serialize/locator) pass with no display.
- **Manual / e2e:** run `tools/automation/example_slice.py` against a built OrcaSlicer
launched with `--automation-server`; confirm model loads, Slice runs, `wait_for`
returns on completion, and both a wx-window PNG and a 3D-viewport PNG are produced.
- **Regression:** build and run with automation **off**; confirm no new threads, no
listener, and ImGui rendering is byte-for-byte unchanged (hooks compiled out of the
hot path via the disabled flag).
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+21
View File
@@ -48,9 +48,12 @@ src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp
src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.h
src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp
src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
@@ -58,6 +61,7 @@ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
@@ -67,7 +71,9 @@ src/slic3r/GUI/Gizmos/GLGizmoText.hpp
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp
src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp
src/slic3r/GUI/GUI.cpp
src/slic3r/GUI/GUI_App.cpp
src/slic3r/GUI/GUI_AuxiliaryList.cpp
@@ -89,6 +95,8 @@ src/slic3r/GUI/Widgets/AMSControl.cpp
src/slic3r/GUI/Widgets/FanControl.cpp
src/slic3r/GUI/Widgets/FilamentLoad.cpp
src/slic3r/GUI/Widgets/TempInput.cpp
src/slic3r/GUI/Widgets/CheckList.cpp
src/slic3r/GUI/Widgets/SwitchButton.cpp
src/slic3r/GUI/ImGuiWrapper.cpp
src/slic3r/GUI/Jobs/ArrangeJob.cpp
src/slic3r/GUI/Jobs/OrientJob.cpp
@@ -98,6 +106,7 @@ src/slic3r/GUI/Jobs/BindJob.cpp
src/slic3r/GUI/Jobs/PrintJob.cpp
src/slic3r/GUI/Jobs/SendJob.cpp
src/slic3r/GUI/Jobs/EmbossJob.cpp
src/slic3r/GUI/Jobs/PlaterWorker.hpp
src/slic3r/GUI/ThermalPreconditioningDialog.cpp
src/slic3r/GUI/ThermalPreconditioningDialog.hpp
src/slic3r/GUI/Jobs/SLAImportJob.cpp
@@ -237,8 +246,12 @@ src/slic3r/GUI/FilamentMapPanel.cpp
src/slic3r/Utils/Obico.cpp
src/slic3r/Utils/SimplyPrint.cpp
src/slic3r/Utils/Flashforge.cpp
src/slic3r/Utils/ElegooLink.cpp
src/slic3r/Utils/CrealityPrint.cpp
src/slic3r/Utils/PrintHost.cpp
src/slic3r/GUI/Jobs/OAuthJob.cpp
src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp
src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp
src/slic3r/GUI/PartSkipDialog.cpp
src/slic3r/GUI/PartSkipDialog.hpp
src/slic3r/GUI/SkipPartCanvas.cpp
@@ -250,4 +263,12 @@ src/slic3r/GUI/NetworkPluginDialog.cpp
src/slic3r/GUI/RammingChart.cpp
src/slic3r/GUI/StepMeshDialog.cpp
src/slic3r/GUI/FilamentPickerDialog.hpp
src/slic3r/GUI/PresetBundleDialog.cpp
src/slic3r/GUI/ExportPresetBundleDialog.cpp
src/slic3r/GUI/DesktopIntegrationDialog.cpp
src/slic3r/GUI/Downloader.cpp
src/slic3r/GUI/DownloaderFileGet.cpp
src/slic3r/GUI/FileArchiveDialog.cpp
src/slic3r/GUI/PrinterCloudAuthDialog.cpp
src/slic3r/GUI/PrinterWebViewHandler.cpp
src/libslic3r/PresetBundle.cpp
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -146,16 +146,16 @@ text = Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned
text = Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts.
[hint:Speed up your print with Adaptive Layer Height]
text = Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!
text = Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster by using the Adaptive Layer Height option? Check it out!
[hint:Support painting]
text = Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it.
[hint:Different types of supports]
text = Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!
text = Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models while saving filament and improving print speed. Check them out!
[hint:Printing Silk Filament]
text = Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results.
text = Printing Silk Filament\nDid you know that Silk filament needs special consideration to print successfully? A higher temperature and lower speed are always recommended for the best results.
[hint:Brim for better adhesion]
text = Brim for better adhesion\nDid you know that when printed models have a small contact interface with the printing surface, it's recommended to use a brim?
@@ -167,13 +167,13 @@ text = Set parameters for multiple objects\nDid you know that you can set slicin
text = Stack objects\nDid you know that you can stack objects as a whole one?
[hint:Flush into support/objects/infill]
text = Flush into support/objects/infill\nDid you know that you can reduce wasted filament by flushing it into support/objects/infill during filament change?
text = Flush into support/objects/infill\nDid you know that you can reduce wasted filament by flushing it into support/objects/infill during filament changes?
[hint:Improve strength]
text = Improve strength\nDid you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?
[hint:When do you need to print with the printer door opened]
text = When do you need to print with the printer door opened?\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature? More info about this in the Wiki.
text = When do you need to print with the printer door opened?\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature? There is more info about this in the Wiki.
[hint:Avoid warping]
text = Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping?
@@ -45,7 +45,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"10"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -51,7 +51,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"12"
],
"filament_retraction_length": [
"0.8"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"12"
],
"filament_retraction_length": [
"0.8"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"12"
],
"filament_type": [
"PETG"
@@ -39,7 +39,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"16"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -45,7 +45,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"16"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -45,7 +45,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"16"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1.8"
"12"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"16"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -45,7 +45,7 @@
"1"
],
"filament_max_volumetric_speed": [
"3"
"6"
],
"filament_retract_before_wipe": [
"100%"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"4"
"10"
],
"filament_retract_before_wipe": [
"100%"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"4"
"10"
],
"filament_retract_before_wipe": [
"100%"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"4"
"10"
],
"filament_retract_before_wipe": [
"100%"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"4"
"10"
],
"filament_retract_before_wipe": [
"100%"
@@ -48,7 +48,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"16"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -48,7 +48,7 @@
"1"
],
"filament_max_volumetric_speed": [
"3"
"6"
],
"filament_retract_before_wipe": [
"100"
@@ -57,7 +57,7 @@
"0"
],
"filament_max_volumetric_speed": [
"1"
"10"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -51,7 +51,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"23"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -54,7 +54,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"18"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -51,7 +51,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"18"
],
"filament_retract_before_wipe": [
"100"
@@ -39,7 +39,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"21"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -45,7 +45,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"21"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -51,7 +51,7 @@
"0"
],
"filament_max_volumetric_speed": [
"2"
"21"
],
"filament_minimal_purge_on_wipe_tower": [
"15"
@@ -124,7 +124,7 @@
"default_filament_profile": [
"Creality HF Generic PLA"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"scan_first_layer": "0",
"thumbnails": [
@@ -124,7 +124,7 @@
"default_filament_profile": [
"Creality HF Generic PLA"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"scan_first_layer": "0",
"thumbnails": [
@@ -124,7 +124,7 @@
"default_filament_profile": [
"Creality HF Generic PLA"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"scan_first_layer": "0",
"thumbnails": [
@@ -54,7 +54,7 @@
"machine_pause_gcode": "PAUSE",
"machine_platform_motion_enable": "1",
"machine_ptc_exist": "1",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_hrc": "0",
@@ -123,7 +123,7 @@
"default_filament_profile": [
"Creality Generic PLA @K1-all"
],
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"scan_first_layer": "0",
"thumbnails_format": "PNG",
@@ -53,7 +53,7 @@
"machine_min_travel_rate": "0,0",
"machine_pause_gcode": "PAUSE",
"machine_platform_motion_enable": "1",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_hrc": "0",
@@ -54,7 +54,7 @@
"machine_pause_gcode": "PAUSE",
"machine_platform_motion_enable": "1",
"machine_ptc_exist": "1",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_hrc": "0",
@@ -54,7 +54,7 @@
"machine_pause_gcode": "PAUSE",
"machine_platform_motion_enable": "1",
"machine_ptc_exist": "1",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_hrc": "0",
@@ -88,7 +88,8 @@
"5"
],
"machine_pause_gcode": "PAUSE",
"machine_start_gcode": ";SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y130 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y130 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X130 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_type": "hardened_steel",
@@ -86,8 +86,8 @@
"5"
],
"machine_pause_gcode": "PAUSE",
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"manual_filament_change": "0",
"nozzle_type": "hardened_steel",
"nozzle_volume": "183",
@@ -86,7 +86,8 @@
"5"
],
"machine_pause_gcode": "PAUSE",
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_type": "hardened_steel",
@@ -86,7 +86,8 @@
"5"
],
"machine_pause_gcode": "PAUSE",
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_type": "hardened_steel",
@@ -138,7 +138,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
@@ -138,7 +138,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
@@ -138,7 +138,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
@@ -138,7 +138,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
@@ -138,8 +138,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": ";SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\n\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "END_PRINT",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
@@ -136,7 +136,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
@@ -136,7 +136,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
@@ -136,7 +136,8 @@
"default_filament_profile": [
"Creality Generic PLA @K2-all"
],
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
"machine_pause_gcode": "PAUSE",
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
@@ -53,7 +53,8 @@
"machine_min_travel_rate": "0,0",
"machine_pause_gcode": "PAUSE",
"machine_platform_motion_enable": "1",
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
"machine_unload_filament_time": "0",
"manual_filament_change": "0",
"nozzle_hrc": "0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "Elegoo",
"version": "02.04.00.02",
"version": "02.04.00.04",
"force_update": "0",
"description": "Elegoo configurations",
"machine_model_list": [
@@ -62,7 +62,7 @@
"change_filament_gcode": "",
"machine_pause_gcode": "M600",
"support_multi_filament": "0",
"layer_change_gcode": "SET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
"machine_start_gcode": ";;===== date: 20251011 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X-1.2 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X-0.5 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"layer_change_gcode": "SET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
"machine_start_gcode": ";;===== date: 20251011 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X-1.2 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X-0.5 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"machine_end_gcode": ";===== date: 20250603 =====================\nM400 ; wait for buffer to clear\nM140 S0 ;Turn-off bed\nM106 S255 ;Cooling nozzle\nM83\nG92 E0 ; zero the extruder\nG1 E-0.8 F1800\nG2 I1 J0 Z{max_layer_z+0.5} F3000 ; lower z a little\nG90\n{if max_layer_z > 100}G1 Z{min(max_layer_z+50, printable_height+0.5)} F20000{else}G1 Z100 F20000 {endif}; Move print head up \nM204 S5000\nM400\nM83\nG1 X202 F20000\nM400\nG1 Y250 F20000\nG1 Y264.5 F1200\nM400\nG92 E0\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off remote part cooling fan\nM106 P3 S0 ; turn off chamber cooling fan\nM84 ;Disable all steppers"
}
@@ -66,7 +66,7 @@
"change_filament_gcode": "\n;==========CC_CHANGE_FILAMENT_GCODE_CCB==========\n;===== date: 2025-12-04-001 =====================\nM106 S0\nM106 P2 S0\nG1 Z{min(max_layer_z+3, printable_height+0.5)} F1200\nM6211 T[next_extruder] L[flush_length] M{old_filament_e_feedrate} N{new_filament_e_feedrate} Q[old_filament_temp] R[nozzle_temperature_range_high] S[new_filament_temp]\nT[next_extruder]\n;==========CC_CHANGE_FILAMENT_GCODE_CCE==========",
"machine_pause_gcode": "M600",
"support_multi_filament": "1",
"layer_change_gcode": "SET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
"machine_start_gcode": ";===== CC_START_GCODE ================\n;===== date: 2026-04-28-001 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nM83\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\n\n\nG90\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X0 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X0.9 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"layer_change_gcode": "SET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
"machine_start_gcode": ";===== CC_START_GCODE ================\n;===== date: 2026-04-28-001 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nM83\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\n\n\nG90\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X0 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X0.9 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"machine_end_gcode": ";===== CC_END_GCODE ================\n;===== date: 2025-12-04-001 =====================\nM400 ; wait for buffer to clear\nM140 S0 ;Turn-off bed\nM106 S255 ;Cooling nozzle\nM83\nG92 E0 ; zero the extruder\nG1 E-0.8 F1800\nG2 I1 J0 Z{max_layer_z+0.5} F3000 ; lower z a little\nG90\n{if max_layer_z > 100}G1 Z{min(max_layer_z+50, printable_height+0.5)} F20000{else}G1 Z100 F20000 {endif}; Move print head up \nM204 S5000\nM749\nM204 S5000\nM400\nM83\nG1 X202 F20000\nM400\nG1 Y250 F20000\nG1 Y264.5 F1200\nM400\nG92 E0\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off remote part cooling fan\nM106 P3 S0 ; turn off chamber cooling fan\nM84 ;Disable all steppers"
}
@@ -86,9 +86,9 @@
"9"
],
"change_filament_gcode": "\n;==========CC2_CHANGE_FILAMENT_GCODE==========\n;===== date: 2026-01-16-001 =====================\nM106 S0\nM106 P2 S0\nG1 Z{min(max_layer_z+3, printable_height+0.5)} F1200\nM6211 T[next_extruder] L[flush_length] M{old_filament_e_feedrate} N{new_filament_e_feedrate} Q[old_filament_temp] R[nozzle_temperature_range_high] S[new_filament_temp]\nT[next_extruder]\n",
"layer_change_gcode": "M73 L{layer_num+1}\n;LAYER:{layer_num+1}\nSET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
"layer_change_gcode": "M73 L{layer_num+1}\n;LAYER:{layer_num+1}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
"machine_end_gcode": ";===== CC2_END_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\n\nM140 S0 ;Turn-off bed\nM83\nG92 E0 ; zero the extruder\nG1 E-1.5 F1800\nG2 I0 J1 Z{max_layer_z+0.5} F3000 ; lower z a little\nM106 S0\nM106 P2 S0\nG90\n{if max_layer_z > 75}G1 Z{min(max_layer_z+5, printable_height+0.5)} F20000{else}G1 Z80 F20000 {endif}; Move print head up \nG180 S9\nM104 S0\nM84\n",
"machine_start_gcode": ";===== CC2_START_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\nG90\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nM190 S[bed_temperature_initial_layer_single] A\nM106 S0\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 FROM_SLICER=1\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\nG28\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n{if first_layer_print_min[1] > 0.5}\nG180 S7\nG1 X{print_bed_max[0]*0.5-1} Y-1.2 F20000\nG1 Z0.5 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 E6 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nM106 S200\nG1 X{print_bed_max[0]*0.5-41} E20 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nG1 F6000\nG1 X{print_bed_max[0]*0.5-46} E0.8\n{else}\nG1 E30 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))}\n{endif}\nM106 S0\nG180 S8\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"machine_start_gcode": ";===== CC2_START_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\nG90\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nM190 S[bed_temperature_initial_layer_single] A\nM106 S0\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 FROM_SLICER=1\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\nG28\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n{if first_layer_print_min[1] > 0.5}\nG180 S7\nG1 X{print_bed_max[0]*0.5-1} Y-1.2 F20000\nG1 Z0.5 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 E6 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nM106 S200\nG1 X{print_bed_max[0]*0.5-41} E20 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nG1 F6000\nG1 X{print_bed_max[0]*0.5-46} E0.8\n{else}\nG1 E30 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))}\n{endif}\nM106 S0\nG180 S8\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
"retract_restart_extra_toolchange": [
"0.5"
]
@@ -2,5 +2,6 @@
"inherits": "0.20mm Standard @Elegoo Giga 0.4 nozzle",
"layer_height": "0.16",
"instantiation": "true",
"renamed_from": "0.16mm Optimal @EOS Giga 0.4 nozzle",
"name": "0.16mm Optimal @Elegoo Giga 0.4 nozzle"
}
@@ -2,6 +2,7 @@
"inherits": "0.30mm Standard @Elegoo Giga 0.6 nozzle",
"layer_height": "0.18",
"instantiation": "true",
"renamed_from": "0.18mm Fine @EOS Giga 0.6 nozzle",
"name": "0.18mm Fine @Elegoo Giga 0.6 nozzle",
"compatible_printers": [
"Elegoo OrangeStorm Giga 0.6 nozzle"
@@ -5,6 +5,7 @@
"from": "system",
"setting_id": "PEOSG04020",
"instantiation": "true",
"renamed_from": "0.20mm Standard @EOS Giga 0.4 nozzle",
"default_acceleration": "3000",
"filename_format": "EOGiga1_{nozzle_diameter[0]}_{input_filename_base}_{filament_name}_{layer_height}_{print_time}.gcode",
"initial_layer_print_height": "0.25",
@@ -4,5 +4,6 @@
"wall_sequence": "inner-outer-inner wall",
"reduce_crossing_wall": "1",
"instantiation": "true",
"renamed_from": "0.20mm Strength @EOS Giga 0.4 nozzle",
"wall_loops": "6"
}
@@ -2,5 +2,6 @@
"inherits": "0.20mm Standard @Elegoo Giga 0.4 nozzle",
"layer_height": "0.24",
"instantiation": "true",
"renamed_from": "0.24mm Draft @EOS Giga 0.4 nozzle",
"name": "0.24mm Draft @Elegoo Giga 0.4 nozzle"
}

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