Commit Graph
826 Commits
Author SHA1 Message Date
harrierpigeon e341a9b84e Merge remote-tracking branch 'upstream/main' into haryr/aug25-rebase
# Conflicts:
#	src/libslic3r/GCode/ToolOrdering.cpp
#	src/libslic3r/Print.cpp
#	src/libslic3r/PrintApply.cpp
#	src/libslic3r/PrintConfig.cpp
#	src/slic3r/GUI/Tab.cpp
2026-08-30 23:30:51 -05:00
Lam Wei Lun 6ffb20a7f5 Update translations. Change import filament message to warning instead. Change messages to be simpler 2026-08-31 12:08:08 +08:00
Clifford Garwood 9abc3bcaf7 Merge upstream main: mixed-filament extruder-count fix, FFmpeg camera view, plugin storage API, warning cleanups 2026-08-30 23:58:48 -04:00
Lam Wei Lun 06517e623f Fixes issue where user imports a 3MF file where filament slots exceeds the maximum number of slots that user has on its printer 2026-08-31 10:46:23 +08:00
Tommaso Bianchi b5ead4b29f Pull a body's edges into a sketch as construction references
Port of snaporca e635627b81. Parity OK: 17 files identical, 8 diverging at their
expected counts.

The last reference an industrial sketcher offers that this one did not: Onshape's
Use, SolidWorks' Convert Entities. Project already turned a body's 3D edges into
2D Lines, Circles and Arcs on a plane, but the result landed in its OWN feature,
so while drawing in one sketch you could not borrow an existing body's edge and
constrain to it.

No new geometry code: Project's per-edge conversion loop is factored into
project_edges_to_entities() and called from a second entry point that appends
into an EXISTING sketch with construction = true. The loop appears once now,
not twice.

Construction is what makes it cheap and safe: SketchEngine already skips
construction entities when building wires, so the references guide without being
built, and being otherwise ordinary entities every constraint from this epic --
Collinear, EqualRadius, the axis-projected distances, PointOnLine, Symmetric --
works against them for free.

Part of this refactors working code, so Project's behaviour identity is the
invariant; the existing [CadDocument][project] cases guard it and a new case
asserts a Project feature still emits construction == false.

project_edges_into_sketch returns the number of entities appended, or -1 on a bad
reference rather than throwing.

VERIFICATION LIMIT, as with the previous four commits: this fork's kernel suite
still cannot run (find_package(assimp) at configure time, snaporca-w80c). Shared
sources are byte-identical to snaporca's, where kernel is 7700 assertions / 270
cases and ALL LADDERS HELD 7/7.
2026-08-31 04:15:05 +02:00
Tommaso Bianchi fac3cf44df Infer parallel, perpendicular, equal radius and tangent while drawing
Port of snaporca 2d36d28770. Parity OK: 17 files identical, 8 diverging at their
expected counts.

infer_axis_constraint returned only Horizontal or Vertical. On the
CAD-1000-hours corpus the top two transitions are sketch_dim -> sketch_draw
(5896) and back (5756): the signature of geometry that does not self-constrain
as it is drawn.

Every rule requires the relation to be ALREADY TRUE within tolerance, so nothing
the user drew is moved; parallel/perpendicular and tangent additionally require a
shared endpoint.

TWO LIMITS THE CORPUS RUNG FORCED, neither visible to the unit tests:

1. At most ONE constraint per rule per new entity, not one per PAIR, and no
   one-at-a-time fallback for the relations batch. EqualRadius has no locality
   restriction, so 200 equal holes produced ~20000 candidates; the rejected batch
   then cost a solve per constraint and pinned the app at 95% of a core with the
   MCP socket unresponsive.

2. Relations only for gesture-sized batches. "A scripted add is not a drawn
   gesture" is already this file's rule at its bulk call site (snaporca-8xg1), and
   EqualRadius also couples geometrically distant entities, merging independent
   connected components and defeating the partitioning that makes large sketches
   solvable (snaporca-yww4). With the cap alone geometry stayed correct (32/32
   sheets clean) but seven of the largest timed out, including MPD681 -- the sheet
   that call site's own comment names.

