Commit Graph
496 Commits
Author SHA1 Message Date
Noisyfox dbcb196f4e Merge branch 'main' into dev/ffmpeg-player 2026-08-26 21:47:20 +08:00
SoftFever 5552ed6cf1 Keep mixed-color filaments intact when the extruder count changes (#15385)
* Keep mixed-color filaments intact when the extruder count changes

The extruder-count spinner resized the filament arrays in bulk at the tail,
which is where mixed-color slots live, so a new filament landed behind the
mix and the sidebar skipped a slot number. It now adds and removes one slot
at a time through the same calls the sidebar's +/- buttons use, so a new
slot opens ahead of the mixed tail and a removal renumbers object filament
ids, painted facets, custom g-code and mixed components rather than
clamping them away.

Drops the vector overload of set_num_filaments(), which this leaves without
callers.
2026-08-26 19:06:33 +08:00
Lam Wei Lun ce277ebbf5 Merge main + clean up code + fix missing include 2026-08-26 16:02:16 +08:00
Ian Chua 858b3024ff fix: tests 2026-08-26 12:04:02 +08:00
Lam Wei Lun cc267055e1 Use proper floating point comparison functions in publish unit test 2026-08-26 11:33:50 +08:00
Ian Bassi a5223279ac Fix uneven corner rounding in multiline infill (#15352)
* Skip straight-run splits in corner smoothing

Teach `CornerSmoother` to treat vertices that only continue a straight segment as part of the same leg instead of rounding them as corners. The smoother now keeps a three-point window so it can emit a corner only once both adjoining legs are known, which avoids unnecessary corner processing while preserving real turns such as hairpins.

* Add regression test for split-leg smoothing

Adds a FillCornerSmoothing regression test covering polylines with an extra collinear vertex in a straight run. The test ensures corner smoothing treats split and unsplit geometry identically, preventing inconsistent rounding radii in triangular/grid infill paths.
2026-08-25 12:46:34 -03:00
SoftFever bfe5f7e63c fix text error 2026-08-25 21:35:43 +08:00
Lam Wei Lun e1a79c4112 Code cleanup and renamed published_* flags to orca_published_* flags to be less generic 2026-08-25 17:37:04 +08:00
Ian Chua c0563be36e Merge branch 'feat/printer-agent-impl' of https://github.com/OrcaSlicer/OrcaSlicer into feat/printer-agent-impl 2026-08-25 14:47:12 +08:00
Ian Chua 462f8dce30 fix: remote do_fetch_filament_info from tests 2026-08-25 14:46:11 +08:00
Ian Chua 8e48312b1f Merge branch 'main' into feat/printer-agent-impl 2026-08-25 13:47:11 +08:00
Ian Chua 4c4eb9a94e Merge branch 'main' into feat/plugin-auditing 2026-08-25 13:46:18 +08:00
Lam Wei Lun 0ba7bae794 Fix conflicts. Update unit tests 2026-08-25 13:18:00 +08:00
Tommaso BianchiandClaude Opus 5 3fd5c3353a A reference is a reference whatever feature holds it
Port of snaporca 1bb9825db0. Four defects from an independent 20-agent audit,
each verified in the code first; two further findings from the same report were
verified OUT and are not in this commit.

remove_feature()/move_feature() remapped Extrude::sketch_ref and a Mate's two
connectors and nothing else, leaving seven of the nine index-bearing fields —
sweep_path_ref, loft_profile_refs[], pattern_curve_sketch, rib_sketch_ref, and
sketch_ref on Revolve, Sweep, Rib and the Surface* family — pointing at whatever
slid into the slot. Quiet by construction: the shifted index still names a real
feature, recompute() succeeds, the solid is built from the wrong profile. The
comment above the loop already required "EVERY field holding a feature index"; the
code under it handled two, because a type switch is only correct on the day it is
written. for_each_feature_ref() visits the FIELDS instead, so a feature type added
later is covered the moment it reuses one. plane_base and axis_plane_a/b are
excluded on purpose and documented at the helper — they encode an ordinal into the
datum-plane list, not an index into features[], and are filed separately. The
delete cascade got the same field-based treatment.

The regression test was run against the pre-fix code to prove it bites: all three
sections fail there, and move_feature returns TRUE while leaving sketch_ref == 1
where it must be 0 — success with the wrong answer, which is what makes this class
expensive.

apply_constraint, commit_entity_constraints and delete_constraint mutated the
recipe with no checkpoint() and no sync_recipe_to_model(), alone among seventeen
mutation sites in that file: Ctrl+Z reached past the constraint edit and discarded
unrelated work, and saving persisted the pre-constraint blob. A rejected constraint
now calls abandon_checkpoint() rather than leaving an undo step that does nothing.

MCP: params["generation"].get<uint64_t>() sat outside the try inside a bare
CallAfter lambda, so one malformed string terminated the process through the wx
event loop; it is type-checked now and the lambda lets nothing escape. The socket
bound with no mode of its own in a world-writable directory — umask around bind()
plus chmod, and it refuses to listen rather than listen wide. The reply write is no
longer a bare write(), which could SIGPIPE the app when a client hung up.

Kernel suite on this fork: 190 cases / 2562 assertions, green. GUI target compiles.
The full ladder gate ran on snaporca (ALL LADDERS HELD — gestures 98/98, offer
108/108, corpus and corpus-scale green) and fork-check parity holds at 17 identical
/ 8 diverging as expected, which is what makes that gate transferable here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MrMzTpAf78U4NG2M8jfvHY
2026-08-24 19:00:45 +02:00
SoftFever e342698d8e Update sublayer option check. Add validation warning for gradient mixed filament without sublayer mixing 2026-08-24 15:30:02 +08:00
Lam Wei Lun 3ed1f64f2e Merge main 2026-08-24 14:19:49 +08:00
Lam Wei Lun 2aff31c07a Published 3MF: match filament slots by exported identity without a Type requirement 2026-08-24 13:19:57 +08:00
Tommaso Bianchi 6e7f6429fa A body carries its own name, because a body is not its first feature
User report 2026-08-23, and it is right: "you have renamed the feature extrusion,
not the body. i clicked rename on the body feature tree and the feature extrude
changed name. this means that you consider the extrusion = the body. this is very
far from truth as a body can contain several extrusions."

That is exactly what the previous commit did. It resolved a selected body to
CadBody::source_feature and renamed THAT feature, on the reasoning that a body has
no name of its own. The reasoning described an implementation detail — CadBody::
name is derived and restamped on every recompute — and mistook it for the user's
model. An Extrude, a Cut and a Fillet all land on the same body: the maker is one
operation in its history, and renaming it renames the wrong object.

A body now has a name of its own. CadBody::user_name, set only by a rename, is:
  - carried across recompute() by body index, next to the per-body colour override
    and under the same index contract the GUI already relies on for visibility and
    Move — without which a name would survive exactly until the next feature;
  - written into the recipe, because bodies are recomputed and never serialised, so
    a name has nowhere else to live and would otherwise vanish on reopen;
  - shown on the Bodies row ahead of the derived maker name, as "Body N — name",
    with the number still leading because every status line, the interference
    report and the mate errors identify a body that way.

The recipe block is APPENDED after the variables block rather than given a version
bump. A build that predates it reads features and variables, returns, and never
looks at the trailing bytes — so yesterday's projects open here and today's
projects still open there. A bump would have cost every project written today its
readability by the previous build, for one optional field.

Renaming a FEATURE is unchanged. The feature tree renames features; the Bodies
list renames bodies; neither reaches into the other.

WHAT THIS COST, and why it is written down. Getting here took two wrong turns
inside one fix, both mine:

  1. UnselectAll() -> Unselect(). Both trees are wxTR_SINGLE, where UnselectAll()
     — the MULTI-selection call — does nothing. That was the one-word reason the
     rename had been vetoed everywhere (BEGIN_LABEL_EDIT refuses while
     tree_body_selection() >= 0). Fixing it turned a pair of harmless no-ops into
     a real loop: the two lists clear each other so "the target" is unambiguous,
     so clicking a body row ran apply_body_row -> m_tree->Unselect() -> the feature
     tree's SEL_CHANGED -> m_parts->Unselect(), which cleared the row just clicked.
     The handler now clears the other list only when it actually holds a selection.
  2. Trusting a screenshot taken after a polluted run. A leftover Rib card had
     shifted the whole panel, so a click measured against it landed nowhere near
     the row. Relaunch, then measure.

VERIFIED, on the rig and in the kernel:
  body renamed          ('Extrude', user_name=False) -> ('Bracket', user_name=True)
  features untouched    ['Sketch', 'Extrude'] before and after
  survives a recompute  add a Hole to the same body: features become
                        ['Sketch', 'Extrude', 'Hole'], body stays 'Bracket'
  survives the recipe   serialize -> deserialize -> 'Bracket'

New kernel test "a body carries its own name, through recompute and the recipe"
pins all three properties; the suite is 189 cases / 2547 assertions.

Gate green: ALL LADDERS HELD — offer table matches the atlas, kernel suite,
engine rungs 1-8, 977-sheet corpus + the heaviest sheets, gesture ladder 98/98,
offer ladder 108/108.
2026-08-23 19:09:17 +02:00
SoftFever 2b1499a087 clean up comments 2026-08-23 22:43:41 +08:00
SoftFever 4a32a9e066 Match mixed filament swatches to the editor's gradient preview
The sidebar Mixed Filament list, the extruder icons, the color painting
gizmo and the canvas filament bar now show the same bottom-to-top fade the
Edit Mixed Filament preview shows, custom gradient curves included, instead
of a horizontal fade between the two component colours. Ordinary and vendor
multi-colour filaments are drawn exactly as before.
2026-08-23 22:11:49 +08:00
SoftFever 38d51783ae Refresh the mixed filament list when a component preset changes 2026-08-23 22:11:49 +08:00
SoftFever 9985688b5b Blend mixed slots in the machine-send and AMS-sync thumbnails 2026-08-23 22:11:49 +08:00
SoftFever d27766aff0 Reject a mixed filament as the wipe tower filament 2026-08-23 22:11:49 +08:00
SoftFever 0c3d7c6ed1 Expand mixed slots in by-object filament bookkeeping 2026-08-23 22:11:49 +08:00
SoftFever 2131ef0560 Keep mixed filaments across app restarts 2026-08-23 22:11:49 +08:00
Ian Bassi 3c37bf9ca4 Import project 2026-08-23 22:11:48 +08:00
Ian Bassi 42f708bbb6 Fixes from Full spectrum port
https://github.com/OrcaSlicer/OrcaSlicer/pull/14383
2026-08-23 22:11:48 +08:00
Ian Bassi 86a7e93a48 Layer subdivision fix 2026-08-23 22:11:48 +08:00
Ian Bassi 8fea099d99 BBL Port Color Mix Base 2026-08-23 22:11:48 +08:00
Tommaso Bianchi 4693542d0d Port from snaporca: the solver's 1024-unknown cliff, and the scale rungs
Two commits carried across (snaporca 579a9a9162, f68613cfc5).

Past about 480 entities a sketch had NO constraints at all and said nothing: libslvs
declares MAX_UNKNOWNS = 1024 and is handed every entity in the sketch at two params
per point, so the whole system came back TOO_MANY_UNKNOWNS and try_add_constraints
rolled the entire inferred batch back. From there no dimension could ever be applied.
Constraints only couple entities that share a point, so the solver now falls back —
only on TOO_MANY_UNKNOWNS — to solving connected components separately and committing
all-or-nothing. The auto-constraint pass batches its Horizontal/Vertical constraints
instead of one solve each, which is what kept the bulk path fast once solves started
succeeding: a 1204-entity load went 1585 ms -> 562 ms.

Plus the scale rungs (a thousand-entity plate drawn on by hand; the heaviest real
drawings graded and timed), the --step 1 fix that used to select nothing while
reporting a clean run, and scripts/ladder-all.sh as the one-command gate.

Parity 17 identical / 8 diverging as expected. Kernel suite here: 188 cases /
2532 assertions, including "a sketch past the solver's unknown limit still solves".

snaporca-yww4, snaporca-x6v7, snaporca-j6sr
2026-08-23 02:04:03 +02:00
Tommaso Bianchi 82db99f337 Port from snaporca: sketch-only projects save, scripted geometry arrives exact,
and a ladder that draws with the mouse

Three commits carried across (snaporca 4ffd60eacb, 421055c2ec, b71216ce0b):

1. A design made only of sketches must survive being saved. CadDocument::recompute
   returned false with "no solid-producing features" for a document that has no
   solid, and two callers read that as "unusable": the GUI syncs the 3MF recipe
   only after a successful recompute, so a sketch-only design was saved with no
   recipe at all, and deserialize_recipe ends with `return recompute()`, so even a
   project that carried one was refused on load. Having nothing to build is now a
   success; a feature that MEANT to build a solid and produced none still fails.
   DesignPanel::refresh_tree syncs the recipe too, for the paths that call
   m_doc.recompute() directly.

2. Scripted geometry arrives exact. The Horizontal/Vertical inference window and
   the endpoint weld window both close to zero for add_entities_scripted; void
   attribution probes from a point strictly inside each loop instead of from its
   first vertex. Corpus rung 39 graded / 39 fully clean, was 35 with 6 failures.

3. scripts/gui-ladder.py — 17 rungs, 84 properties, all driven by synthetic clicks
   and typed values rather than through the socket.

Parity 17 identical / 8 diverging as expected. Kernel suite here: 188 cases /
2532 assertions.

snaporca-mtav, snaporca-8xg1, snaporca-5hvl, snaporca-730j
2026-08-23 01:22:32 +02:00
Tommaso BianchiandClaude Opus 5 942f6c28c7 Port: mirror emits a half that continues the chain
Carries snaporca 0231bd5b68. Parity holds: 17 files identical, 8 diverging as expected.

A reflection reverses orientation, so mirror_entities now hands the reflected half back reversed
in ORDER and flipped per ENTITY — an arc swapping its angles as well as its ends, a spline
reversing its control points. Appending it to the source then yields one walkable chain instead
of two halves meeting head-to-head, and a mirrored CCW loop stays CCW.

This is the producer half of the confusion that cost three defects; the consumers (offset, and
the exact loop area) keep their defensive handling, because that is what makes them correct for
hand-built and imported sketches rather than only for geometry this function produced.

Contract change, carried with the reason: a mirrored line's p0 is the reflection of the SOURCE's
p1, and a mirrored CCW arc keeps a POSITIVE sweep — the reflection negates it, walking it the
other way negates it again. Both [SketchEdit] cases updated, and a new [SketchProfile] case
"a mirrored half continues the original chain" pins the property directly.

Kernel here: all tests passed, 2687 assertions in 232 test cases. GUI target builds and links.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 15:00:50 +02:00
Tommaso BianchiandClaude Opus 5 cbbd24dcb4 Port the exact loop area, the offset traversal fix, and the 2D sketch ladder
Carries snaporca 572f794c84, d0f9a0052a, 9f7e4e3627 and 3974f8a170. Parity holds: 17 files
identical, 8 diverging by their expected counts.

EXACT AREA. A loop's area is now integrated entity by entity in traversal order — Green's
theorem — instead of being shoelaced over the render polyline, which faceted every arc into 24
chords and lost 2.02 mm2 on a 3706.86 mm2 stadium. 0.054%, invisible on screen, and wrong in a
number reported as "the area".

OFFSET FOLLOWS THE TRAVERSAL. Offsetting a mirrored profile put one half on the wrong side and
split the loop in two, because the chainer only followed p1->p0 links and each entity's offset
side was taken from its stored direction. Chains are now orientation-aware, seeded at a free end,
offset by `reversed ? -d : d`, and normalised head-to-tail on the way out — so offset is correct
for any input ordering and its own output cannot reintroduce the problem.

Both are the same underlying lesson, which has now cost three separate defects: an entity's
STORED direction is not its direction of TRAVEL around the loop.

THE LADDER. scripts/sketch-ladder.py is a graded suite of 2D sketches judged the way a person
judges them — VERTEX, LENGTH, ARC, TANGENT, SYMMETRY, CLOSED — with area only as a cross-check,
because area is derived and nobody can confirm it by eye. Eight rungs from a rectangle up to
MPD5 from the StudyCadCam corpus, a dia 27 x 95 pin reproduced as its revolve half-profile with
the R5 fillet tangency solved exactly. Entirely 2D: no extrude or any solid feature.

Kernel here: all tests passed, 2681 assertions in 231 test cases, including the new
"profile: a mirrored half offsets as one loop, not two". GUI target builds and links.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 14:26:46 +02:00
Tommaso BianchiandClaude Opus 5 5d7fc8c545 Port the sketch layer work from snaporca: offset chains, right-click, MCP verbs
Carries snaporca 971320e129, 6b049f0dc6, 4aae782029, 444d59f212, 74cf3d7e54 and the build
guards from 597557a6e4. Parity re-verified after every hunk: 17 files identical, 8 diverging by
their expected counts — DesignPanel.cpp still 32, DesignCanvas.cpp still 16, which is the proof
each hunk landed on the right side rather than being copied over a real divergence.

OFFSET OFFSETS THE CHAIN. Per-entity offsetting returned a closed rectangle as four parallel
segments that no longer touch, so entities_to_wires gave four OPEN wires and nothing could be
extruded. offset_entities now chains by shared endpoints and repairs each seam by mitering the
neighbours to their intersection. Second bug, invisible to any single-entity test: +d meant
"left of travel" for a line but "radius + d" for an arc regardless of sweep, so a slot outline
offset with its straights going one way and its caps the other. The convention is now written on
the declaration and pinned by a test.

tests/libslic3r/test_sketchprofile.cpp is new and asserts the LOOP rather than coordinates —
the property that decides whether a profile can be built, and the one the existing single-entity
[SketchEdit] cases cannot see. Its include is catch2/catch_all.hpp here: this fork ships Catch2
v3 while snaporca is on v2, which is why the test files are a tolerated divergence.

RIGHT-CLICK PICKS WHAT YOU POINTED AT, so a line's own verbs are offered instead of the
empty-selection vocabulary; sk_delete stops sharing btn:delete with the feature tree; and an
element's defining number (length / radius / diameter / angle / distance) can be typed, from the
menu or from V.

TWELVE MCP SKETCH VERBS. The socket had ~40 verbs and none touched a sketch, so the 2D layer
could only be exercised by driving a GUI with synthetic clicks. sketch_describe reports each
closed loop, the loops it encloses as voids, exact areas, and where a chain is still open;
sketch_validate/sketch_heal are FreeCAD's ValidateSketch — find vertices that overlap within a
tolerance but carry no coincidence, then weld them AND record the constraint, so a loop closed
by floating-point luck becomes one closed by construction. scripts/mcp-sketch-smoke.py is the
loop that asserts all of it.

Kernel suite on this fork: all tests passed, 2677 assertions in 230 test cases. The GUI target
links against the rebuilt deps image (the wxInspector blockage is gone) and the binary carries
the new verbs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 09:00:26 +02:00
Kris Austin 4b397fc2cc fix: slice the same model to the same lightning infill every time (#15311) 2026-08-21 22:24:39 -03:00
Lam Wei Lun 28b1f3ab03 Merge main 2026-08-21 19:05:15 +08:00
Lam Wei Lun 36e5d4770b Code cleanup and fixes 2026-08-21 19:04:10 +08:00
Lam Wei Lun 95e392e206 Code cleanup 2026-08-21 18:36:30 +08:00
SoftFever 1c405f8a2c filament_id v4.1: resolve retired QD_* ids in the Qidi box miss path
QidiPrinterAgent composes QD_<series>_<vendor>_<typeidx> setting ids from
device enums and previously degraded any slot whose id matched no visible
preset to generic-by-type. Plan v4.2 retires every QD_* preset id to the
succession ledger, so insert the ledger walk between the direct match and
the generic fallback: a composed QD_* id now forwards to its family's
minted OF* successor exactly like every other retired id.

Behavior-neutral until the ids are actually retired (the ledger holds no
QD_* keys yet, and live QD_* presets still match directly).

Also documents the code<->ledger lockstep on the hardcoded non-numeric-
series fallback table (QD_1_0_1/_11/_41/_50) and adds a C++ test pinning
that the succession walk is key-format agnostic.

Gates: libslic3r_tests [filament_id] 14 assertions green; libslic3r_gui
compiles.
2026-08-21 18:34:20 +08:00
SoftFever f3fa3a34bd Merge branch 'main' into feature/filament_id 2026-08-21 17:30:12 +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
Ian Chua ba7fdb8ceb Merge branch 'main' into feat/printer-agent-impl 2026-08-21 14:17:56 +08:00
Lam Wei Lun a0e95abebe Fix unit test: 2026-08-21 13:26:17 +08:00
Lam Wei Lun aa3ce35683 Published 3MF: silent geometry-only fallback in old versions via tag/config-less export 2026-08-21 13:00:32 +08:00
Lam Wei Lun 19a0407560 Merge main 2026-08-21 12:28:24 +08:00
Ian Bassi ca65f0fd8e Normalize the junction direction vector over XYZE (#15308)
* Normalize the junction direction vector over XYZE

calc_vmax_junction_deviation() treats the dot product of two jd_unit_vec as a
cosine, but the vectors were scaled by 1 / block.distance, which is the XYZ
length. On an extruding move the E component then pushes the 4D norm above 1 and
the dot product below -1, so the corner reads as straighter than it is and is
planned too fast -- the more so the higher the flow. Measured on a 6 degree
corner at scv 5: 86.9mm/s with no extrusion, 94.4mm/s at 0.029mm/mm, 150.0mm/s
at 0.1mm/mm.

Neither firmware does that. Marlin normalizes over XYZE for any extruding move
(planner.cpp: `if (... || esteps > 0) normalize_junction_vector(unit_vec)`) and
Klipper leaves E out of the cosine entirely, dotting only axes_r[0..2]
(toolhead.py::Move.calc_junction). Normalizing satisfies both: with E normalized
in, the cosine differs from the XYZ-only one by ~1e-5 at printing flow rates.

This is a deliberate divergence from PrusaSlicer, which still scales by
1 / distance -- it carries an older Marlin's behaviour.

Travel moves are unaffected, their vector was already unit length.

Reported by Copilot in review of #15304.

* Test that extrusion rate does not change corner planning

The junction deviation tests were all travel-only, which is exactly why the E
component of the junction vector went unchecked. Cover it: the same corner has
to be planned the same whether nothing, an ordinary 0.42 x 0.2 line, or a fat
large-nozzle line is extruded through it, on both Klipper and Marlin 2.

Reported by Copilot in review of #15304.
2026-08-20 18:50:23 -03:00
Tommaso Bianchi 52d16e4218 Merge remote-tracking branch 'prfork/cad-mainline' into cad-mainline 2026-08-20 17:45:15 +02:00
Tommaso Bianchi 2b02a8e3dd Design tab: move the CAD sources into their own folder
Review request on PR #15238: "Place CAD-related files (e.g. CadDocument/
GeometryEngine) into a separate folder."

  src/libslic3r/CAD/     the kernel — CadDocument, GeometryEngine, the four
                         Sketch* units, SketchSolver, ThreadStandards
  src/slic3r/GUI/CAD/    the tab — DesignPanel, DesignCanvas, DesignSketchTool,
                         SketchInlineEditor, McpControl, generated DesignOffer

Pure relocation: no line of logic changes. Two include rewrites follow from it —
files that moved re-spell their own neighbours against src/ (already on the
include path), and files that did not move pick up the new folder. docs and
docs/ux/mockups/gen_offer_table.py follow the same paths.

Verified: libslic3r, libslic3r_gui and libslic3r_tests all build, CAD suite green
at 2518 assertions in 194 test cases, and the sibling fork builds identically —
17 shared sources still byte-identical, 8 diverging by their expected counts.
2026-08-20 17:44:34 +02:00
Ian Bassi aaa8e98bb0 Time estimator fixes (#15304)
* Plan corners with junction deviation where the firmware uses it

The time estimator only ever had the classic per-axis jerk model, which limits a
corner by the largest single-axis component of the velocity change. That is
anisotropic: the same corner is allowed sqrt(2) more speed on a diagonal than on
an axis, which paints a four-lobed ripple around every circular wall in the
actual speed and actual flow views, worst on small parts whose walls are made of
short segments.

Klipper has no classic jerk at all and Marlin 2 has none while M205 J is in use;
both plan corners with junction deviation, which sees only the corner angle. Add
that model and use it for those machines:

  - Klipper: derived from the square corner velocity, as the firmware does
    (jd = scv^2 * (sqrt(2) - 1) / max_accel), reading the scv from
    machine_max_jerk_x, where process_SET_VELOCITY_LIMIT() already stores
    SQUARE_CORNER_VELOCITY.
  - Marlin 2: machine_max_junction_deviation, which was already loaded into the
    machine limits but never reached the planner.
  - Every other flavor keeps the classic jerk path unchanged.

The model has no per-axis jerk floor, so this also drops the hard slow spot the
estimator drew at the start of every loop from machine_max_jerk_e.

Toolpaths are unaffected: on a full export the only lines that change are M73.

The junction deviation maths, including Marlin's JD_HANDLE_SMALL_SEGMENTS arc
approximation, is ported from PrusaSlicer's src/libslic3r/GCode/GCodeProcessor.cpp.
The Klipper mapping is not in PrusaSlicer, which ignores SET_VELOCITY_LIMIT.

* Add tests for junction deviation corner planning

Cover the three properties the change rests on:

  - a right angle on Klipper is planned at exactly the square corner velocity,
    the identity that makes the scv to junction deviation mapping correct, and a
    shallow corner is planned far faster than per-axis jerk allows;
  - junction deviation gives the same speed whatever the corner's orientation,
    while classic jerk keeps its sqrt(2) spread, which is the four-lobed ripple;
  - machines that do not plan with junction deviation are provably untouched,
    including a Marlin 2 printer that has it disabled.
2026-08-20 09:16:02 -03:00
Ian Chua 0d0d281d0c feat: parse nozzle information for qidi and moonraker printer agents 2026-08-20 20:11:31 +08:00