Commit Graph
18842 Commits
Author SHA1 Message Date
Kris AustinandRodrigo Faselli 213c6569ab cut the GPU load of moving the mouse over the 3D viewport (#15674)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-18 20:19:37 -03:00
Kris Austin e0410db22e fix: hide the CAD gizmos unless the experimental CAD feature is enabled (#15762) 2026-09-18 18:38:55 -03:00
packerlschupfer f9e9cff53a calib: defensive guards in find_optimal_PA_speed (fix CLI pa-tower SIGSEGV) (#14414) 2026-09-18 14:26:14 -03:00
Ian Bassi 8a17df4a89 Fix some overhang detection (#15694) 2026-09-18 14:23:38 -03:00
SoftFever 60ebbf7daa Stop estimating a prime tower for a single used filament
The no-tower case was gated on there being no purge volume, but the SEMM flush matrix reads every configured slot and is nonzero even when only one filament is used, so the plater preview drew a tower the print would never contain.
2026-09-19 00:38:22 +08:00
Kiss Lorandandyw4z e573fc4680 Align Publish 3MF dialog styling and fix tab-switch flicker (#15695)
* Fix Publish 3MF dialog styling

Use Orca's shared checkbox widget in the Publish 3MF dialog. Keep checkbox labels clickable, preserve toggle event propagation and disabled-row state, and use Windows-only double buffering on the tab page hosts to reduce flicker during page switches.

Also align the dialog's guide-link color with existing Orca dialogs.

* Fix Publish 3MF dialog styling

Use Orca's shared checkbox widget in the Publish 3MF dialog. Keep checkbox labels clickable, preserve toggle event propagation and disabled-row state, and use Windows-only double buffering on the tab page hosts to reduce flicker during page switches.

Also align the dialog's guide-link color with existing Orca dialogs.

* match font size and left margins

---------

Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-09-18 19:25:53 +03:00
yw4zandNoisyfox c5f9257878 Compact bbl nozzle UI (#15083)
* init

* drop usage of StaticGroup for ExtruderGroup

* completely remove StaticGroup from project

* fix alignment of "Not installed" text

* fix crash on linux while clicking edit button

* Fix background color on macOS

---------

Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
2026-09-18 23:43:42 +08:00
SoftFever 3f001489bf Speed Dial Enhancements (#15562) 2026-09-18 23:21:19 +08:00
Kris Austin 7472b87b67 build: fix the macro redefinition that fails clang Debug builds (#15759)
libslic3r_version.h defines ORCA_CHECK_GCODE_PLACEHOLDERS from the CMake
option, 0 by default, and GCode.hpp then forces it to 1 whenever NDEBUG
is undefined. Clang reports the second definition under
-Wmacro-redefined, which is on by default, so since -Werror (#15660)
every clang Debug build fails at the libslic3r files that include
GCode.hpp. Release and RelWithDebInfo define NDEBUG and never compile
the override, which is why no CI configuration sees it. The override
dates from #3861 and has warned in every Debug build since.

Undefine the macro before overriding it. Debug builds get the same
value the redefinition already produced, so nothing else changes.

Follow-up to #15749, refs #15748.
2026-09-18 11:56:44 -03:00
CliffordandClaude Opus 5 83e729d20e fix(build): drop two unused lambda captures that break non-Release clang builds (#15749)
fix(build): drop two unused lambda captures that break non-Release builds

Both handlers capture this and never use it. They sit inside
#if !BBL_RELEASE_TO_PUBLIC, which CMakeLists defines as $<CONFIG:Release>, so
the code compiles in Debug and RelWithDebInfo but not in Release. Clang warns
on an unused capture under -Wall, and since every warning became an error the
two captures fail any clang build that is not Release - an Xcode scheme on its
default Debug configuration, for instance. The CI matrix builds Release, where
the block does not exist, and GCC does not implement the warning at all, so
nothing in CI can see it.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:42:31 -03:00
Ian Chua ce41b5a3cb fix: stale audit mode on_lifecycle_event 2026-09-18 22:33:46 +08:00
Ian Chua 8fd93907fd Merge branch 'main' into feat/plugin-lifecycle-evts 2026-09-18 21:35:09 +08:00
Ian Chua 080f27f602 fix: windows unsubscribed loaded plugin not allowed (#15751)
# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->
On windows, after installing and loading a plugin, if you try to
unsubscribe from a loaded plugin, and on refresh, it will be an orphaned
plugin.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
On unsubscribing from a loaded cloud plugin
<img width="1418" height="862" alt="image"
src="https://github.com/user-attachments/assets/9ba8353c-1645-4b21-86ff-2ed235f4d259"
/>

On plugin refresh
<img width="1418" height="862" alt="image"
src="https://github.com/user-attachments/assets/fd58c144-7970-4448-ba3b-e72e52980467"
/>

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-09-18 21:32:57 +08:00
Lam Wei Lun 4ea50a33e4 Testing macOS fixes 2026-09-18 19:07:03 +08:00
Ian Chua 23c77f15cf feat: add CI to generate OPC for OTA workflow (#15624)
# Description

Adds the CI half of the profile OTA pipeline: a push-triggered workflow
that
rebuilds a vendor's binary preset cache (`<vendor>.opc`) whenever its
profile
changes on `main` / `release/*`, and publishes it as a versioned release
asset
for OrcaCloud's OTA Manager to pick up.

### `.github/workflows/post_merge_profiles.yml` (new)

Push-triggered counterpart to `check_profiles.yml` (which only gates
PRs):

- Diffs the push to find which vendors under `resources/profiles/**`
changed.
- Reads the Orca version from `version.inc` and each vendor's 4-part
`version`
from `resources/profiles/<vendor>.json` (fails the run if it isn't
`A.B.C.D`).
- Downloads the prebuilt `generate_system_cache` from this repo's
`nightly-builds` release and builds one `<vendor>.opc` per changed
vendor.
- Packages each as
`<orca_ver>_<vendor>_<profile_version>_<UTCyyyymmddHHMM>.zip` (zip root
`<vendor>.opc`) — the asset-name contract OrcaCloud's release scanner
expects.
- Uploads them to a per-Orca-version release on the profiles repo via a
scoped
  GitHub App token.

It stops there: no changelog, no R2, no OTA webhook — a maintainer still
publishes from the OTA Manager. Job is guarded to
`OrcaSlicer/OrcaSlicer`;
workflow permissions are `contents: read` (the cross-repo write uses the
App
token only).

### `.github/workflows/build_orca.yml`

Two steps on the Linux leg: upload `generate_system_cache` as a CI
artifact,
and (on `main`) deploy it to the `nightly-builds` release as
`generate_system_cache_Linux_Ubuntu2404_nightly` so the workflow above
has a
tool to download. `.opc` is 64-bit little-endian and
platform-independent, so
only the Linux binary is shipped.

### `src/dev-utils/generate_system_cache.cpp`

New `-v` / `--vendor` option to generate the cache for a single vendor
(plus the
always-loaded Orca filament library) instead of all vendors, and an
error if the
named vendor produced no `.opc` (catches typos). Reuses
`PresetBundle::set_vendor_to_validate()` from #14217.

## Operational prerequisites

- Repo secrets `PROFILES_APP_ID` / `PROFILES_APP_PRIVATE_KEY` for a
GitHub App
  with `contents: write` on the target profiles repo.
- `env.PROFILES_OWNER` / `env.PROFILES_REPO` in
`post_merge_profiles.yml` must
  point at the production profiles repo OrcaCloud reads.
- `generate_system_cache_Linux_Ubuntu2404_nightly` only appears after
the first
post-merge nightly `build_orca` run; a profile-only push before then
fails at
  the download step.

## Screenshots/Recordings/Graphs

n/a — CI only.

## Tests

Exercised end-to-end in a staging environment: per-vendor `.opc` zips
published
in the expected `<orca_ver>_<vendor>_<profile_version>_<timestamp>.zip`
layout,
and `generate_system_cache -v <vendor>` confirmed to emit only that
vendor's
`.opc`.

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-09-18 18:53:26 +08:00
Ian Chua 00f78c18cf Merge branch 'main' into fix/windows-unsub-plugin 2026-09-18 18:51:50 +08:00
SoftFever 1159ca5f7f Merge branch 'main' into weilun/speed_dial 2026-09-18 17:28:24 +08:00
SoftFever 2dd3ef7cff fix build errors on mac 2026-09-18 17:24:47 +08:00
peachismomo 2bfab589ee fix: windows unsubsribe loaded plugin not allowed 2026-09-18 16:07:25 +08:00
Lam Wei Lun 8f4e3dde55 Merge main 2026-09-18 15:59:00 +08:00
Ian Chua 4a72a3bba2 Merge branch 'main' into feat/ota-opc-ci 2026-09-18 14:56:00 +08:00
SoftFever f1f68ffc3f Merge branch 'main' into cad-mainline 2026-09-18 14:01:23 +08:00
Ian Bassi c833ccdf6f Update localizations and improve strings (#15739) 2026-09-17 14:27:53 -03:00
SoftFever f520e9221f Repair shipped default materials and obsolete settings, and validate them (#15741)
* add orca profile skill

* add default material check

Improve validation for default materials and filament profiles

* Fix default materials and obsolete keys

* clarifying orca-profiles skill
2026-09-18 00:39:46 +08:00
Valerii Bokhan 60b4a61854 Fix: Show indexed coFloatsOrPercents options in unsaved changes dialog (#15472) 2026-09-17 10:56:17 -03:00
Ian Bassi 7065fa9eae Fix extruder clearance help link anchor (#15738) 2026-09-17 09:54:42 -03:00
Ian Bassi 59e40a2c2e Print unsupported walls last (#15411) 2026-09-17 09:14:20 -03:00
Ian Bassi 82e91bd472 Port wipe tower BBS improvements (#15485) 2026-09-17 09:08:50 -03:00
packerlschupfer ca668a3bc9 CLI: --inspect-paint — dump per-facet paint state as JSON (#14608)
* CLI: --inspect-paint — dump per-facet paint state as JSON

Reads the per-facet enforcer/blocker/extruder/fuzzy-skin state stored
on every ModelVolume (supported_facets / seam_facets /
mmu_segmentation_facets / fuzzy_skin_facets) and emits a structured
JSON summary to stdout. Machine-readable alternative to opening the
paint gizmos.

Per (object, volume, layer, state): facet count, surface area in
mm², and mesh-local bounding box. Empty layers collapse to
{"empty": true}. Summary at the top level rolls up totals.

One correctness detail worth calling out: FacetsAnnotation::
get_facets_strict returns an indexed_triangle_set whose `vertices`
array is the whole source mesh — only `indices` are filtered to the
painted triangles. A naive bounding_box(its) would report the whole
mesh's bbox even when only a few facets are painted. The helper
its_referenced_bbox() walks only the vertices actually indexed by
the painted triangles, so `bbox` correctly localizes the painted
region.

Rationale: every paint-driven workflow — GUI-painted .3mf verified
in CI, AI agents planning support enforcers, MMU color layout checks
— needs to know what's already painted on a model. Today that's a
GUI-only read. --inspect-paint closes that loop for scripted callers.

New file src/slic3r/Utils/PaintCLI.{hpp,cpp} (~215 lines). Depends
only on Model, TriangleMesh, TriangleSelector, FacetsAnnotation, and
nlohmann::json — all already in tree. No new dependencies, no
signature changes, no behavior change when the flag is absent.

Registered as an action (parallel to --info) so it satisfies the
"needs an action" check and bypasses the GUI fallback; control falls
through the normal post-action path to a clean exit 0.

Verification:
  unpainted STL:     every layer {"empty": true}, summary zero
  GUI-painted .3mf:  enforcer count / area / bbox match painter
  clean JSON:        parseable via jq

* CLI --inspect-paint: exit after printing, reject conflicting actions

- Finish like the end of CLI::run once the JSON is written, as the
  tooltip says. The callback manager is Linux-only, so its use is
  guarded.
- Reject actions that would otherwise be skipped without notice
  (--slice, --export-3mf, ...) before loading. Load-time options such as
  --uptodate are still accepted.
- Replace invalid UTF-8 in object names and paths instead of throwing.
- Report every input file as sources; inputs are merged into one model
  before actions run.

* CLI --inspect-paint: reject a run without input

Without an input file or --load-assemble-list there is nothing to
inspect, and the run printed nothing and exited 0. Reject it up front
with CLI_INVALID_PARAMS, next to the other invalid-parameter checks.
2026-09-17 12:01:49 +08:00
Ian BassiandRodrigo Faselli 72774e5398 Toolchange Cyclic Order (#14868)
* Toolchange Cyclic Order

* Apply cyclic order to first layer

* Unit test

* Copilot fixes

---------

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-16 12:19:03 -03:00
SoftFever ade9e77b6b Run every profile maintenance job from one tool (#15726)
* Run every profile maintenance job from one tool

orca_id_tool.py becomes orca_profile_tool.py, and orca_extra_profile_check.py
and orca_filament_lib.py fold into it as subcommands: check, generate-id, fix,
trim, update-index and update-snapshot. The three scripts already overlapped --
the checker imported half of its rules from the id tool, which in turn kept a
copy-pasted set of output helpers to avoid the resulting import cycle -- while
disagreeing on how a vendor is enumerated, how a JSON file is read and what the
exit code means. One file settles all three.

check, normalize, trim and update-index reproduce their predecessors exactly; normalize and
update-index were diffed byte-for-byte against the old scripts over a copy of
the whole tree. Deliberate changes: the compatible-printers check no longer
switches itself off when --check-materials is passed, an error exits 1 rather
than -1, update-index honours --profile-type and reports a profile it cannot
place instead of dropping it from the index, fix and update-index gained
--dry-run, trim keeps an unindexed file some surviving profile still inherits
from, and vendors are enumerated as directories with an index -- which is why
blacklist.json, a data file that an unscoped index rebuild once wrote four empty
list sections into, loses them here and will not collect them again. The dead
rename_filament_system() helper is gone.

The suite under scripts/tests now covers the maintenance commands too, and CI
runs it; nothing there ran in CI before. No shipped profile data changes apart
from those four keys.

* update vendor index files with "python3 ./scripts/orca_profile_tool.py update-index" and "python3 ./scripts/orca_profile_tool.py normalize"
2026-09-16 19:53:23 +08:00
Lam Wei Lun 075a84093f Fixes issue with showing hidden settings in speed dial. 2026-09-16 16:02:39 +08:00
packerlschupfer 93c8b3f2b0 CLI: --ground-* orientation from the Lay on Face planes, and --inspect-mesh (#15073)
* CLI: --ground-face-* / --lay-flat / --center-on-bed orientation primitives

Adds the CLI counterparts to the GUI's lay-flat / face-pick gizmos.
Scripted / CI / AI pipelines can now set orientation without rendering
a wxWidgets frame; today the only way is a GUI round-trip.

New CLI actions (all operate in the mesh-local frame so they compose
with prior --rotate-* / --orient flags):

  --ground-largest-face 1     Auto-detect the largest planar-face
   or  --lay-flat 1           cluster (area-weighted), rotate so its
                              normal points -Z. Covers "this part has
                              one obvious flat side" cases.

  --ground-face-normal NX,NY,NZ    Pick the face whose mesh-local
                                   normal best matches the given
                                   vector; ground it. e.g.
                                   `--ground-face-normal 1,0,0`
                                   stands a part on its +X side.

  --ground-face-point X,Y,Z        Find the triangle containing the
                                   given mesh-local point; ground its
                                   face. Disambiguates when several
                                   faces share a normal (largest
                                   containing triangle wins).

  --center-on-bed 1                Translate so the XY bounding-box
                                   centroid lands at the bed center
                                   (derived from printable_area).

New file `src/slic3r/Utils/MeshOrient.{hpp,cpp}`:
- collect_triangles_object / compute_face_clusters — quantize
  per-triangle normals (0.001, ~0.06°) and area-weighted-average
  within clusters. Same clustering logic used by lay-flat.
- apply_ground_rotation — same math as Selection::flattening_rotate
  in the GUI (Selection.cpp:1432): world-space quaternion from the
  transformed normal to -Z, applied as offset * new_rot * old_no_offset
  on every instance of every object, then a per-instance Z-lift so the
  grounded face lands at exactly 0 (avoids "No layers were detected"
  from FP-error z≈-1e-9).
- ground_face_point uses a top-N cluster search + point-in-triangle
  test in local space; largest-area triangle wins on ambiguity.

Rationale: without these, any CLI pipeline that needs a specific
face on the bed must either encode custom rotation math per part or
break out of the pipeline into the GUI. Both are bad for
reproducibility. The --ground-face-* triple + the largest-face
auto-mode cover essentially every orientation intent expressible
in a slicing wizard.

Scope:
- `src/slic3r/Utils/MeshOrient.{hpp,cpp}` — new, ~420 lines
- `src/slic3r/CMakeLists.txt` — 2-line registration
- `src/libslic3r/PrintConfig.cpp` — 5 new CLIMiscConfigDef entries
- `src/OrcaSlicer.cpp` — 58-line handler block + 1 include

No behaviour change when the flags are absent.

(cherry picked from commit c45a9795e1)

* CLI grounding: choose among the Lay on Face planes, per object

Addresses review:
- Move the geometry of GLGizmoFlatten::update_planes() into
  libslic3r/LayOnFace and use it from the gizmo and the CLI, so the
  --ground-* options pick convex-hull faces per object and instance,
  with part transformations (--rotate-x/y) applied.
- Drop --center-on-bed, the --lay-flat alias and MeshOrient; make
  --ground-largest-face a coBool.
- Parse --ground-face-normal and --ground-face-point strictly. A point
  that only some objects contain grounds those and leaves the others.
- Fold in --inspect-mesh from #14603, reporting the same planes.
- Tests in tests/libslic3r/test_lay_on_face.cpp: bounding boxes before
  and after, rotate then ground, two objects, and a ribbed part whose
  parallel inner faces outsum its base.

* CLI --inspect-mesh, --ground-face-*: reject missing input and empty values

- Without an input file or --load-assemble-list, --inspect-mesh printed
  nothing and exited 0. Reject it up front with CLI_INVALID_PARAMS.
- An explicit empty --ground-face-normal or --ground-face-point was
  silently ignored. Only options given on the command line reach the
  transforms loop, so an empty value now fails the strict parse like any
  other malformed value.
2026-09-16 12:56:46 +08:00
packerlschupfer 9321f24959 CLI: --strict, and a warnings array in result.json (#14601)
# Description

Add `--strict` for CI and scripted pipelines, and a structured
`warnings`
array in `result.json`.

## `--strict`

A NON_CRITICAL slicing warning is logged and the slice succeeds: return
code
`0`, G-code written. That suits interactive use, but a pipeline then
ships a
slice with a warning nobody saw. With `--strict`, such a warning fails
the run
with `CLI_SLICING_ERROR` before the G-code is exported. Without the
flag,
nothing changes.

In FFF the warning that reaches this path is "support needed but
disabled"
(`PrintObject::generate_support_material`). `--no-check` skips that
check, so
`--strict --no-check` is rejected with `CLI_INVALID_PARAMS`.

`--strict` is read before any work, so it doesn't depend on argument
order and
`result.json` reports it for early failures as well.

## `result.json`

Two new top-level fields:

- `warnings`: `[{"class", ...details}]`. One class is wired:
`slicing_warning_non_critical` with `plate_id` and `text`, recorded
whenever
such a warning fires, with or without `--strict`. The array also fills
on
  runs that succeed, so `return_code` stays the verdict.
- `strict_mode`: whether `--strict` was on.

`record_exit_reson` writes `result.json` on Linux only, so both fields
exist
only there. The non-zero exit works on every platform.

## Tests

- `tests/fff_print/test_support_material.cpp` (all platforms): an
overhang
sliced with support off raises the NON_CRITICAL support-needed status,
and
  the no-check flag suppresses it.
- `tests/cli/test_cli_strict.sh` (Linux only): runs `orca-slicer`
without
flags, with `--strict`, and with `--strict --no-check`, and checks the
shell
status and `result.json` of each. It runs the built binary, so it
carries the
`RequiresApp` label, which `scripts/run_unit_tests.sh` excludes because
the
  unit-test job only receives `build/tests`. Run it with
  `ctest --test-dir build/tests -C Release -L RequiresApp`.
- CI: `unit_tests.yml` now passes `Release` on Linux too.
`build_linux.sh`
configures Ninja Multi-Config, and without a config ctest drops the
labels of
plain `add_test()` tests, so this test ran as "Not Run" instead of being
excluded. The docs that assumed Linux was single-config are corrected
too.

Built and run locally on Linux (GCC 14) on current `main`: both tests
pass,
and the touched files compile clean under Clang with `-Werror`.
2026-09-16 12:54:48 +08:00
Lam Wei Lun f76e4b1e02 Fixed centering of icons. Disabled zooming in/out on Windows. Added default icons 2026-09-16 12:15:27 +08:00
Lam Wei Lun 435336a3cf Merge branch 'main' into weilun/speed_dial 2026-09-16 10:20:13 +08:00
Valerii Bokhan 3e1daccd7c Feature: Add inward wipe for external perimeters (#15407) 2026-09-15 20:01:39 -03:00
Kris Austin ac3997c0d1 fix: bounds-check the toolchange flush-volume and HRC per-filament lookups (#15289)
* fix: bounds-check the toolchange flush-volume and HRC per-filament lookups

GCode::set_extruder's toolchange flush-volume lookup and
GCodeProcessor::update_slice_warnings's HRC check index per-filament and
per-extruder arrays (flush_volumes_matrix, the filament map, the nozzle list)
by filament/extruder id. When a config leaves one of those arrays shorter than
the filament count (partial or legacy multi-extruder projects, minimal
configs), the reads run off the end: silent on a normal STL, a hard abort under
_GLIBCXX_ASSERTIONS.

Route both reads through bounds checks: the flush lookup falls back to no flush,
matching the existing unknown-old-filament branch beside it, and the HRC check
skips an unmapped filament, mirroring the required_nozzle_HRC guard on the line
above. When the arrays are sized to the filament count the values are unchanged,
so correctly-specified configs are unaffected.

* ci: retrigger checks
2026-09-15 09:46:31 -03:00
Tommaso Bianchi 4ebac62519 Extrude accepts a negative distance, and the Bodies card gains Boolean 2026-09-15 14:40:14 +02:00
Kris Austin bd1304443c fix: guard per-filament array reads against short config arrays (#14789)
* fix: guard H2C per-filament array reads against short config arrays

The H2C tool-ordering, wipe-tower, and g-code export paths index per-filament
config arrays by filament/tool id. A config with fewer entries than the filament
count (partial or legacy projects, minimal test configs) makes these reads run
past the end of the vector: silent under a normal STL, but UB that aborts under
the flatpak build's bounds-checked STL (_GLIBCXX_ASSERTIONS).

Route the reads through the existing clamping accessors (get_at,
get_filament_category, is_in_same_extruder) and add a small clamp helper for
filament_change_length. The guards are no-ops when the arrays are sized to the
filament count, so correctly specified configs are unaffected.

* fix: size the grouping context's filament_info to the filament count

build_filament_group_context built model_info.filament_info by walking
filament_type, so a config whose filament_type is shorter than the filament
count produced a short vector. FilamentGroup indexes filament_info by filament
id, so clamping the individual reads only moved the out-of-bounds access
downstream. Loop to filament_nums and read all three fields through get_at,
and drop filament_ids entries past the filament count, since the grouping code
pairs filament_ids and filament_info by position.

Adds a regression test with four filaments and one-entry filament_type /
filament_is_support. Without the fix it throws bad_alloc from copying a garbage
std::string read past the end.

* fix: guard the carousel nozzle-change length reads too

The carousel branch added in b90ac13d86/b0dddb4648 reads
m_filaments_change_length by tool id without a bounds check, the same
pattern this branch already routed through filament_change_length_at
a few lines above in both plan_toolchange and plan_tower_new.

* fix: guard WipeTower per-filament array reads against short config arrays

The BambuStudio WipeTower sync reintroduced raw per-filament array
indexing that reads out of bounds when a config leaves an array shorter
than the filament count: m_physical_extruder_map in format_line_M104/M109
(indexed even when empty), and m_filament_categories in get_wall_skip_points
and get_wall_filament_for_all_layer. Silent on a normal STL, a hard abort
under the bounds-checked STL the Flatpak build uses.

Bounds-check the physical extruder map before indexing (omitting the T
token, as the existing -1 path already does), and route the two raw
m_filament_categories reads through the clamping get_filament_category()
accessor the surrounding code already uses. No change for correctly-sized
configs.
2026-09-15 09:03:20 -03:00
Lam Wei Lun 2b6eb425e4 Update YouTube URL for publish 3MF guide 2026-09-15 18:04:06 +08:00
Ian Chua 173706750d Merge branch 'main' into feat/ota-opc-ci 2026-09-15 15:19:43 +08:00
HanifKoh 5514559feb Load Each Vendor Tree Once When the CLI Resolves System Presets (#15693)
# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->

Since #15438, every CLI run that loads a system preset spends about a
second per preset file re-parsing that vendor's entire profile tree. A
slice with a machine, process and filament preset got roughly 2.5 s
slower, and a four-filament slice roughly 4 s slower. This PR loads each
vendor tree once per run instead. Resolved presets and G-code are
unchanged.

The GUI never takes this path, and no release contains #15438, so the
regression only affects CLI runs on current dev and nightly builds. That
includes print farms, slicing services and plugins that call
`orca-slicer --slice`, and CI suites.

## Changes

### Why it was slow

`PresetBundle::resolve_preset_config` resolves a system preset through
its vendor manifest by loading the whole OrcaFilamentLibrary bundle and
the whole vendor tree from JSON, then picking the one preset out. The
CLI did that separately for every `--load-settings` and
`--load-filaments` file, on a fresh `PresetBundle` each time. With BBL
presets, a machine + process + filament run opened `BBL.json` three
times and read BBL's 2,879 profile files and the library's 512 three
times over.

### Load each vendor tree once

- `PresetBundle` keeps every vendor bundle its manifest path loads,
keyed by source root, vendor and substitution rule, and reuses them for
later resolutions on the same bundle.
- OrcaFilamentLibrary is cached the same way, so vendors under one root
share a single library load and the library's own presets resolve from
that same instance. A vendor bundle only reads from its base while
loading, so sharing it is safe.
- A failed or throwing load is not kept, so error reporting is
unchanged.
- The key includes the source root, so presets from two different
profile roots still resolve separately.
- The CLI resolves every system preset through one `PresetBundle` for
the whole run, instead of creating one per file.

The resolved configurations still come from the same canonical vendor
loader, so what a preset resolves to does not change. Only the CLI calls
`resolve_preset_config`, so a long-lived GUI bundle cannot end up
holding profile trees that later change on disk.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

CLI slice of a 20 mm cube with X1 Carbon system presets. Both builds get
the same datadir, best of 3, Linux. "Before" is this PR's base from CI.

| System presets loaded | Before | After |
|---|---|---|
| machine | 0.95 s | 0.87 s |
| machine + process | 1.67 s | 0.92 s |
| machine + process + 1 filament | 2.51 s | 0.97 s |
| machine + process + 4 filaments | 5.00 s | 0.99 s |

Files opened during the machine + process + filament run (`strace -e
openat`):

| | Before | After |
|---|---|---|
| `BBL.json` | 3 | 1 |
| `OrcaFilamentLibrary.json` | 3 | 1 |
| `system/BBL/**/*.json` | 8,634 | 2,880 |
| `system/OrcaFilamentLibrary/**/*.json` | 1,536 | 512 |

Peak memory did not rise: max RSS 306 MB → 286 MB for the three-preset
run, and 305 MB → 285 MB for four filaments. The "before" figure is an
AppImage, so part of that gap is probably packaging.

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

- New test "Manifest-backed resolution reuses the vendor tree it already
loaded" in `tests/libslic3r/test_preset_bundle_loading.cpp`. It resolves
one preset, changes the parent profile on disk, then resolves a sibling.
The same bundle returns the value it already loaded, and a fresh bundle
picks up the change.
- New test "Manifest-backed resolution shares the library between
vendors under one root". It resolves through one vendor, changes a
library profile on disk, then resolves through a second vendor and a
library preset on the same bundle. Both return the value already loaded,
and a fresh bundle picks up the change.
- All `[Preset][Bundle]` tests pass (87 test cases, 1,069 assertions),
including the existing manifest-backed resolution cases for source-root
scoping, malformed vendor loads, missing parents and type mismatches.
- G-code of the three-preset slice is identical before and after, header
lines excluded.
- The external CLI regression suite passes. Two cases report as
unexpectedly passing because #15639 fixed their bug. They pass the same
way on this PR's base without the change.
- A GUI-vs-CLI parity run over 10 fixtures shows no new differences.
- Builds clean on Linux (Release, with tests).

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-09-15 15:01:24 +08:00
Hanif Koh d5cf1502c4 Share One Library Load Between Vendors in the CLI Preset Resolver
The manifest resolver loaded OrcaFilamentLibrary once per vendor it
resolved through, so a run that mixes vendors parsed the library tree
again for each of them. The library is now cached like any other vendor
tree, keyed on its root and substitution rule, and doubles as the base
every vendor under that root loads against. A vendor bundle only reads
from its base while loading, so sharing the instance is safe.

The cache key carries the substitution rule as its enum, and the lookup
lambdas take a const bundle since they only read.
2026-09-15 13:31:30 +08:00
HanifKoh 37e2b6c928 CLI: let --export-settings - write the merged config JSON to stdout (#15698)
`--export-settings` already writes the merged config as JSON at the
right point in the CLI flow. Passing `-` now writes that same document
to stdout, so scripts can inspect the effective config without a temp
file. This replaces #14605.

- `ConfigBase::save_to_json` gains a stream overload. The file overload
serializes through it before opening the file, so the output format is
unchanged, and a config that cannot be serialized (invalid UTF-8) now
leaves the existing file untouched instead of truncating it.
- On stdout, invalid UTF-8 in string values is written as U+FFFD instead
of ending the process with an uncaught `type_error`. Files keep the
strict behaviour.
- To keep stdout pure JSON, `-` is rejected up front (stderr message,
`CLI_INVALID_PARAMS`, shell status 254) when combined with an action or
transform that can write to stdout or does real work: `--info`,
`--help`, `--orient`, slicing and exporting. Options that do nothing
without a slice (`--uptodate`, `--min-save`, `--pipe`, ...) are still
accepted.
- The one unconditional stdout write on a success path, "skip locked
instance" during arrange, now goes to the log.
- Every other value, including the default `output.json`, behaves as
before.

Tests in `tests/libslic3r/test_config.cpp`: the stream output equals the
file output and keeps the tab-indented format; invalid UTF-8 throws on
the strict path and is replaced when asked; a failed save leaves the
previous file intact.
2026-09-15 13:16:22 +08:00
Kris Austin efc9f253ee fix: resolve relative input paths given on the command line (#14803)
Opening a model with a relative path, for example `orca-slicer ./some.3mf`,
failed with "Loading of a model file failed." and "The file does not contain
any geometry data.", while the same file opened by an absolute path or by
drag and drop worked.

GUI_App::init_app_config() changes the working directory to <data_dir>/log,
and it runs from the GUI_App constructor because the app config is needed
early for instance checking. The input files are opened much later, in
post_init(), so a path still relative at that point resolved against the log
directory instead of the directory OrcaSlicer was started from, and the 3MF
reader failed to open it.

Resolve the input paths in CLI::setup(), which runs before GUI_App is
constructed and therefore before the working directory moves. Absolute paths
are returned unchanged, so the forms that open today are unaffected, and
custom open protocol URLs are passed through since post_init() hands those to
the downloader rather than the file loader.

The working directory change is left alone. It was added in #3248 so the TUTK
logs land in the data directory instead of the working directory (#3209).
2026-09-15 12:47:01 +08:00
Kris Austin 292cf0095e drop the per-frame mouse raycast that only a drag start reads (#15664) 2026-09-14 18:31:23 -03:00
packerlschupfer 31eb8a2bd1 CLI: let --export-settings - write the merged config to stdout
--export-settings already writes the merged config as JSON at the right
point in the CLI flow. Passing - writes the same document to stdout.

- ConfigBase::save_to_json gains a stream overload. The file overload
  serializes through it before opening the file, so the format is
  unchanged and a config that cannot be serialized leaves an existing
  file untouched instead of truncating it.
- On stdout, invalid UTF-8 in string values is written as U+FFFD instead
  of ending the process with an uncaught type_error; files keep the
  strict behaviour.
- - is rejected up front when combined with an action or transform that
  can write to stdout or does real work, so stdout carries only the
  JSON.
- The unconditional "skip locked instance" stdout write during arrange
  now goes to the log.
- Tests in tests/libslic3r/test_config.cpp.
2026-09-14 19:35:29 +02:00
Daniel Williams 70247ad298 Extract Layer::choose_ironing_extruder for unit-testable ironing routing (#13467)
* Extract Layer::choose_ironing_extruder for unit-testable ironing routing

The ironing extruder selection in make_ironing() was a 5-line nested
conditional inlined at the top of the loop, with no isolated test
coverage. Pull the gating into a static helper so the routing decision
is unit-testable without spinning up the slicing pipeline.

Pure refactor: the helper preserves the original logic bit-for-bit
(NoIroning -> -1; AllSolid always enabled; TopSurfaces and TopmostOnly
require some top shells or, in spiral mode, more than one bottom shell;
TopmostOnly additionally requires being on the topmost layer; enabled
ironing routes to solid_infill_filament).

Add tests/fff_print/test_choose_ironing_extruder.cpp covering:
- AllSolid regardless of layer position
- TopSurfaces with top_shell_layers > 0
- TopSurfaces with top_shell_layers=0 + spiral mode + bottom_shell_layers>1
- TopmostOnly + topmost layer
- NoIroning short-circuit
- TopSurfaces with top_shell_layers=0 (and not spiral) -> disabled
- TopSurfaces, spiral, but bottom_shell_layers=1 -> disabled
- TopmostOnly on a non-topmost layer -> disabled

* Move ironing routing test into the Fill subsystem file

Rename the test to tests/libslic3r/test_fill.cpp and tag it [Fill] to
match the subsystem it covers, use flat behavioral test cases with
GENERATE for the parameterized ones, and drop the history narration from
the code comments.

* tests: move ironing routing tests into fff_print/test_fill.cpp

Keeps the Fill tests in one file, alongside the existing ironing
rotation-template test.
2026-09-14 09:37:04 -03:00
Lam Wei Lun e1d3c90030 Fixes for window rounding 2026-09-14 18:08:34 +08:00