Also fixes the tolerance leak behind 2: the bulk path asks for exact inference
with ang_tol_rad = 0 but len_tol_frac kept its 0.01 default.

ALSO independent of this feature: run-kernel-tests.sh defaulted to
TAGS=[CadDocument] while four CAD test files carry their own tags and nothing
selected them (2624 assertions / 206 cases reported, 7648 / 264 actual). All 58
dark cases were passing; the coverage was never exercised.

VERIFICATION LIMIT, as with the previous three commits: this fork's kernel suite
still cannot run (find_package(assimp) at configure time, snaporca-w80c). Shared
sources are byte-identical to snaporca's, where kernel is 7651 assertions / 265
cases and ALL LADDERS HELD 7/7.
2026-08-31 03:43:08 +02:00
Tommaso Bianchi 45494c6035 The origin and the two axes become things you can constrain to
Port of snaporca 5b1294de59. Parity OK: 17 files identical, 8 diverging at their
expected counts.

Every industrial sketcher gives you the origin and the axes as references. Here
the origin was only a SNAP target and the axes did not exist, so Symmetric needed
a third picked ENTITY as its mirror axis: symmetry about the sketch's vertical
axis first required drawing a construction line.

Every constraint reference resolves through four lambdas in the solver
(valid/ptOf/primOf/coordOf), so teaching those about three negative sentinel
indices makes the origin and both axes available to EVERY constraint type at
once. No new SketchEntity type, no serialization change; -1 still means "unset".
The references live in G_FIXED and add no degrees of freedom, which a test
asserts via the reported DoF.

SymmetricAboutY / SymmetricAboutX are two buttons that need no third pick and no
construction line. Making the axes clickable in the viewport is deliberately left
out: that is canvas hit-testing work with its own risks.

Recorded in the tests because it will catch the next person: sys.dragged[] is
populated only during a drag, so a plain sketch_solve of an UNDER-constrained
system may move any free parameter -- solvespace runs Newton, it does not
minimise movement. PointOnLine onto an axis is one equation in two unknowns and
the point legitimately slides along it. Those tests pin the free direction
instead of asserting the other coordinate is untouched.

VERIFICATION LIMIT, as with the previous two commits: this fork's kernel suite
still cannot run (find_package(assimp) fails at configure, snaporca-w80c). The
shared sources are byte-identical to snaporca's, where kernel is 2624 assertions
/ 206 cases and ALL LADDERS HELD across all seven rungs.
2026-08-31 02:29:24 +02:00
Tommaso Bianchi a63bba2d55 Horizontal and vertical distance dimensions
Port of snaporca 9fa304c77a. Parity OK: 17 files identical, 8 diverging at their
expected counts.

The everyday dimension in SolidWorks and Onshape, and this kernel had no form of
it. Distance constrains the straight-line gap; LockX/LockY pin one point's
ABSOLUTE coordinate. Neither relates two points along an axis.

DistanceX/DistanceY emit SLVS_C_PROJ_PT_DISTANCE against two unit direction
lines built in the solver's G_FIXED group, so they add no degrees of freedom.

THE DIRECTION IS SIGNED, and getting it backwards is silent. libslvs defines a
LINE_SEGMENT's direction as point[0] - point[1] (entity.cpp) and
PROJ_PT_DISTANCE constrains (pB - pA).dot(dir) (constrainteq.cpp:234), so the
reference lines are built head-first to mean +X and +Y.

The same signedness was a real defect in the GUI: the inline editor was
pre-filled with |delta|, so when the closest endpoint pair ran right-to-left,
opening the dimension and accepting the number shown would flip the point to the
other side of its anchor. Opening a dimension and accepting its own value must
be a no-op. The refs are now ordered so the shown value is positive.

On the CAD-1000-hours corpus, dimensioning and constraining is 31.9% of all
observed CAD time -- the largest single class, 7.6x feature operations. This is
the item in the constraint epic that lands most directly on it.

