Commit Graph
66 Commits
Author SHA1 Message Date
SoftFever 489e337bad Merge Belt Printing Into Upstream (#14394)
# Belt printing for OrcaSlicer

This merges the `belt-printer` branch into `main`. It adds support for
conveyor-belt ("infinite Z") printers like the BabyBelt Pro, the
IdeaFormer IR3 V2 and the CR-30 family, along with the things that had
to grow around it: tilted-plane slicing, supports that terminate on the
belt, belt-aware brims, a purge tower that works without a flat bed,
multi-colour belt prints, a preview that shows the part the way it was
designed, and starter profiles.

It has been a long road (the first attempt was #12733 back in March, the
pipeline has been rebuilt twice since) and a lot of people have put work
into it. Credits are at the bottom; please tell me if I've missed
anyone.

Closes #2628, closes #11344, closes #6885, closes #9004, closes #14188.

---

## How belt printing works

Slicers assume the bed sits on the XY plane and layers stack along Z. A
belt printer breaks that in two ways at once: the belt is tilted
(usually 45°) and the axis that advances the belt is the printer's Z, so
the "bed" lives on the XZ plane and is, in principle, infinitely long.

<img width="6360" height="2702" alt="A flat bed vs a tilted belt"
src="https://github.com/user-attachments/assets/3f5542f2-6dab-42bf-9233-f96d863b40c8"
/>

Rather than teach every part of the slicer about tilted beds, the branch
leaves the slicing engine alone and transforms what goes in and what
comes out. The pipeline has five steps.

![printer
pipeline](https://github.com/user-attachments/assets/4d2f9965-f768-4b22-afbe-b3fda05354c4)

<details>
<summary><b>The five steps in detail</b></summary>

### 1. Pre-slice rotation
The mesh is rotated by the belt angle before slicing
(`BeltSliceStrategy`, `BeltTransform`), so that the ordinary horizontal
slicer produces layers that are actually tilted planes relative to the
part. Rotation is about X by default (belt along Y); Y rotation and a Z
lift are supported too. A "global" mode rotates every object about one
common origin, which is what keeps several parts on one belt consistent
with each other.

### 2. Slice
Nothing special. The mesh is rotated, the slicer does what it always
does. This is the reason most of Orca's features (walls, infill, seams,
ironing, painting, …) just work on a belt without belt-specific code.

### 3. Supports
Supports are generated after slicing against a virtual floor: the belt
surface, expressed in the rotated frame (`BeltFloorContext`). Normal,
tree and organic supports all terminate on that plane instead of on Z=0,
and nothing may be generated below it. Painted supports and seams are
transformed with the same `trafo_sliced()` as the layers.

![adding
supports](https://github.com/user-attachments/assets/39c1a858-23e2-48ee-8eb3-7b1abb9be172)

### 4. G-code back-transform
The G-code is rotated back into the model's Cartesian frame
(`BeltBackTransform`). A nice side effect: with step 5 switched off you
can slice at a non-standard angle and print the result on a normal
printer. The first belt-sliced Benchy was printed exactly that way, on a
Sovol SV08.

### 5. Machine frame
The printer doesn't know its bed is tilted. To make straight walls come
out straight, the axes are remapped (the default mapping is X → reversed
X, Y → Z, Z → Y) and the result is sheared and scaled:

```math
\begin{bmatrix} X \\ Y \\ Z \end{bmatrix}
\longrightarrow
\begin{bmatrix} X \\ \dfrac{Y}{\cos\alpha} \\ Z + Y\cdot\tan\alpha \end{bmatrix}
```

This lives in `GCodeWriter` behind a small `MachineKinematics` strategy
(`BeltKinematics` on belts, identity otherwise), so the writer itself
has one code path. The slicing angle and the machine angle can differ if
you want to, e.g. slice at 30° on a 45° machine — mind your nozzle
clearance if you do.

</details>

A short recording of the back-transform from the original PR:


https://github.com/user-attachments/assets/cdb9cc83-711d-48b7-9d86-a014a32c5e8e

---

## What had to change to make it work

Belt mode is gated on the `belt_printer` printer setting; with it off,
every code path below is the old one.

<details>
<summary><b>Slicing and geometry</b></summary>

- `PrintObjectSlice` / `PrintObject`: the rotation, Z lift and
per-object layer-grid shift, plus the invalidation that goes with them.
Modifiers and painted volumes are transformed with the same matrix as
the model.
- `FirstLayerPlane`: "the first layer" on a belt is a band along the
belt, not the first slicing layer. First-layer speed, line width and the
fan band are measured against it.
- `Print::validate`: clearance checks against the gantry instead of the
printable height; skirt, raft, draft shield, the classic prime tower,
arc fitting, spiral vase with brim, and scarf-joint seams are refused or
disabled on belts because each of them either doesn't exist on a belt or
moves the belt the wrong way (a scarf seam starts one layer low, which
on a belt is a 0.28 mm back-step into the previous layer at every seam).
- Z-hop defaults to 0 on belt profiles; a lift on a belt is a belt move.

</details>

<details>
<summary><b>Supports</b></summary>

- `SupportMaterial`, `TreeSupport`, `TreeSupport3D`, `TreeModelVolumes`:
a shared `BeltFloorContext` provides the belt plane; supports clip to
it, extension layers are numbered sequentially, and the first-layer
flange that used to be stamped under everything is gone.
- `build_plate_tilt_x/y` (from #12733) is now derived from the slicing
rotation in `Print::apply`, so GUI and CLI agree; it's capped below 90°.
- Organic supports that reach the belt no longer produce negative flow.

</details>

<details>
<summary><b>Brims (#15155)</b></summary>

A belt first layer is effectively a single line of contact, so a brim
matters more than usual. `BeltBrim` generates per-layer bands along the
belt plus an "apron" ahead of the part, in the object's brim filament.
Two belt-specific settings came with it: **Leading edge brim length**
(more lines on the side printed first) and **Extra brim width** (across
the belt), plus a **Leading edge only** brim type.

<img width="1849" height="1043" alt="belt brim"
src="https://github.com/user-attachments/assets/f963ed8e-53e7-48f8-a495-123cb9ae27f7"
/>

</details>

<details>
<summary><b>Multi-colour: the belt purge tower</b></summary>

The classic wipe tower can't exist on a belt (its G-code bypasses the
transform and it needs a flat bed to stand on). Instead the GUI
generates a long, thin "purge prism" beside the parts, flush with the
far edge of the belt, one per plate (`BeltPurgeTower.cpp`). It is a real
model object, so it is sliced like everything else, and
`Print::_plan_belt_purge` routes every filament change into it via
`flush_into_objects`. It's sized from the flush matrix, split into one
island per simultaneous tool change, snapped onto the parts' layer grid,
cut off after the last colour change and stripped of infill no change
claimed, so what prints is a good deal smaller than the model you see in
Prepare.

</details>

<details>
<summary><b>G-code generation and cooling</b></summary>

- `GCodeWriter` + `MachineKinematics`/`BeltKinematics`: the
back-transform, axis remap, shear and scale, lifts that are belt moves,
and the first-layer travel speed.
- `GCode.cpp` / `BeltGCode`: a belt header (slicing rotation, remaps,
machine tilt) that the preview reads back; it is written outside the
optional header block so printers with a BTT TFT thumbnail still get it.
Exclude-object outlines are emitted in the plate frame.
- `CoolingBuffer`: the "first layers" the fan stays off for are a band
above the belt, marked per extrusion segment by the generator
(`;_BELT_BAND_START/END`) and honoured on every layer.
- `GCodeProcessor`: belt header parsing, start-G-code Z handling, and
height checks that don't compare belt travel against the printable
height.
- `ToolOrdering` / `BeltPurge`: filament changes are detected by
scanning the ordering (an apron layer never carries the first-layer
flag).

</details>

<details>
<summary><b>GUI</b></summary>

- Printer settings tab: the belt group (slicing rotation, angle, global
mode, infinite Y, purge tower, floor settings). The axis remap and
pre-slice remap options are Develop-mode only.
- `ConfigManipulation`: everything that doesn't apply on a belt is
greyed out (skirt, raft, draft shield, the wipe tower group, scarf
seams, …).
- Preview: a "designed view" that back-transforms the toolpaths onto the
model so you see the part upright, with `B` toggling the raw
machine-frame G-code (`GCodeViewer`, `Shortcuts`). The tilt comes from
the belt header, so imported G-code behaves.
- Arrange: parts are packed from the end of the belt that prints first,
colours are grouped into runs so each filament change happens once, the
purge tower's strip and the brim width are reserved, and piles aimed at
an off-centre `best_object_pos` are clamped to the bed (`Arrange.cpp`,
`ArrangeJob.cpp`, one clamp in `libnest2d`).
- `PartPlate`: plate icons stay in the gap between plates on a long,
narrow bed; the plate is open along Y for containment tests on an
infinite-Y belt.
- Calibration: a belt temperature tower (overhang variant) that slices
correctly on a tilt.
- The old tilted-bed rendering in Prepare was dormant and has been
removed; the bed is shown as the slicing pipeline treats it.

</details>

<details>
<summary><b>Config options</b></summary>

Printer: `belt_printer`, `belt_printer_infinite_y`,
`belt_slice_rotation`, `belt_slice_rotation_angle`,
`belt_slice_rotation_global`, `belt_preslice_global`,
`belt_frame_tilt_decouple`, `belt_frame_tilt_angle`,
`belt_support_floor_mode`, `belt_support_floor_offset`,
`belt_support_z_offset_mode`, `enable_belt_purge_tower`,
`first_layer_plane`, `first_layer_plane_offset`,
`first_layer_plane_thickness`, `build_plate_tilt_x/y`,
`gcode_back_transform`, `gcode_remap_x/y/z`, `preslice_remap_x/y/z`,
`preslice_remap_global`.
Process: `belt_purge_tower_width`, `leading_brim_length`,
`extra_brim_width`, brim type `leading_edge_only`. Object:
`belt_purge_tower_object`.

All of them have defaults that leave non-belt printers untouched, and
old `belt_support_floor_mode` values map to `none`.

</details>

<details>
<summary><b>Tests</b></summary>

`tests/libslic3r/test_belt_brim.cpp` and `test_arrange.cpp`, and belt
cases in `fff_print` (`test_print.cpp`, `test_skirt_brim.cpp`,
`test_gcodewriter.cpp`, `test_gcode_processor.cpp`): scarf gate, fan
band, gantry clearance, organic supports on the belt, brim with and
without the purge tower, apron widths, machine mapping at non-45°
angles, first-travel lift, start-G-code Z, arrange clamp and colour
grouping. All three suites pass on Linux, and the tree-wide profile
check passes.

</details>

---

## Starter profiles

Three vendors ship belt profiles. Belt mode needs the printer settings
at **Advanced** or above to show its group.

<img alt="advanced mode"
src="https://github.com/user-attachments/assets/7a519ce5-b3b5-400c-a914-4f208bb577b0"
/>

| Printer | Vendor bundle | Nozzles | Processes | Filaments |
|---|---|---|---|---|
| **Generic Belt Printer** (`MyBeltPrinter`) | Custom | 0.2, 0.4, 0.6,
0.8 | 0.20mm Standard, 0.12mm Fine | library |
| **BabyBelt Pro** (Printcepts) | Printcepts | 0.4 | 0.20mm Standard |
Generic PLA, Generic PETG, eSUN PLA |
| **IdeaFormer IR3 V2** | IdeaFormer | 0.4 | 0.20mm Standard | Generic
PLA, Generic PETG, eSUN PLA |

To set up a printer that isn't listed: pick **Generic Belt Printer**,
set the belt width and length, save the profile, then copy in your
machine's start/end G-code and limits and tune from there.

<img alt="generic belt printer"
src="https://github.com/user-attachments/assets/90134d55-d6fe-4dba-8695-5ea44e78ec2b"
/>

<details>
<summary>BabyBelt Pro</summary>
<img width="2467" height="1392" alt="BabyBelt Pro"
src="https://github.com/user-attachments/assets/2b448cee-339e-43c9-964b-1ee9044044c7"
/>
</details>

---

## Contributions

### @HarrierPigeon
***Majority of design & implementation***

I did most of the work here by myself with AI tools (primarily Claude,
some Codex.)
PRs #12733, #12998, #14385, #15155, #15361, #15156, #15526, #16127

### The @Unlayered3D Team
***Rotation-Mode Pipeline***

The initial version of this sheared the model in the pre-slice pipeline.
Talking with them convinced me to switch to the current
rotate->slice->unrotate-> remap & shear method, which had significant
immediate improvements. Working with them has been a blast.


### @tommasobbianchi
***IdeaFormer IR3V2 Profile, eSUN PLA Tuning, G-Code Render / Preview***

TommyB came in at the perfect time to help keep me motivated and
contributed several things I hadn't had the werewithal to implemement
yet. Without their contributions and encouragement, we wouldn't be here
yet.


### The BabyBelt Community

**BabyBelt Pro** — @rexit1982 for the profile, and @RobMink of
Printcepts for the printer and a lot of patient testing.

**Field reports** — Many members of the BabyBelt community helped,
testing on their machines, providing G-Code and examples of issues, and
encouraging me to keep working on it. Among them:
- @RobMink - creator of the BabyBelt
- @rexit1982 - initial BabyBelt Pro profile, bug hunter
- @shubhracc - found a *lot* of technical bugs
- @NeoDLC - bug hunter
and BabyBelt Discord members who found bugs & gave feedback in no
particular order:
- @horatio42 - also provided a build machine while mine was down
- @HotCubCar - requested belt printer brim support
- Swap_File
- @Nyctelios
- Sup
- @matschi140
- @Rise-Run
- @shooby-dooby

### OrcaSlicer Maintainers
**Generic belt printer** — @SoftFever
**Keeping on top of upstream** @RF47 & @HanifKoh
**Review and fixes** — @HanifKoh (#15685 and the review on this PR).
Among them: plates after the first printed off the bed on belts; painted
supports and seams ignored the belt transform; support generation failed
at a 90° tilt; the object table crashed on the Support column on every
printer because tilt keys were in the per-object tables; apron brims
printed in the wrong filament; `build_plate_tilt` went stale outside the
GUI; the CLI reserved a wipe tower on belts; every G-code file was
treated as belt G-code because the config block carries the angle; tests
didn't compile on Clang/MSVC; plus a long list of smaller clean-ups and
the review questions that led to the clearance check, the header length
fix, the retired floor modes and the removal of the diagnostic logging.

### Additional Thanks

A special thanks to LDO Motors, who provided equipment for validating
multicolor, and my wife, who not only put up with with this obsession,
and the addition of three belt printers to our home, but has encouraged
me to keep going ever since I started this project six months ago.

---

## Known Issues

- The purge prism's first tool is chosen by the shared `ToolOrdering`
logic; on some layouts the print opens on the wrong filament and makes
one extra change at the thin tip of the prism (the "cannot absorb the
full purge volume" warning at a low height).
- Colour grouping in arrange is a soft cost: when the belt is too short
for clean runs, colours overlap rather than spill onto another plate.
- Three OrcaFilamentLibrary filaments still carry `filament_z_hop` 0.4;
belt profiles override it to 0.

Also: slicing at an angle other than the machine's (decoupled frame
tilt) is supported but not something the starter profiles exercise.
2026-10-09 15:24:47 +08:00
ExPikaPaka 6d616c288a Texture displacement: speed up the connected-net layout 3x (#16149)
Make the connected-net layout affordable on a dense patch

Laying a patch out as a connected net cost ~175 ms on a 42k-triangle patch,
against ~21 ms for the unwrap it works from, and the gizmo asks for it on every
preview, overlay and bake. Measured on a real project the grid behind it ran
~19 million triangle-pair tests per net, nearly all of them misses: a cell
holds every triangle whose box touches it, and a candidate really meets a
couple of them.

Keep a bounding box with each stored triangle and answer those misses with four
comparisons instead of a full intersection. The net drops to ~53 ms with
identical output - the seam metrics on the test project did not move by one.

Two further attempts were measured and dropped, and are recorded in the comment
so they are not tried again: a free-space pre-check per chart came out slower,
because a folded chart lands against the net by construction and the cells
under it are occupied anyway, and splitting the boxes into their own array for
locality lost more to growing two vectors per bucket than it gained.

Also pick a pair's fold line from the longest boundary they share rather than
whichever edge came first, and grow the net strongest-adjacency-first rather
than breadth-first by area. Only the fold a chart is reached by comes out
matching, so a chart claimed across a short boundary leaves the long one it
shared with its true neighbour torn.

texture_unwrap_dump reports an unwrap from a saved project - charts, their
topology, folded triangles, where the texture is discontinuous and how long
those seams are. All of the above was found with it, and it is what keeps a
claim about this code honest; reading the 3D view and guessing had produced
three wrong diagnoses in a row.
2026-10-09 14:23:23 +08:00
Joseph Robertson 64cff12b9d Merge branch 'main' into belt-printer 2026-10-08 16:15:51 -05:00
Fernando Marino` e6be22dd4c Assign lib_name from $1 in has_host_runtime_library (#15912) 2026-10-08 17:22:57 -03:00
Joseph Robertson e39d6cc089 Merge branch 'main' into belt-printer 2026-10-07 14:14:20 -05:00
harrierpigeonandClaude Fable 5.1 006a9377c3 Profile validator: slice belt printers with two cubes along the belt
The slice check (-s) prints one 10 mm cube per printer with a height
range on filament 2 and expects the filament change to fire.  Since
#16236 a belt object's slicing Z starts at the belt below its leading
end, well below the part's first printed layer, so the range 4-10 falls
into the empty lead-in and filament 2 is never used: every belt printer
reported "the filament change never fired" and the Slice check job on
belt-printer went red.  A height range in slicing Z does not map onto a
part on a belt in any case.  Slice belt printers with two cubes one
behind the other along the belt, the second on filament 2, which gives
the one plain T1 the check looks for.  Other printers are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 14:11:15 -05:00
SoftFever 6d02a945e0 Speed up the Linux incremental rebuild (optimizes packaging step) (#16244)
## Speeds up OrcaSlicer incremental rebuild on Linux

Profiled `build_linux_image.sh`: 96 s, of which 51 s in the dependency
audit.

**`appimage_is_elf_file()`** ran `file` and `grep` per candidate. An
AppDir holds ~9.6k of them, 4.8k being the bundled Python runtime and
none of them ELF: ~19k processes, 14 s. Reads the four-byte magic
instead. Checked against the old result on 4000 files, no disagreement.

**The dependency walk** popped its queue with `"${queue[@]:1}"`, which
rebuilds the whole array each time. At ~4.8k entries that was 22 s of
copying an array around. Uses a read index.

Audit still passes. `shellcheck` v0.11.0, the version CI uses, is clean.

## Notes
The 96 s -> 12.7 s. Measured on a 32-core / 48 GB machine, but the audit
is a serial bash loop, so cores and RAM is not the bottleneck. On slower
hardware the saving should be larger

## Images
<img width="1987" height="782" alt="Screenshot_20261007_092516"
src="https://github.com/user-attachments/assets/96fcb917-38e5-49e7-8cbe-b37be1a2f23a"
/>
<img width="1807" height="742" alt="Screenshot_20261007_092621"
src="https://github.com/user-attachments/assets/4a65ffae-4d59-41f0-a1c7-ee5b49f3c4be"
/>
2026-10-07 19:07:22 +08:00
HanifKoh ff8aebe76f Allow Unsigned Executable Memory in the macOS Entitlements (#16203)
The Bambu network plug-in's code protector rewrites one page of its own signed __TEXT after loading. The hardened runtime tolerates that until the page is evicted; the next read of it then kills OrcaSlicer with CODESIGNING Invalid Page. Bambu Studio signs with allow-unsigned-executable-memory for this reason; with it added, the same build survives critical memory pressure that killed it in 30 s without.
2026-10-07 15:52:18 +08:00
ExPikaPaka bd0ef35b90 Speed up the Linux image build
Profiling build_linux_image.sh: 96 s, of which 51 s in the dependency audit.

appimage_is_elf_file() ran file(1) and grep per candidate. An AppDir holds ~9.6k
of them, 4.8k being the bundled Python runtime, none of them ELF: ~19k processes
for 14 s. Read the four-byte magic instead; checked against the old result on
4000 files, no disagreement.

The dependency walk popped its queue with "${queue[@]:1}", which rebuilds the
whole array each time. At ~4.8k entries that was 22 s of copying. Use a read
index.

96 s -> 12.7 s. The audit still passes.
2026-10-07 09:25:19 +02:00
Joseph Robertson d2aa937ad7 Merge branch 'main' into belt-printer 2026-10-05 23:20:23 -05:00
Kris Austin 1d577ea4e2 build: add the missing includes only a Windows build reports (#16110) 2026-10-05 21:49:32 -03:00
Hanif Koh 325ecdda10 Merge Main into Belt Printer 2026-10-05 17:27:52 +08:00
HanifKoh 4895bc03b4 Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced (#16099)
* Remove Unused Project Includes and Forward-Declare Where a Type Is Only Referenced

Generated with include-what-you-use and applied conservatively. Only OrcaSlicer's own headers, the ones under src/ and tests/, are removed or forward-declared; standard-library and third-party includes are left alone. An include is removed only when both the Release and the Debug configuration leave it unused, never from inside a conditional block, and never from a file with platform-specific blocks, which only gain includes. Files whose only use of a header sits behind a feature or debug macro (libvgcode's OpenGL ES and marker code, the ARACHNE/TESTS_EXPORT_SVGS debug output) keep their includes.

clonable_ptr.hpp gains #pragma once; it had no include guard and was only safe while Config.hpp was its sole includer.

* Remove Unused Project Includes From Files With Platform-Specific Code

A Linux include-what-you-use run cannot see the code inside _WIN32, __APPLE__ or __linux__ blocks, so its verdict is only taken where nothing the removed header declares, directly or through what it includes, is named inside those blocks. Removals also have to hold in both the Release and Debug configuration and never touch a line inside a conditional block.

* Restore the libslic3r Precompiled Header and Direct Includes Lost in the Platform Pass

The platform-file pass treated pchheader.hpp as an ordinary header and
emptied it, and left GUI_Preview.hpp and 14 other files relying on
headers they no longer reached directly.

* Restore MainFrame.hpp in ParamsDialog.cpp for the Windows-Only Reparent Call

* Include Headers That Files Reached Through Ones the Cleanup Removed

* Drop Includes Duplicated by the Cleanup or by Main's Own Additions

* Leave PreciseSeam.cpp as Main Has It After the Precise Seam Rework
2026-10-05 16:47:17 +08:00
harrierpigeon 7eb9aa3832 Profile validator: accept a belt printer's tool change without a tower
The validator slices every printer with two filaments and the prime tower
forced on, then requires the tower's CP TOOLCHANGE START block as proof
that change_filament_gcode ran. A belt printer has no wipe tower: it purges
into a prism object on the belt, so Print::has_wipe_tower() is false and
the change is the plain T command set_extruder() emits. All six belt
printers failed the sweep on that alone, although each one changes
filament throughout the slice. Look for the T1 line on belt printers.
2026-10-04 22:57:53 -05:00
HanifKoh 88346efceb Stop Format/STEP.hpp Defining a Global fs Alias (#16102)
Every file that included STEP.hpp, directly or not, got namespace fs = boost::filesystem at global scope, and 29 sources and three headers relied on it without saying so. Headers now spell out boost::filesystem, and each source that uses fs declares the alias itself.
2026-10-04 14:50:30 +08:00
HanifKoh 84657ff11e Add Missing Includes Across the Remaining Sources and Tests (#16071)
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy

Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp.

* Ignore minilzo's Config Headers in clang-tidy

lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes.

* Add Missing Includes Across the Remaining Sources and Tests

Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo.

* Make the GUI and Test Headers Compile on Their Own

Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone.

* Keep Windows and nanosvg Setup Ahead of the Added Includes

OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build.

* Add the GUI Includes the First Pass Missed

Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass.

* Keep the Added Test Includes Below the NOMINMAX Guard

test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
2026-10-03 13:45:21 +08:00
SoftFever c03f4925a9 Apply per-variant cooling, ramming and temperature range on nozzle-rack slices
Fan speeds, multi-tool ramming, the tower interface and flush temperature
fallbacks and the custom G-code placeholders now use the extruder variant a
filament prints with on each layer, instead of reading by filament id.
2026-10-01 02:54:18 +08:00
Eric McCannandCodex 0eb6e6814e Preserve nozzle variant tuning and cooling controls
Include variant-aware validation, temperature and pressure controls, named nozzle selection, and filament sidebar refresh.

Co-authored-by: Codex <codex@openai.com>
2026-09-29 22:42:30 -04:00
SoftFever 185cfe4323 Slice-validate every custom G-code and filename_format in system profiles 2026-09-29 01:53:30 +08:00
SoftFever 2b602943a5 Load profile includes so synced Bambu Lab presets get their G-code back 2026-09-24 13:22:37 +08:00
HanifKoh 19e094a1d1 Move the Cube with the Wipe Tower in the Profile Validator (#15799)
The slice check centres its cube on the bed, puts the prime tower beside
it, then pulls the tower alone inside the printable outline. On a bed too
narrow for the estimated footprint that pull drags the tower back over
the cube: Volumic EXO42 IDRE MIRROR MODE (189 mm wide, 87.6 mm estimate)
logged "gcode path conflicts found between WipeTower and cube" in every
run, and three ~105 mm beds were left with 0.15 to 3.3 mm of clearance.

The cube and the tower's footprint are now pulled inside as one rigid
pair, so the clearance between them is fixed by construction. A bed too
small for the pair keeps the old placement, and presets that were never
clamped keep their exact layout.
2026-09-22 18:22:42 +08:00
Ian Chua 173706750d Merge branch 'main' into feat/ota-opc-ci 2026-09-15 15:19:43 +08:00
Kris Austin 7888452666 build: clear 7 warning categories across 26 sites (#15615)
* build: clear 2 warnings - cast the NSTextField the class check already proved

mainframe_text_field is NSTextField* and was assigned a bare NSView*, which
Clang reports as -Wincompatible-pointer-types. Both assignments sit inside
if ([viewObject class] == [NSTextField self]), so the runtime type is already
guaranteed, and the line above the second one casts the same variable the same
way to call setTextColor. macOS only, since nothing else compiles this file.

* build: clear 6 warning categories from the clang-cl inventory

-Wmissing-braces (9). Aggregates whose first member is itself an aggregate.
GUID's fourth member is BYTE[8], so the trailing eight bytes take their own
braces. The others were reaching for zero-initialization with {0} and say {}
now. bbs_3mf's backup Task ends in an anonymous union, which needs braces of
its own; those braces initialize the union's first member rather than the one
named at the call site, so the RemoveBackup site says so in a comment.

-Wmacro-redefined (11). SendMultiMachinePage.hpp defines five names that
Preferences.hpp, PresetBundleDialog.hpp, ExportPresetBundleDialog.hpp and
TroubleshootDialog.hpp also define with different values, so the value in
force depended on include order. All nine of this file's DESIGN_ macros take
the SEND_ prefix it already uses for its own macros, values unchanged, so a
DESIGN_ name added elsewhere later cannot collide with it again. They read as
one page-local palette, a 900 to 400 gray ramp plus sizes, so the four with
no current readers stay: dropping them would leave gaps in a named scale. test_marchingsquares.cpp defines NOMINMAX,
which libslic3r already passes as a PUBLIC compile definition, so it takes
the #ifndef guard the other suites use.

-Wbraced-scalar-init (3). Two PushStyleVar calls resolve to the float
overload, so the braces were initializing a scalar. ConfigOptionFloatsNullable
already takes an initializer_list, so the inner braces did the same thing.

-Wmicrosoft-goto (2). Both gotos in copy_file_gui jump forward over the
initialization of size, dwRead and dwWrite, which only MSVC accepts. Those
declarations move up to join the others at the top of the function.

-Wunused-private-field (3). Every use of ColourPicker's m_clrData and
m_picker_widget is behind !defined(__linux__), so on Linux they are written
and never read; the members now carry the same guard. ParamsPanel's
m_size_move is read nowhere. Tab has its own, which is the one Tab.cpp uses.

-Wnonportable-include-path (2). BaseException.h asked for "stackwalker.h"
and the file on disk is StackWalker.h.
2026-09-10 07:39:14 -03:00
Ian Chua 02a8011b97 feat: add CI to generate OPC for OTA workflow 2026-09-10 16:27:23 +08:00
Kris Austin f18eb21b82 build: clear 11 single-site clang-cl warning categories (#15584)
build: clear eleven single-site clang-cl warning categories

Each of these is the last site left in its category, and every one is the
compiler saying it cannot tell what the code meant. Nothing here changes
defined behavior.

- OrcaSlicer_app_msvc.cpp printed a DWORD with %d
- StackWalker.cpp ran delete[] through an LPVOID
- ToolOrdering.cpp used a bare ; as a deliberate skip loop's body
- WipeTower.cpp had finish_block_tcr = finish_block_tcr, so the branch that
  reached it did nothing. Folding the condition into the enclosing if leaves
  the other branch untouched
- GCodeProcessor.cpp had an else binding to the inner if while the outer if
  carried no braces
- AmsMappingPopupUpdate.cpp wrote >= 1 || <= 3 where its own comment says &&
- CalibrationWizardPresetPage.cpp left max_decimal_length unset through a
  pair of conditions that cover every value but not visibly so
- DevManager.cpp bound map elements to pair<K, V> rather than
  pair<const K, V>, copying every one
- SyncAmsInfoDialog.cpp had extraneous parentheses around a comparison
- Http.cpp had if (speed > 0.01) speed = speed;. speed now starts at 0 as
  well, because curl_easy_getinfo leaves the target untouched when it fails
  and the value reaches Progress either way
- SnapmakerPrinterAgent.cpp truncated npos into an unsigned int, so the
  != npos guard was always true. A colour with no # still yields 0, because
  the wrap produced 0 as well

Nine categories go to zero. -Wtautological-overlap-compare and
-Wsometimes-uninitialized reach zero when #15583 merges their second site.
2026-09-09 12:01:19 -03:00
Hanif Koh fae77be3db Place the Wipe Tower in the Profile Validator
The validator forces a two-filament print with the prime tower on and
slices it at the config default position (x 15, y 220), which lies off
any bed shallower than the tower. It calls validate() but slices
regardless, so the off-plate tower was exported silently; with the
generation-time footprint check it is rejected instead, and 522 of the
1013 printer presets failed the slice check.

The validator now positions the tower the way the GUI and CLI do before
slicing: beside the centred cube, clear of the edge exclusion strips some
beds carry, then pulled inside the printable outline by the tower's own
estimated footprint.
2026-09-09 15:45:16 +08:00
SoftFever 06e665fca7 Merge branch 'main' into feature/filament_id 2026-09-06 22:34:06 +08:00
Kris Austin 2ad9c87dda fix: GIT_COMMIT_HASH forces full rebuilds and defeats compiler caching (#15537) 2026-09-05 14:08:38 -03:00
SoftFever df101ead91 fix more issues 2026-09-04 00:29:52 +08:00
SoftFever 21fdd7028f Fix spurious slice-validation failures when checking all vendors
The sweep now validates each printer with the filament that printer ships, so a run
over every vendor reports what a single-vendor run does. Validator only - no change
to slicing output or shipped profiles.
2026-09-03 11:25:49 +08:00
Noisyfox dbcb196f4e Merge branch 'main' into dev/ffmpeg-player 2026-08-26 21:47:20 +08:00
ExPikaPakaandSoftFever 5ed56eb876 Cache system presets to eliminate startup and wizard load times (#14217)
* Add caching system for presets

* Removing user\bundle serialization and keeping it only for system presets

* Integrate caching into WebGuideDialog which speeds up time of SetupWizzard and PrinterSelection dialog

* Add CI\CD step to prepare cache file in ahead of time so user does not need to wait

* Add partial cache generation when only one of the vendros is changed to speed up recalculation time

* Handle corrupted files

* Add cache to GuideDialog as previos version didn't work as expected

* Add inspecting tool and fix CI cache generation

* Generate cache per vendor

* Simplify code by mergin it in PresetBundle

* Simplify code a bit more

* Add cereal serialize() to VendorProfile, PrinterModel, Preset, and Semver

* Remove CachedPrinterModel/VendorProfile/Preset mirror structs from VendorCache

* Fix use-after-free in CallAfter lambda; replace raw thread pointer with unique_ptr

* Use get_vendor_cache_key() to match cache keys written by the app

* Remove BOM added by VSC

* Skip invalid vendors

* Remove leftover cache file

* Fix build for windows arm64

* Revert json cache back

* Update check for stale cache

* Serealize all value fields for Preset class to minimize regression later

* Minimize field duplication by moving Cache thing into PresetBundle

* Add tests for Cache system

* Add a bit more tests

* Merge branch 'main' into feature/cache_profiles_and_optimize_loading_speed

* Rvert from per-verndor to single cache file

Replace N per-vendor .cache files with a single system_presets.cache
that holds all vendors and presets in one serialized blob.

Cache load is now all-or-nothing: on hit all vendors are applied from
the bundle (sub-second); on miss all vendors are parsed from JSON and
a fresh bundle is written to the user cache dir.

Invalidation is driven by bundle_key - a sorted concatenation of all
vendor JSON version strings. Any vendor update invalidates the whole
cache and triggers re-parse on next launch.

Guide wizard (WebGuideDialog) loads the bundled cache into a plain
PresetBundle instead of a separate VendorGuideData struct, removing
the duplicate data model.

generate_system_cache simplified from a per-vendor loop to a single
save_system_presets_cache() call producing one output file.

* Transfer all Preset fields from cache via move assignmet

apply_vendor_preset_group was copying fields manually and missed
bundle_id, user_id, base_id, sync_info, updated_time, key_values,
ini_str. Replace field-by-field copy with move assignment of the
fully-deserialized Preset, then restore the vendor pointer which
is excluded from serialization.

* Ignore cache for future

* Remove not used files

* Ship one preset cache per vendor in place of the profile JSONs

Each vendor's system presets serialize into a single <vendor>.opc built at
package time, and a shipped build carries that file alone — the profile JSON
and its sub-file tree are pruned. The vendor loader, the setup wizard's profile
list and the resource installer all read a vendor through its cache, falling
back to parsing whenever one is absent, stale or unreadable, so the cache stays
an optimization and never a source of truth. Caches hold presets in source form
and resolve inheritance at load, through the same code the JSON path uses.

* Make the preset cache self-describing and load each vendor from the system folder alone

The cached DynamicPrintConfig is keyed by name, through a per-file dictionary of the
distinct opt_keys, the type each was written as, and the distinct enum value names,
instead of by serialization_key_ordinal — a position assigned by declaration order at
static init, where inserting one option shifts every later ordinal and the lookup then
succeeds on the wrong option. Because a name-keyed payload drops the options this build
cannot place rather than being rejected wholesale, the schema fingerprint goes, and with
it the two fallbacks that existed only because an installed cache died on every app
upgrade: the second lookup tier into resources/profiles and the parse fallback to the
same place. A vendor is loaded from <data_dir>/system/ and nowhere else, as on main —
which is what makes the app write its .opc files there again.

* Simplify the preset cache internals after review

* Use the shared temp-dir helper in the preset bundle loading test

* Bound stamp string reads in the preset cache

* Speed up the setup wizard with a profile-data cache

The wizard's per-vendor fast path threw on vendors present only in
resources, falling back to a ~29 s raw JSON scan on every open. Each
vendor now loads from the directory it was found in, and the derived
model/machine/filament/process catalog is cached whole in
<data_dir>/cache/wizard_profile_data.json, stamped by each vendor's
name and version - a fresh cache makes an open one file read, with no
bundle built and no presets installed (~0.2 s vs ~2 s).

* Remove debug SVG dump from a geometry test

* Move the per-vendor cache file format into PresetCacheFormat

* Move the vendor install helpers from PresetBundle into Utils

* rename

* fix flatpak

* change cache version to 1

---------

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-08-21 16:56:52 +08:00
peachismomo 050ebbb1f4 Merge branch 'main' into fix/clang-cl-windows-support 2026-08-19 03:14:10 +08:00
NoisyfoxandClaude 41c107d4f7 Fix Linux AppImage bundling of deps-built shared libraries
The AppImage dependency closure resolves each bundled ELF's DT_NEEDED
entries with plain ldd, which cannot resolve the deps-built FFmpeg stack
(libavcodec/libavutil/libswscale) once it is copied into the bundle:
those libs are not installed in any standard loader path and carry no
RUNPATH of their own, so ldd reports the siblings as missing and the
build aborts. Extend the loader path with the bundle directory plus the
source directories of already-bundled files (mirroring
scripts/check_appimage_libs.sh), and key the dedup set on the bundled
file path instead of the source path so dependencies resolved from the
bundle directory are not copied onto themselves.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-13 22:22:02 +08:00
Noisyfox 86d4429197 Try fix appimage build 2026-08-13 21:33:57 +08:00
SoftFever bf932e8797 Merge branch 'main' into feat/plugin-feature 2026-07-15 17:19:14 +08:00
SoftFever 8cb0cb7693 Merge branch 'main' into feat/plugin-feature
Resolve five conflicts, all of which needed both sides rather than a pick:

- BackgroundSlicingProcess: ours was a pure tabs->spaces reformat of base, so
  keep main's per-filament volume/nozzle map read-back (its only change here).
- GUI_App: main's #12506 else-if attached to an `if` this branch deleted;
  re-expressed onto the same-agent early-return path (the agent factory caches
  per id, so pointer equality is the same predicate).
- MainFrame: both sides relocated Sync Presets independently; keep main's
  push_notification plus the branch's Plugins menu items.
- Tab: the "TODO: Orca: Support hybrid" blocks were unchanged base, not a branch
  decision; take main's enabled Hybrid to match the already auto-merged siblings.
- test_config: union of both sides' cases (6 plugin + 9 multi-nozzle).
2026-07-15 17:18:46 +08:00
SoftFever be165c97de Add --outdir to profile validator to save sliced g-code 2026-07-15 16:52:02 +08:00
SoftFever f1fd49c12f Add slice-validation sweep for shipped profiles 2026-07-15 01:49:04 +08:00
Gabriel Monteiro 0013c1da34 Merge branch 'main' into fix/clang-cl-windows-support 2026-07-10 11:10:33 -03:00
SoftFever 8747605930 Fixes invalid inherits/compatible_printers/compatible_prints references that point at a deleted or renamed preset. (#14595)
* fix profile reference for Creality

* fix profile reference for Blocks

* fix profile reference for OrcaArena

* fix profile reference for re3D

* fix profile reference for Chuanying

* fix profile reference for Prusa

* fix profile reference for Wanhao France

* fix profile reference for MagicMaker

* fix profile reference for Afinia

Remove the ABS/ABS+/PLA/TPU/Value ABS/Value PLA filament presets that referenced the non-existent "Afinia H400 Pro" printer. The real printer is "Afinia H+1(HS)", already served by the @HS filament variants.

* fix profile reference for Comgrow

Remove the orphaned "0.20mm Standard @Comgrow T500 1.0" process preset and its process_list entry. Its only compatible printer "Comgrow T500 1.0 nozzle" never existed (the T500 model defines nozzle diameters 0.4/0.6/0.8 only).

* always run check_preset_references
2026-07-05 20:26:36 +08:00
Ian Chua ecddf3d18f feat: Python Plugins 2026-07-02 17:49:36 +08:00
SoftFever f516f47c8e add profile validator checks 2026-07-01 21:55:19 +08:00
SoftFeverandCopilot Autofix powered by AI c2e91cb86c validator: detect ambiguous (duplicate) filament subtypes per printer (#14459)
* validator: detect duplicate filament subtype per printer (opt-in)

A filament is matched from the AMS by (filament_id + printer compatibility);
if two compatible filament presets for one printer share a filament_id, the
match is ambiguous and the runtime silently picks whichever loads first.

Add PresetBundle::check_duplicate_filament_subtypes(), gated behind a new
has_errors(check_duplicate_filament_subtypes) parameter and the validator's
-f/--check_filament_subtypes flag (off by default). For each system printer it
groups its vendor's compatible filament presets by filament_id and errors on
any group of 2+, reporting each preset as a clickable file:// URI with a single
"how to fix" hint. CI runs it for BBL only (-v BBL -f) until the other vendors'
profiles are cleaned up.

* profiles: fix ambiguous BBL filament matches

Resolve the duplicate-filament-subtype errors flagged by the validator:
- align compatible_printers with Bambu Studio where Orca over-claimed a nozzle
  that already has a dedicated preset (Bambu PLA Basic/Matte/ABS @BBL H2DP;
  Bambu ASA/PETG HF @BBL H2DP 0.6 nozzle; Fiberon PETG-ESD @BBL X1)
- fix a copy-pasted printer name in Overture Matte PLA @BBL A1M 0.2 nozzle
- fix a wrong inherits in Panchroma PLA Silk @BBL X1C 0.2 nozzle (was inheriting
  Panchroma PLA @base, giving it filament_id GFPM001 instead of GFPM004)

Bump BBL profile version.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-28 00:42:02 +08:00
pallaswept 5d352294ed Allow appimage execution for non-owner (#14339) 2026-06-26 01:27:47 +08:00
dbd9c22d80 feat: native Windows ARM64 build support (Snapdragon X Elite) (supersedes #14059) (#14381)
* feat: native Windows ARM64 build support

Builds on the merged DEPS_ARCH=arm64 plumbing (#13424) by adding the
dependency and source fixes needed for a green native ARM64 build on the
windows-11-arm runner. Validated end-to-end on Snapdragon X Elite hardware
(via a downstream fork using the same fixes); see OrcaSlicer/OrcaSlicer#8271
for the full writeup.

Dependencies:
- OpenEXR 2.5.5: ImfSimd.h hard-codes IMF_HAVE_SSE2 for any MSVC, pulling in
  <emmintrin.h> (x86-only) -> C1189. Patch the header to require an x86 target
  and force SSE cache vars off on ARM64.
- Boost.Context: use the winfib implementation on ARM64 (Windows Fiber API)
  to avoid the armasm64 / CMake ASM_ARMASM linker-module bug, while keeping
  the Boost::context target Boost.Asio needs.
- OpenCV: disable WITH_IPP on ARM64 (Intel IPP/IPP-ICV is x86/x64 only;
  otherwise ~200 unresolved ippicv* externals at link).
- OpenSSL: use VC-WIN64-ARM on ARM64.
- FindGLEW: add an ARM64 arch branch.

Sources:
- clipper Int128.hpp: _mul128 is an x64-only intrinsic guarded by _WIN64
  (true on ARM64); guard on _M_X64 and use the portable path.
- imgui imgui_widgets.cpp: fix va_start(vaList, &text) -> va_start(vaList, text)
  (the &-form compiled on x64 but is invalid on ARM64).
- crash reporter: StackWalker.cpp gains an _M_ARM64 branch; BaseException.cpp
  uses Cpsr instead of the x86-only EFlags on ARM64.

CI:
- New build_windows_arm64.yml on windows-11-arm: pins CMake 3.31.x, stages
  ARM64 GMP/MPFR from MSYS2 clangarm64 (with llvm-dlltool import libs),
  caches deps with a fixed-depth hashFiles key, builds and uploads the binary.

OCCT/STEP, SVG-to-3D and text emboss all build and work on ARM64 (no stubs
needed). Full feature parity with x64.

* fix(ci): use forward-slash DESTDIR to avoid CMake '\a' escape error

deps configure failed at GMP/GMP.cmake: "Invalid character escape '\a'"
because DESTDIR carried Windows backslashes (C:\a\...) and is re-parsed
when re-set with the /usr/local suffix. Pass DESTDIR (and the slicer's
DEPS prefix) with forward slashes via %CD:\=/%.

* fix(ci): don't export DESTDIR env var (CMake staged-install doubles paths)

Setting a DESTDIR *environment* variable made CMake treat it as the staged
install prefix and prepend it to every dependency's install path, so e.g.
FreeType installed to <DESTDIR>/a/.../OrcaSlicer_dep/usr/local and OCCT
then couldn't find its headers. Compute the forward-slash path into a
differently-named var (ORCA_DESTDIR) and pass it only via -DDESTDIR.

* ci(windows-arm64): fold ARM64 build into the standard Windows matrix

Replace the standalone build_windows_arm64.yml with a matrix entry on the
existing build_windows job, so x64 and ARM64 share one reusable workflow
chain (build_all -> build_check_cache -> build_deps -> build_orca), per
review feedback on #14059.

- build_all.yml: build_windows now matrices over {x64: windows-latest,
  arm64: windows-11-arm} and threads `arch` through. Self-hosted runner
  stays x64-only.
- build_check_cache.yml: cache key and dep-prefix path are now
  architecture-specific on Windows (deps/build-arm64/OrcaSlicer_dep).
- build_release_vs.bat: accept an `arm64` argument (mirrors
  build_release_vs2022.bat) -> uses `-A ARM64` and the build-arm64 tree.
  The top-level CMake auto-derives CMAKE_PREFIX_PATH from the build dir,
  so no explicit prefix is needed.
- build_deps.yml / build_orca.yml: gate the ARM64-only prep behind
  `inputs.arch == 'arm64'` -- pin CMake 3.31.x, and stage MSYS2
  clangarm64 GMP/MPFR import libs. NSIS installer/PDB/profile_validator
  remain x64-only; ARM64 ships the portable zip. Artifact names get an
  arch suffix to avoid collisions between the two Windows jobs.

https://claude.ai/code/session_0164c7ZhCLsYBmCiVN9pWDjK

* ci(temp): generate GMP/MPFR win-arm64 blobs to commit to repo

* feat(deps): add prebuilt GMP/MPFR win-arm64 blobs

The repo ships prebuilt GMP/MPFR import libs + DLLs for win-x64 and
win-x86; the Windows ARM64 build path copies from win-${DEPS_ARCH}
(CMakeLists.txt) but the win-arm64 blobs were missing, so the slicer
configure failed at "file COPY cannot find .../win-arm64/libgmp-10.dll".

Add win-arm64 libgmp-10.{dll,lib} and libmpfr-4.{dll,lib}, generated from
the MSYS2 clangarm64 gmp/mpfr packages with MSVC-compatible import libs via
llvm-dlltool. Headers are shared across arches and unchanged.

* simplify OpenEXR.cmake

* set default arch

* support msix

* ship installer

* try to fix webview2runtime issue

---------

Co-authored-by: Adam Behrman <adam.behrman@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Adam Behrman <abehrman@users.noreply.github.com>
2026-06-25 22:10:49 +08:00
Gabriel 9e635925c8 fix: explicit cast T2A_ to const char* for clang-cl
clang-cl is stricter about converting the T2A_ helper result in this expression.

Cast the conversion helper result explicitly to const char* so the intended
string conversion is unambiguous across MSVC and clang-cl.
2026-06-24 00:36:42 -03:00
yw4zandRodrigo Faselli ff51abc69b Improve incremental compile speed on windows (#12881)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-06-08 11:30:10 -03:00
Ocraftyone 4154785025 Speed up encoding check (#10873)
Update encoding check
2026-05-15 23:45:39 +08:00
SoftFever c04be9ab37 Introducing Orca Cloud: https://cloud.orcaslicer.com (#13414)
* Add OrcaCloud sync platform and preset bundle sharing system

  Introduce OrcaCloud, a cloud sync platform for user presets, alongside
  a preset bundle system that enables sharing printer/filament/process
  profiles as local exportable bundles or subscribed cloud bundles.

  OrcaCloud platform:
  - Auth to Orca Cloud
  - Encrypted token storage (file-based or system keychain)
  - User preset sync with
  - Profile migration from default/bambu folders on first login
  - Homepage integration with entrance to cloud.orcaslicer.com

  Preset bundles:
  - Local bundle import/export with bundle_structure.json metadata
  - Subscribed cloud bundles with version-based update checking
  - Thread-safe concurrent bundle access with read-write mutex
  - Canonical bundle preset naming (_local/<id>/... and _subscribed/<id>/...)
  - Bundle presets are read-only; grouped under subheaders in combo boxes
  - PresetBundleDialog with auto-sync toggle, refresh, update notifications
  - Hyperlinked bundle names to cloud bundle pages

  Co-authored-by: Sabriel Koh <sabrielkcr@gmail.com>
  Co-authored-by: Derrick <derrick992110@gmail.com>
  Co-authored-by: Mykola Nahirnyi <mnahirnyi@amcbridge.com>
  Co-authored-by: Ian Chua <iancrb00@gmail.com>
  Co-authored-by: Draginraptor <draginraptor@gmail.com>
  Co-authored-by: ExPikaPaka <112851715+ExPikaPaka@users.noreply.github.com>
  Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
  Co-authored-by: Ocraftyone <Ocraftyone@users.noreply.github.com>
  Co-authored-by: yw4z <ywsyildiz@gmail.com>
  Co-authored-by: peterm-m <101202951+peterm-m@users.noreply.github.com>

* Fixed an issue on Windows it failed to login Orca Cloud with Google account
2026-05-01 18:01:29 +08:00