VERIFICATION LIMIT, as with the previous commit: this fork's kernel suite still
cannot run (find_package(assimp) fails at configure time, snaporca-w80c). The
shared sources are byte-identical to snaporca's, where kernel is 2603 assertions
/ 200 cases and ALL LADDERS HELD across all seven rungs.
2026-08-31 01:46:09 +02:00
Tommaso Bianchi a1f2a2687a Equal radius and Collinear, and one Equal button that knows what it picked
Port of snaporca 9ec6405e2d. Parity OK: 17 files identical, 8 diverging at their
expected counts (DesignPanel.cpp 32, test_slvs_constraints.cpp 3).

Measured on the CAD-1000-hours corpus: 51.5% of observed CAD time is 2D sketch
work, and dimensioning/constraining alone is 31.9% -- the largest single class.
Two constraints every industrial sketcher has were missing here.

EqualRadius fixes a dead end rather than adding a feature. Picking two circles
and pressing Equal emitted EqualLength, which maps to SLVS_C_EQUAL_LENGTH_LINES
and constrains nothing on a curve: a silent no-op with no error. Equal is now
one button with two meanings, as in Onshape and SolidWorks.

Collinear emits PARALLEL plus PT_LINE_DISTANCE=0 rather than PT_ON_LINE, whose
internal valP param this libslvs port leaves at 0, drifting an already-collinear
pair.

Both types are appended at the END of SketchConstraintType: cereal serializes it
positionally, so inserting elsewhere reinterprets every saved recipe.

VERIFICATION LIMIT, stated rather than implied: this fork's kernel suite could
NOT be run. scripts/CAD/run-kernel-tests.sh fails at CMake configure time on
find_package(assimp), before any source compiles -- a pre-existing deps gap
(snaporca-w80c), not this change. The shared sources are byte-identical to
snaporca's, where the full gate passed: kernel 2588/195 and ALL LADDERS HELD
across all seven rungs.

Also fixes two defects in this fork's scripts/CAD/run-all-checks.sh:
  - `cd $(dirname $0)/..` landed in scripts/ instead of the repo root, so every
    rung looked for itself under scripts/scripts/. Broken since the script moved
    into scripts/CAD/; the three sibling scripts were fixed then and this was
    missed, so the gate has not run since.
  - C defaulted to snaporca-gui, the OTHER fork's rig container, so this fork's
    gate would drive snaporca's app and report green about the wrong binary.
    run-kernel-tests.sh:31 documents the identical defect being fixed once
    already for the build volume; this is the third instance.
2026-08-31 01:09:43 +02:00
SoftFever ea0242feda Merge branch 'main' into feature/filament_id 2026-08-30 20:54:33 +08:00
SoftFever cb4a90402f Merge branch 'main' into feature/texture_displacement 2026-08-30 13:52:54 +08:00
SoftFever dfb102f68d Merge branch 'main' into dev/ffmpeg-player 2026-08-29 22:44:08 +08:00
SoftFever 8f014de84c Load the CAD recipe from projects saved before it was renamed
The recipe's 3MF entry moved from Metadata/SnapOrca_cad.bin to
Metadata/orca_cad.bin, so projects saved by earlier builds opened with an
empty Design tab. Both 3MF backends now read either name and write only the
new one; the recipe version advances to 6 to mark the move.
2026-08-29 01:56:48 +08:00
Tommaso Bianchi 13d5eac891 Move the Design-tab scripts into scripts/CAD/ and name them by role
Requested by SoftFever on PR #15238: ten of these had accumulated loose in
scripts/ next to ~20 unrelated upstream ones, with names that only meant
something to whoever wrote them. They now sit in scripts/CAD/, mirroring the
src/libslic3r/CAD/ and src/slic3r/GUI/CAD/ split, and the verb in the name is
the role: build- produces a binary, start- brings something up, run- runs a
suite, check- asserts one thing against a live app.

  kernel-test.sh        -> CAD/run-kernel-tests.sh
  ladder-all.sh         -> CAD/run-all-checks.sh
  sketch-ladder.py      -> CAD/check-sketch-engine.py
  ladder-corpus.py      -> CAD/check-sketch-engine-corpus.py
  gui-ladder.py         -> CAD/check-gui-sketching.py
  offer-ladder.py       -> CAD/check-gui-context-menu.py
  mcp-sketch-smoke.py   -> CAD/check-mcp-sketch.py
  rig-build.sh          -> CAD/build-gui.sh
  docker-iter-build.sh  -> CAD/build-gui-incremental.sh
  gui-session.sh        -> CAD/start-headless-gui.sh

"Ladder" was the worst of them: it named the shape of the test (rungs of
increasing difficulty) rather than what the test proves, so nothing in the
directory listing told you which one needed a GPU and which was pure kernel.

Every reference rewritten -- the docs, the cross-calls between the scripts,
Dockerfile.deps, and the container-side /OrcaSlicer/scripts paths. The three
shell scripts resolve REPO relative to themselves and now sit one level
deeper, so that walk went from /.. to /../.. . The copies these push into a
container's /tmp were renamed to match, or the container would have kept the
old names alive.

Two runtime paths deliberately NOT renamed. /tmp/orca-rig-build.lock is a
cross-fork contract -- both forks take the same lock so two concurrent builds
serialise instead of OOMing the box, and renaming it on one side silently
removes that guard. /tmp/gui-session.log is a runtime artefact, not a script.

Added scripts/CAD/README.md: what each script proves, what it needs, and the
two constraints that have each cost a session (never build inside the GUI
container; a window manager is required or synthetic keys are ignored).

On CI, which was the other half of the request: the kernel suite is already
there and always has been. The cases are registered in
tests/libslic3r/CMakeLists.txt under if (SLIC3R_CAD), which defaults ON and no
workflow turns off, so they build into libslic3r_tests and run under ctest on
every platform via unit_tests.yml -- like any other unit test, needing no new
job. They have simply never been seen to run, because the workflows on this PR
are still awaiting maintainer approval. run-kernel-tests.sh is the local loop
over the same cases, and it is the only script here CI could run: the other
six need an OpenGL canvas and synthetic input.

Verified: scripts/CAD/run-kernel-tests.sh from its new location, all tests
passed, 2562 assertions in 190 test cases.
2026-08-28 19:34:03 +02:00
SoftFever 149ae6c7fe Merge branch 'main' into cad-mainline 2026-08-28 19:08:46 +08:00
Lam Wei Lun a62db72e02 Publish 3MF: import-side hardening and test coverage
Validate mixed-filament definitions during the published material pass: definitions whose components reference slots that do not exist or hold other mixed filaments, or that carry fewer than two components, are reported through the shared skipped_keys channel instead of shipping a mix the GUI integrity check would only flag later.

Fix the slot-limit exhaustion report being silently dropped: it wrote to published_config->skipped_keys, which the pass's final move-assignment from the local vector clobbers. All rejections now go through the local.

Remove the unreachable persist branch from add_detached_preset: no caller passes save_to_project=false, so the parameter is gone and the copy is always project-embedded.

Tests: cover the exhaustion path, the new definition validation, the identity-tier matching matrix (including substitute reporting), the structural-key denylist, whole-vector size-mismatch skips, relocation payload degradation, the "(Published 2)" uniquify chain, mixed blend colours staying out of shared preset configs, and duplicate-slot last-wins. Also fix the legacy-3mf scenario passing vacuously behind an if-guarded assertion. All existing published/3mf tests pass unchanged.
2026-08-28 15:24:03 +08:00
Lam Wei Lun 25b008ba3e Merge main 2026-08-28 10:38:36 +08:00
Lam Wei Lun 28b325805b Fixed issues with remapping mixed filaments when importing published 3MF 2026-08-28 10:37:54 +08:00
Kris Austin 6d1584844e fix: STEP part names with accented characters import as numbers (clears 6 warnings) (#15406) 2026-08-27 19:06:06 -03:00
SoftFever 2efb29d9c7 Make the CAD recipe tests self-contained and cover the importer 2026-08-28 02:48:02 +08:00
Clifford GarwoodandClaude Opus 5 018091f43b fix(imex): let the mixed-filament refusal outrank the multi-color rule
A mixed filament is unsupported in a parallel mode outright, but the rule saying
so ran third. A plate carrying a blend plus any second filament tripped the
multi-color rule's used > 1 gate first and was told its active tools all sit on
one gantry -- a diagnosis of a multi-color print the user never configured,
whose remedy is to go rework the mode's tool roster. The blend was never
mentioned. Move the check ahead of both rules below it; being unsupported
regardless of routing or topology, it dominates them.

Nothing is masked that leads anywhere else: every branch of
imex_multicolor_block_reason is itself confined to non-primary modes, so the
mixed message's remedy -- switch this plate to Primary -- silences those too.

Say "Mixed filaments", not "Blended". Every other string in the app calls these
mixed, including the button that creates one and the sibling refusal for the
wipe tower filament, so the user had no way to connect the message to the
feature it names.

Three comments in the block were wrong, and two of them were newly wrong. The
routing rule's bounds-check note still said "Blended slots are out of range by
construction, but they never reach here -- the rule above returns first": the
rule above is now the multi-color one, which does not return first for a single
mixed filament, and out-of-range is not guaranteed at all. Mixed slots are kept
at the tail of the filament arrays by convention, not by enforcement --
PresetBundle::set_num_filaments grows filament_is_mixed with resize(), so
raising a printer's extruder count with a blend present lands physical slots
after the mixed one. The scan is position-agnostic and stays correct; only the
stated reason was wrong.

The same discovery makes the empty-routed_list guard live rather than the dead
code it was described as. Print::apply() normalises physical_extruder_map before
validate() runs, so an unauthored map is never the cause -- but a printer with
more filaments than logical extruders leaves the tail slots outside the map, and
raising the extruder count does exactly that.

The new test validates the plate twice. The first pass, with no blend, asserts
the multi-color rule is armed at all; without it the second proves nothing,
because the rule only fires here thanks to a degenerate fixture mode whose two
tools share a gantry. Give that mode a Span tool and the whole test would pass
under either ordering while appearing to guard it. It also pins err.object,
which the mixed path sets and the multi-color path leaves null -- a discriminator
that survives the next wording change. Verified by reverting the order: the test
fails on both the message and the object.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 08:48:14 -04:00
SoftFever 5a170520d3 refactor: rename SnapOrca references to Orca in CAD components to avoid confusion and update recipe versioning 2026-08-27 18:49:50 +08:00
SoftFever 0aeb6df122 Merge branch 'main' into cad-mainline 2026-08-27 17:06:37 +08:00
Ian Chua f9c415fc41 Merge branch 'main' into feat/plugin-auditing 2026-08-27 14:52:20 +08:00
Lam Wei Lun 20e5a7e042 Merge main 2026-08-27 14:11:41 +08:00
Lam Wei Lun 76d9b8bac0 Publish 3MF: support mixed filaments and per-extruder slot selection
- Publish mixed-filament slots as whole units: serialize the filament_mixed_* definition into project_config on import, grow the receiver's parallel arrays in lockstep, and report unappliable definitions as skipped instead of dropping them silently

- Per-extruder printer selection: one inner tab per extruder, rows keyed by full "#N" ids; single-extruder receivers collapse variants onto their slot (first applied, rest skipped), multi-extruder receivers override element-wise

- New per-slot "Enable" toggle gating what gets published; enabling a mix auto-enables + Full Publishes its components

- Mixed page previews: fixed-size ratio bar, ternary triangle (3 components) and Material Ratio vs Model Height graph (gradients), always visible regardless of Enable

- Tab strip shows full swatch compositions with adjustable spacing; barycentric helpers shared via FilamentBitmapUtils
2026-08-27 13:17:19 +08:00
Clifford GarwoodandClaude Opus 5 2e883df7d9 fix(imex): name the physical head in M104/M109 tool indices
M104/M109 address a heater, but every caller of the instance
GCodeWriter::set_temperature overload addresses filaments by logical id, so on a
printer whose physical_extruder_map is not the identity the emitted T named the
wrong head -- or, where the logical id exceeds the head count, no head at all.
With a map of 0,0,0,0,1,2,3 a toolchange to filament 5 emitted "M109 S265 T4"
and "M104 S190 T4 ;cooldown" while the head it meant was T1.

Upstream already treats these commands as physical: the preheat it injects in
GCodeProcessor maps through the same map before emitting, and BBS's own wipe
tower does likewise. Emitting logical is the half that never got the memo.

That mismatch also disabled the cooldown suppression beside the preheat, which
compares the line's T against pem[tool_number] and so never matched a logical
one -- 171 cooldowns survived in a two-head print where none should have. Worse,
it could match the wrong line: a cooldown for filament 1 emitted T1, and a
toolchange to filament 5 gives pem[4] == 1, so a legitimate cooldown for head 0
was deleted because the incoming head happened to be numbered 1.

Translate once, in the instance overload every logical-space caller passes
through. The static overload is already physical-in and is left alone.

Gated on is_imex. physical_extruder_map carries two readings in this tree: the
BBS paths index it by extruder id, the IMEX paths by filament id, and the two
coincide only when the filament and nozzle counts match. Mapping unconditionally
would impose the IMEX reading on profiles that mean the other one --
fdm_bbl_3dp_002_common ships a non-identity [1,0], spared today only because
single_extruder_multi_material suppresses the T qualifier entirely.

The wipe tower's interface-temperature pass has to move with it. It strips the
M109 that post_toolchange emits by searching for that filament's tool index, so
it now searches for the mapped one; left alone it would have stopped matching,
and the surviving blocking M109 would have silently defeated the interface
temperature. Its sibling pass reads WipeTower2 output, which emits no T at all,
and is deliberately unchanged.

The bare T<n> toolchange stays logical -- it selects an AFC lane, not a heater.

Test slices two objects across a head boundary, the only case that reaches this
emission: the same-physical short-circuit in set_extruder suppresses the
cooldown entirely for lane swaps within one head. It scans every M104/M109
rather than matching fixed strings, so it catches any unmapped emission and not
just the two sites changed here. With the mapping neutered it reports 101
offending lines; with it in place, none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:04:23 -04:00
Clifford GarwoodandClaude Opus 5 f0778ef5fa fix(imex): shorten the off-primary message and block blended filaments
The routing error ran to roughly 450 characters and explained the mechanism
before it got to the remedy. It also offered to "edit the mode in Printer
Settings so its Primary tool is one of %3%", which on a plate whose filaments
resolve to no head at all rendered as "one of no configured extruder". Cut it
to the mode, the tool it prints with, where the plate's filaments actually are,
and the two things the user can do about it.

The second msgid that named candidate modes went with it. It could only suggest
a mode whose primary is among the routed heads, and every mode on the printers
this fires for declares 0:P, so it had nothing to offer.

Blended filaments now return before that check rather than falling through it.
A blend is mixed at the nozzle by its component toolheads, and a parallel mode
is already using those toolheads to print copies or mirrors, so the two cannot
run at once regardless of where the components route -- including when a
component sits on the declared primary. Reaching the routing rule would also
have described them wrongly: mixed slots sit past the end of
physical_extruder_map, so they resolve to no head and read as merely unrouted.

Keeps the empty-list guard the shortening first dropped. validate() reads the
raw physical_extruder_map, whose registered default is a single entry, so a
profile that declares IMEX modes without authoring a map leaves every slot past
the first outside it -- and the sentence ended in a dangling "on .".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:04:02 -04:00
Noisyfox dbcb196f4e Merge branch 'main' into dev/ffmpeg-player 2026-08-26 21:47:20 +08:00
Clifford GarwoodandClaude Opus 5 b24733cea8 Merge the color mixing subsystem into the IMEX work
Brings the upstream color-mixing feature and its follow-ups onto the branch so the
IMEX placement and primary-routing checks are built and tested against them for the
first time.

Merged clean, no conflicts. Not yet exercised together: a mixed filament is a virtual
slot no nozzle carries, while physical_extruder_map routes logical slots to physical
heads, so the IMEX pem lookups have no defined answer for one. Print::extruders()
lists mixed slots under their own id while tool_ordering.all_extruders() lists them
post-expansion, and the IMEX code reads both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 09:25:41 -04: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
Lam Wei Lun cc267055e1 Use proper floating point comparison functions in publish unit test 2026-08-26 11:33:50 +08:00
Clifford GarwoodandClaude Opus 5 ba07716ddb Merge upstream main: color mixing feature and related fixes
Brings in 54 upstream commits, the bulk of them the BambuStudio-ported color
mixing / mixed filament subsystem (#15347) plus its follow-ups, along with the
Assimp-backed colored OBJ import, warning-policy build changes, and assorted
profile and localization updates.

Two conflicts, both "each side added at the same point", resolved by keeping
both:

- Print::validate() -- our IMEX multi-color block and upstream's new gradient
  mixed filament warning were inserted at the same spot after the empty
  extruders check. They test unrelated conditions, so both are kept, each with
  its own closing brace.
- tests/libslic3r/test_3mf.cpp -- our three IMEX per-plate round-trip scenarios
  and upstream's mixed-filament round-trip scenario both append to the end of
  the file, and each side added one include. All four scenarios and both
  includes are kept.

Everything else merged cleanly, including GCode.cpp, ToolOrdering.cpp,
PartPlate.cpp and PrintConfig.cpp. Upstream left the is_extruder_used block
untouched, so the IMEX supplement still applies, and estimate_wipe_tower_polygon
is unchanged, so the prime tower hull work is unaffected.

Not addressed here, and worth its own change: a mixed filament is a virtual slot
that no nozzle carries, while physical_extruder_map routes logical slots to
physical heads. Print::extruders() lists mixed slots under their own id whereas
tool_ordering.all_extruders() lists them post-expansion, so the IMEX pem lookups
have no defined answer for a mixed slot. Upstream's own guards reject a mixed
filament where a physical slot is required; IMEX likely wants the same.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 13:07:26 -04:00
Clifford GarwoodandClaude Opus 5 baef398ee3 fix(imex): refuse a plate whose filament cannot reach the mode's primary tool
The IMEX primary tool prints the sliced paths directly, so it can only load a
filament that physical_extruder_map routes to it. The ghost filament picker
enforces that for the secondary tools -- it offers only lanes whose pem entry
equals that head -- but the primary's filament comes from the ordinary object
filament selector, which has no IMEX awareness. Nothing detected the mismatch:
collect_imex_warnings() computes the same condition and discards it into a
display fallback, and the multi-color rule never examines it.

Block it in Print::validate() via the existing imex_primary_tool_for_mode and
imex_primary_logical_from_objects helpers. The message names the declared
primary, the heads the plate's filaments actually live on, and any configured
modes whose primary would work, and carries the object so the notification can
offer a jump to it.

Blocks rather than warns, matching the multi-color rule: the plate is not
printable as configured, and where the routed head is also absent from the
mode's active tools the 1st->2nd layer temperature branch skips it too, leaving
that head at its initial-layer temperature for the whole job.

The multi-color check now runs first. Its constraints -- an MMU manifold sharing
one head, a single-gantry mode -- cannot be fixed by switching mode, so the more
specific error should win rather than be masked by routing advice that leads
straight back to it. The extruders().size() > 1 gate moved onto that call, since
the routing check must also see single-filament plates, which is its common case.

The copy-mode guard-rail test printed on a filament routed off the primary, so
it asserted a plate this rule now refuses; retargeted to a well-formed plate.
Its replacement pins the object's own extruder, because ModelVolume reports its
extruder_id and would otherwise put a primary-routed slot on the plate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-25 12:58:52 -04: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
harrierpigeon 30351d40e1 tests: adapt belt brim coverage to upstream validation 2026-08-25 10:27:08 -05:00
SoftFever bfe5f7e63c fix text error 2026-08-25 21:35:43 +08:00
harrierpigeon d289478618 Merge remote-tracking branch 'upstream/main' into haryr/aug25-rebase
# Conflicts:
#	resources/profiles/Custom.json
#	src/libslic3r/Brim.cpp
#	src/libslic3r/GCode.cpp
#	src/libslic3r/GCode.hpp
#	src/libslic3r/Preset.cpp
#	src/slic3r/GUI/3DScene.cpp
#	src/slic3r/GUI/ConfigManipulation.cpp
#	src/slic3r/GUI/GLCanvas3D.cpp
#	src/slic3r/GUI/Plater.cpp
2026-08-25 06:50:46 -05: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 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