Commit Graph
4130 Commits
Author SHA1 Message Date
Lam Wei Lun bcff39661c Comments and dead code cleanup 2026-09-03 13:40:58 +08:00
Lam Wei Lun 8c7160079e Revert clang-format changes then reapplied chagnes for Plater/PresetBundle. Fixed extruder masking incorrectness. Fix warning notifications stacking 2026-09-03 13:17:13 +08:00
Clifford GarwoodandClaude Opus 5 5ac1d05fcb Stop three IMEX changes from altering non-IMEX behaviour
None of these came from the review; they were found auditing the branch for the
same class of leak review comment 6 identified.

physical_extruder_map was normalised through effective_physical_extruder_map on
every Print::apply(), so any printer with more than one nozzle and no authored map
got the identity [0,1,...,n-1] where the single-element {0} default belongs. The
key carries two readings: the IMEX paths index it by logical extruder and need one
entry per extruder, while the inherited BBL paths read it through the clamping
get_at(), for which {0} means "everything is physical 0". Deriving unconditionally
imposed the IMEX reading on profiles that mean the other one, changing the config
block line and the {first_tools} / {first_filaments} / {curr_physical_extruder_id}
placeholders for multi-nozzle non-IMEX printers. Gated on is_imex; every consumer
needing the per-extruder form is already IMEX-gated, and profiles with an authored
map of the right length are unaffected either way.

That gating unmasked a latent out-of-bounds read: WipeTower's M104/M109 emitters
index m_physical_extruder_map by tool with no bounds check, which reads past the
end of the single-element default on a multi-nozzle machine. Upstream's bug, from
the BambuStudio wipe tower sync, previously hidden because the map was being
widened for everyone. Now bounds-checked, falling back to the tool's own index --
the form GCodeProcessor already uses for the same map.

Preset::save() and get_preset_differed_for_save() carried a branch storing the
full vector whenever a child and its parent had different lengths. It was written
against a set_with_nil that threw on mismatched sizes; upstream #13035 replaced
that with a tolerant version that keeps the child vector verbatim and nil-marks
only the overlapping range, and that fix was already in the tree when this branch
was rebased. Left in, it defeated the delta encoding for every user printer preset
whose extruder count differs from its parent's: a 7-extruder profile inheriting a
single-extruder base wrote out all of its per-variant retraction keys as literals,
including ones identical to the parent, pinning them against future vendor updates
while the UI still reported the preset as inheriting. Removed; the two save loops
are now identical to upstream. Note this only affects new saves -- presets already
written keep their frozen values until re-saved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:58 -04:00
Clifford GarwoodandClaude Opus 5 a5ba39393e Escape the IMEX plate attributes written into the 3MF
Closes review comment 1.

imex_parallel_mode and imex_head_filament_map were streamed raw into XML attribute
values, while every other free-text attribute in the same writer goes through
xml_escape. Mode names are free text, so "PLA & ABS", a quote or a "<" made the
document malformed. The failure is not a bad value on reload: both load paths for
model_settings.config return false on an expat error, and m_is_bbl_3mf is set
before the second entry loop runs, so the whole project fails to open with
"Archive does not contain a valid model config".

Both attributes now use xml_escape_double_quotes_attribute_value(), which also
emits tab, CR and LF as numeric character references. That matters and plain
xml_escape would not do: XML normalises literal whitespace in attribute values on
read, so a tab in a mode name would come back as a space and silently rename the
mode. The read side needs no change -- it takes expat's already-decoded value with
no second unescape -- so this is a lossless round trip and a file written by the
new code still loads in an older build.

The round-trip test used "copy_mode", which exercised none of this; it now carries
&, <, a quote and a tab, and also pins that ' and > come back unmodified, since
both are legal raw inside a double-quoted value.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:58 -04:00
Clifford GarwoodandClaude Opus 5 5629dd29e9 Restore the pre-IMEX pressure advance output for non-IMEX printers
Closes review comment 6.

The per-tool pressure advance work changed set_pressure_advance() for users who
are not using the feature. RepRapFirmware lost its D qualifier when no tool index
was supplied: upstream emits M572 D0 S<pa> unconditionally, and a bare M572
applies to whatever tool is currently selected and errors when there is none, so
PA started depending on tool-selection state for every RRF user. The D is back,
defaulting to 0, and D<tool> is reached only from the IMEX paths.

The same rewrite had also changed the comment separator from "<value>; Override"
to "<value> ; Override" on the Klipper, RRF, Marlin 2.x and Marlin Legacy
branches, so every non-IMEX print of those flavors carried a one-byte diff.
Restored. Upstream is internally inconsistent here -- BBL and Repetier do use the
spaced form -- and the point is to match it exactly rather than to tidy it.

Emitted output for all six flavors with no tool index is now byte-identical to
upstream. Verified on a real slice: a Klipper profile emits
"SET_PRESSURE_ADVANCE ADVANCE=0.02; Override pressure advance value", an exact
string match, with no EXTRUDER= qualifier. The tests were pinning the regressed
form and are inverted.

Also records at the imex key registrations why they are kept out of the g-code
config block, matching the house convention at the other banned keys.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:35 -04:00
Clifford GarwoodandClaude Opus 5 612e0e3932 Fall back to Primary when an IMEX mode does not resolve, and keep imex keys out of the config block
Closes review comment 2, part of 18, and one non-IMEX regression the review did
not cover.

An imex_parallel_mode naming no entry in imex_mode_names still entered the
parallel branches. get_imex_active_tools() returned empty and the else was
skipped, so no head received its 1st-to-2nd layer temperature transition, and
imex_suppresses_bare_toolchange() still dropped the initial T<n> on the
expectation that a mode script would select the tool. validate() did not catch it
because its guard is declared_primary >= 0 and an unresolved mode yields -1. You
reach it by renaming a mode after a plate is set to it, or by opening a 3MF whose
printer preset names its modes differently. m_imex_parallel_mode is no longer
assigned before the lookup; a non-Primary name that matches no row now warns and
re-resolves against the Primary row, which is the fallback the PR description
already claimed. A mode that resolves to an empty tool roster takes the same path,
since the emitted G-code is wrong in the same way.

Warned rather than blocked: opening someone else's 3MF is a legitimate way to get
here and the Primary reading prints correctly, so refusing to slice would turn a
recoverable situation into a dead end. Silent was not an option either, because
the plate keeps showing the stale mode name while drawing no zones.

Separately, the 14 imex config keys all register non-nil defaults, so
append_full_config was emitting "; imex_* = <default>" into every exported
G-code, including on single-nozzle printers with nothing to do with the feature.
They are banned from the dump, matching the treatment already given to the
fast-purge, extruder-change and timelapse keys, so the config block is
byte-identical to the pre-IMEX baseline for the whole shipping fleet. Nothing
reads them back: GCodeProcessor has no imex reference, and the two per-plate keys
round-trip through the 3MF's model_settings.config on an independent path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:35 -04:00
Clifford GarwoodandClaude Opus 5 3629056831 Derive the IMEX slice offset in the engine
Closes review comment 11, and fixes a worse bug found while doing so.

The offset was only ever pushed from PartPlate::refresh_imex_slice_offset(),
reachable from update_slice_context() and the plater -- both GUI-only, and it
dereferences wxGetApp(). A headless slice therefore kept Vec2d::Zero(), so
orca-slicer --slice on a plate with imex_firmware_managed_zones emitted
slicer-managed coordinates while the firmware applied its own offsets on top.
Print::update_imex_slice_offset() now derives it from the applied config and runs
from process() and export_gcode(), so a CLI slice gets the value a GUI slice does.
It reads m_full_print_config rather than m_config because imex_tool_layout and
imex_carriage_margin are printer-preset options with no member in the static
PrintConfig, and it takes the mode from the same place GCode.cpp resolves it, so
the shift cannot disagree with the mode that is emitted.

The GUI push and Print::set_imex_slice_offset() are deleted rather than kept as an
override, because the two did not agree. calc_imex_zones() divides
get_extents(m_shape), and set_shape translates m_shape by the plate position, so
the pushed offset carried the plate origin -- which translate_to_print_space() and
the writer offset already subtract. Plate 1 sits at the origin and agreed by
accident; every later plate had the origin subtracted twice and was shifted by a
full plate stride. Not silent, either: the displaced geometry fell outside the
printable area, so slicing plate 2 failed validation with "part is off the plate".
Confirmed fixed on hardware profiles -- the same model on plates 1 and 2 now emits
identical extents.

Deleting the push also removes the post-apply ordering constraint that forced the
duplicate call in Plater::priv::update_background_process: the value is computed
at the point of use, and process()/export_gcode() are structurally after apply().

Also routes validate()'s primary-routing check through imex_resolve_routing() so
the hard block and the plater's warning cannot describe a plate differently
(review comment 20), and through find_imex_mode() for the mode lookup (18).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:35 -04:00
Clifford GarwoodandClaude Opus 5 fcfda6c835 Move the IMEX bed-zone geometry into libslic3r
Closes review comment 16, and fixes three defects the move exposed.

PartPlate::calc_imex_zones() was 392 lines deciding where every zone, collision
strip and safety margin sits, in the GUI layer, with no test coverage. The three
wxGetApp() calls that kept it there were all in its first 25 lines, fetching two
configs. The geometry now lives in compute_imex_zone_layout(); the wrapper fetches
the configs, calls it, and clips the returned rects to the bed outline for the
GLModels, which is the only part needing GUI types. libslic3r gained no wx
dependency: it takes DynamicPrintConfig directly, so the option lookups moved
verbatim rather than through a hand-written value struct that could drift.

The move is otherwise exact -- verified by a line-for-line diff of every
arithmetic expression against the original, and by running 15 scenarios through
the extracted code against hand-derived values. The only deletion is a lambda that
was never called.

Three fixes on top, each of which needed the code to be testable:

- An off-grid or absent Primary left pri_col/pri_row at their (0,0) initialisers
  and built a layout from them, reporting the whole bed as the clear primary zone
  and the whole bed as a blocked mirror zone at once; under
  imex_firmware_managed_zones it shifted the slice by the bed centre. Guarding on
  the resolved primary head covers both routes. Reachable only from a hand-edited
  preset or a 3MF authored against another printer -- the editor pins Primary to
  tool 0 -- but that is the same class the unresolved-mode fallback handles.
- imex_nozzle_clearance_x/y fell back to 0.0 where PrintConfig registers 30.0.
  Both strip loops are gated on the value being positive, so the fallback emitted
  no collision strips at all while the preview still drew 30 mm toolhead boxes.
- The collision-strip loop asked each mirror head for its own grid cell, but an
  aggregated gantry's cell is pinned to the primary's column and expanded into a
  full-width row strip. Where the representative's column differed from the
  primary's, no boundary matched and the plate came back with no strips and no
  margin bands -- an object flush against the shared boundary sliced without a
  warning while the far carriage occupied it. Present since Span aggregation was
  added in 4966d0fae8 and carried out of PartPlate verbatim. The flags now come
  from the painted cells; an exhaustive sweep of the reachable grid, role and
  layout space (1,630,720 configurations) shows the only behaviour change is the
  missing strips appearing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:04 -04:00
Clifford GarwoodandClaude Opus 5 171a721304 Harden the IMEX helper layer and give it one mode lookup
Closes review comments 3, 5, 7, 8, 18 and 19, plus the library half of 20.
These share a file, so they share a commit; each is independent of the others.

- 3: ::isspace(char) is undefined for bytes above 0x7F because char is signed on
  our targets. Three call sites now go through one strip_whitespace() using an
  unsigned char cast. Line 308 parses imex_head_filament_map straight out of 3MF
  metadata, so a non-ASCII byte reached it without passing through the UI.
- 5: an imex_head_filament_map override past the end of physical_extruder_map now
  falls through to the printer's own routing instead of resolving to a wrong
  filament. Bounded in resolve_filament_for_head, where the slot count is known,
  rather than at the parse site, which has no count to check against; the parse
  site also gains the absolute MAXIMUM_EXTRUDER_NUMBER cap its sibling already had.
- 7: imex_physical_heater_for's !is_imex early return is what keeps a stock BBL
  profile (physical_extruder_map [1,0]) out of the heater remap, and had no test.
  Six cases now cover it, pinning pass-through rather than get_at()'s clamp.
- 8: ImexRole::Span was missing from the imex_head_transform switch, so it warned
  under -Wswitch. Identity is correct, not merely convenient: a Span tool prints
  the primary's own zone through mid-print toolchanges and has no zone to be
  translated into.
- 18: three positionally coupled string vectors were resolved by nine open-coded
  lookups using three incompatible bounds idioms. None read out of bounds, but six
  folded the guard into the match condition, so a ragged row did not stop the scan
  and a later duplicate name could win. struct ImexMode + find_imex_mode() is now
  the only resolution rule: the names array is the roster, first match wins, a
  short sibling pads to empty and sets ragged, not-found is an explicit -1.
- 19: the letters P/C/M/S existed in three independent copies, one of which was the
  writer of the on-disk format. kImexRoleTable is now the single source, read by
  both parsers and the serializer. Adding a role was 14 edit sites with one
  compiler-enforced; it is now the enum, the table entry, and four -Wswitch
  switches. Verified by adding a fifth enumerator and recompiling: exactly four
  warnings, nothing else.
- 20: imex_resolve_routing() extracts the mode/primary/routing chain that
  Print::validate and the plater's warning collector each derived separately.

The three config keys keep their names, types and on-disk representation. This is
a read-side view only; presets and 3MF files are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 00:55:03 -04:00
SoftFever fa8edd0f69 Merge branch 'main' into feature/filament_id 2026-09-03 12:16:38 +08:00
Lam Wei Lun 686b7f66ac Merge main and fixed conflicts 2026-09-03 10:55:01 +08:00
53c26a5724 fix: save 3d mouse settings (#15397)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-02 20:21:27 -03:00
Kiss Lorand 51fd6327fe Fix omitted assembly parts with height ranges (#15499) 2026-09-02 18:15:18 -03:00
Tommaso BianchiandClaude Opus 5 bb6a1810f6 A stray click must not break a model that looks perfect on screen
Revolve failed on a sketch whose profile was closed. Decoding the reported 3mf: four
entities forming a proper closed loop (joints open by 4.44e-06 mm, well inside
tolerance) plus one stray 1.82 mm Line at (-24.2, 80.3), inside the shaded region,
touching nothing.

The viewport's region_loops discards open chains ON PURPOSE — it exists to find
EXTRUDABLE regions — so the user saw one clean closed region. entities_to_wires kept
the stray as its own one-edge loop, so it returned two wires, and Revolve goes through
entities_to_wire which demands exactly one. Extrude would have failed one step later in
wires_to_face, because a one-edge open wire bounds no face. Same class as the tolerance
split fixed in 8b568b7b: the viewport and the kernel disagreeing about the sketch — this
time about what BELONGS to the profile.

entities_to_wires/entities_to_wire/build_sketch_wire take closed_only. It is not a
blanket rule: a SurfaceExtrude builds a sheet FROM an open profile and a Sweep PATH is
normally open, so all ten call sites are classified individually — true for the face
fallback, Extrude-taper, Revolve, the Sweep PROFILE and Loft profiles; false for
SurfaceExtrude/Revolve/Loft/Fill and the Sweep path.

A component counts as open when some welded node has DEGREE 1. The first attempt used
"the traversal did not return to its starting node", which regressed the bridged C
profile: a closed loop that also carries a second edge across the same two nodes has no
free endpoint, but its Eulerian walk ends elsewhere. Degree-1 is the property that
actually distinguishes a stray segment from a closed profile; the suite caught the
difference.

Behaviour change decided by Tommaso: a stray is IGNORED, not refused. The test that
required refusal dates from when ignoring meant falling through to a default rectangle —
geometry nobody drew. That fallback is gone, so ignoring now builds the circle the user
actually drew. Its assertion is updated with the reason.

The bridge round-trip test extruded an ENTIRELY open chain and "worked" only because
OCCT will make a face from an open wire. It gets a genuinely closed profile: the test is
about serialization, and deserialize_recipe recomputes, so the document has to be one
that legitimately builds.

Failures now say WHERE. sketch_open_ends reports free endpoints under the same weld
tolerance the wire build uses, and open_loop_message is shared by both throws, because
Extrude fails through build_sketch_face and Revolve through build_sketch_wire — enriching
only one would have left the commoner path the less informative one.

Kernel 66115 assertions / 608 cases green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
2026-09-02 14:58:05 +02:00
Alexandre Folle de Menezes 640fd9f454 Fix misc. errors on GUI strings (#15468)
* Fix misc. errors on GUI strings

* Adding context to single-letter unit strings

* Fix duplicate strings on po files
2026-09-02 09:36:54 -03:00
Tommaso BianchiandClaude Opus 5 8b568b7b9d The viewport and the kernel now answer "is this joint closed?" with one number
Tommaso asked the question that names the real defect: if the sketch was open, why was
the same sketch shaded closed and offered for extrude? Because the two halves used
different tolerances. region_loops shades a region closed at 1e-3 mm; connected_loop
chained at 1e-3; the kernel welded at 1e-4 and OCCT matched vertices at 1e-7. The
2.28e-5 mm gap in the reported sketch did not cause that disagreement, it only made it
visible — and fixing the gap alone would have left the contradiction in place, ready to
reappear anywhere in (1e-4, 1e-3].

kSketchJoinTol now lives in SketchEngine.hpp and is the only place the number exists.
region_loops, loop_report, connected_loop and entities_to_wires all read it through
sketch_join_tol(). The viewport cannot promise a region the kernel refuses to build.

The welding is optional, because a kernel that silently closes loops should let you say
no: "Auto-close sketch loops" in Preferences, default ON, no restart. OFF means only
exactly coincident endpoints join — and since both halves read the same value, the
viewport simply stops shading the region closed, so an open loop is visible rather than
welded behind your back. No separate UI needed for that; it falls out of sharing one
number.

Details that matter. The kernel defaults to auto-close ON independently of the GUI, so
headless and MCP callers behave like the viewport instead of inheriting an unset
preference. With the tolerance at 0 the comparisons become <=, because OFF must mean
exact, not broken. OCCT never receives a zero vertex tolerance — it is clamped to
Precision::Confusion.

The preference is pushed from EVERY entry that starts a sketch session, not just
begin(): a Constrain session enters through begin_constrain / begin_constrain_entities
and uses region_loops and connected_loop, so a single push site would have left those
sessions running on whatever the previous one set. begin_imported_transform is excluded
deliberately — it works on imported regions, not chained entities.

Tests: a loop with one joint open by 9e-4 mm, given out of traversal order, builds a
closed four-edge wire; with auto-close off the same loop yields no wire; and an exactly
closed loop still builds with auto-close off, proving OFF means exact. Kernel 66104
assertions / 606 cases green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
2026-09-02 14:34:17 +02:00
Tommaso BianchiandClaude Opus 5 faf4406f89 A wire that lost two edges still called itself done
An extrude built a solid the user never drew: three sides of the handle plus the arc
that bulges outside the outline, with the bowl's second arc and the left edge missing.

Two faults met. entities_to_wires added edges in ENTITY-CREATION order, so a partial
wire rejects the next edge even when the sketch closes perfectly; and one joint of the
reported sketch is open by 2.28e-5 mm, wider than OCCT's 1e-7 vertex tolerance and
wider than this function's own EPS of 1e-6, so that edge was refused on geometry too.

Neither showed up, because BRepLib_MakeWire::Add DROPS a disconnected edge
(BRepLib_DisconnectedWire + NotDone) while every successful Add ends with
BRepLib_WireDone + Done() — overwriting the failure. `if (!wm.IsDone()) return {}`
was therefore asking only whether the LAST edge connected. Six edges in, four out,
IsDone() true.

Endpoints now weld into shared nodes at one tolerance (kSketchWeldTol) used by BOTH
the union-find grouping and the wire build — they disagreed before, which is how a
joint gets united into a loop and then refused by the builder. Each node becomes ONE
TopoDS_Vertex, so the builder matches on identity instead of proximity, with the
vertex tolerance widened because BRepLib_MakeEdge::Init projects a vertex onto the
curve within that tolerance and a welded node sits up to the weld gap off its
neighbour's curve. Members are then walked in traversal order. Finally the result is
counted: IsDone() alone is not evidence, edge_count == members.size() is.

Arc geometry is untouched — the midpoint from (start_angle+end_angle)/2 and the
solver's angle reflow both measured correct and were never part of this.

The regression case carries the reported sketch verbatim, open joint included. It
fails 4 == 6 without the fix, which was measured, not assumed. Kernel 66092
assertions / 604 cases green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA
2026-09-02 13:15:51 +02:00
Kris AustinandRodrigo Faselli 1749c293a6 build: clear 237 warnings - unused lambda captures (#15417)
* build: enable /Zc:lambda for MSVC

MSVC keeps its legacy lambda processor under /std:c++17, which rejects
reading a constexpr constant inside a lambda that does not capture it
(C3493). No other compiler requires that capture, and clang reports it as
an unused one, so the two cannot both be satisfied without the flag.

/Zc:lambda selects the conforming lambda parser that clang and GCC
already use. It is implied by /std:c++20 and /permissive-, so it is only
needed while we are on C++17. clang-cl is conforming already and does not
take the flag.

It requires VS2019 16.8, so build_release_vs.bat now says 16.8+.

* build: clear 237 unused lambda capture warnings

236 captures across 81 files, 142 of them `this`. Removing an unused
capture changes no behavior; clang does not report a capture whose type
has a non-trivial destructor, so nothing held only to extend an object's
lifetime is in this set.

Nine of them are the second half of the warning, "is not required to be
captured for this use", where the capture is a const or constexpr value
the body does read. Those depend on the /Zc:lambda change in the previous
commit. One of them, in FillRectilinear.cpp, had been worked around with
an #ifndef __APPLE__ guard around the capture list, which is now gone.

GUI_ObjectTableSettings.cpp captured its reset button only to read it
inside #ifdef __WXOSX_MAC__. That branch now takes the button from the
event it is already handling.

* build: fail configure on MSVC older than 19.28 instead of dropping /Zc:lambda

cl.exe answers an unrecognized /Zc: sub-option with warning D9002 and keeps
going, so on VS2019 before 16.8 the flag is silently ignored and the build
instead dies with C3493 in FillRectilinear.cpp, nowhere near the cause.

* fix: delete three locals that are now unused

Their only remaining use was the lambda capture this branch removed. The
Clang builds set -Wno-unused-variable, so the build never flagged them.

---------

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-02 07:38:05 -03:00
SoftFever 7a0ca15df8 Merge branch 'main' into feature/filament_id 2026-09-02 15:34:20 +08:00
Lam Wei Lun f1719b5580 Fixes mixed filament growth bug. Fixes unit test 2026-09-02 14:29:15 +08:00
Lam Wei Lun f5984e7523 Merge main 2026-09-02 11:43:04 +08:00
CliffordandClaude Opus 5 b6ef6cf1be fix: out-of-bounds write migrating per-variant values when switching printers (#15456)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:29:11 -03:00
Kiss Lorand e8115658e0 Fix overhang fan control when overhang slowdown is enabled (#15158) 2026-09-01 18:07:02 -03:00
Mikhail f. Shiryaev 96e85b1db5 Fix build with disabled precompiled headers (#15484)
Fix broken build with disabled PCH
2026-09-01 14:21:38 -03:00
Lam Wei Lun d00af63a61 Removes identity matching for when slots need to grow without writing type key 2026-09-01 18:18:33 +08:00
Lam Wei Lun 7133d6b225 Added wiki and youtube guide link as placeholders. Add to recently opened in home screen after publishing. Show PUB badge. Add .published as a file save name hint. 2026-09-01 16:59:25 +08:00
SoftFever 7c063b2933 Merge branch 'main' into publish_3mf 2026-09-01 12:20:48 +08:00
SoftFever e1c28a5f7c Merge branch 'main' into feature/filament_id 2026-09-01 11:43:57 +08:00
Tommaso Bianchi 8f3f835636 Constrain while you sketch, and stop losing work to Esc and to invisible points
Five defects from ten minutes of real use, and the mode split behind the worst
of them. One commit because the changes overlap in the same functions; the
pieces are separable in the diff, not in the file.

CONSTRAINING NO LONGER NEEDS A COMMITTED SKETCH. A constraint could only be
applied by committing the sketch, selecting it in the feature tree, pressing the
padlock, and only then picking. While drawing, the CONSTRAIN toolbar was not even
on screen (set_ui_mode showed it in UiMode::Constrain alone) and apply_constraint
answered "Press Constrain on a sketch first" -- in a status line nobody looks at.
Draw two lines, press Parallel, get nothing: that is what a user reported as "the
UX is a mess", and they were right.

apply_constraint now takes the live session first, reading the picks from the
selection model the sketch tool already had (click, ctrl-click to extend,
double-click for the loop) and applying through try_add_constraints, which
already did append -> solve -> keep-or-rollback. The committed Constrain path
stays for editing an old sketch; it is no longer the only way in. The twenty
constraint buttons now show in Sketch mode as well.

The discriminator is is_sketching() && !is_constraining() &&
!is_constraining_entities(). Both begin_constrain and begin_constrain_entities
set m_active, so is_sketching() alone is true DURING a constrain session and the
new path would hijack the old one -- compiling perfectly and failing in
behaviour.

ONE PLANNER, NOT TWO. A second caller meant duplicating the logic that decides
whether a constraint is legal, which roles it binds and whether it needs a typed
value. That duplication is how today's Coincident bug survived: fixed in one
branch, alive in the next one down. plan_entity_constraint() now lives in the
kernel -- pure, no wx, no translation -- and both UI paths call it. DesignPanel
loses 304 lines and gains 155.

Being in the kernel makes it TESTABLE. The Parallel defect existed because a
constraint type met an entity type nobody had tried, and the only instrument was
a 13-minute GUI ladder. 19 new kernel cases cover the matrix: 264 -> 283 cases,
7648 -> 7867 assertions.

Parallel, Perpendicular and EqualLength gain the two-line guard they never had.
On non-lines they used to emit a def the solver silently dropped -- the sketch
reported itself constrained when it was not, the same class as Horizontal on a
Point. EqualLength on two rounds still promotes to EqualRadius first.

Symmetric is planned completely, including its axis pick: the plan carries a
VECTOR of defs because Symmetric on two lines is two constraints (P0/P0 and
P1/P1). A single def would have half-applied it -- one end pinned, one free,
looking correct until something moves.

A PLACED POINT SURVIVES THE COMMIT. Type::Point was created correctly and never
drawn once committed: both renderers skip it, correctly, since entity_polyline
gives a point nothing. What was missing is the vertex-marker path the live
session already used. rung_point passed throughout because it asserts the
document, and the point was always in the document -- the pixels lied.

ESC STOPS EATING AN UNSAVED SKETCH. The third press reached cancel_sketch(),
clearing m_entities with no warning and nothing to undo. live_sketch_has_work()
existed and was never consulted. The exit layer refuses once when there is work
and lets a second consecutive Esc through; the refusal re-arms on a button press,
never on mouse motion, or Esc could never exit while the hand moves.

TWO NEW RUNGS. D10 drives Parallel through the committed path -- it passes on
the PRE-fix binary, which is how we know the user's failure was the mode and not
the constraint. D11 is the acceptance for the collapse: draw, pick both, press
Parallel, no commit and no padlock. Ladder 126 -> 135 properties, all holding.

CON_BTN_SKETCH is measured, not derived: in Sketch mode the group renders after
the sketch toolbar, so the first button is at 677, not 449. Pitch 42, twenty
buttons, read off a screenshot. Deriving it by offset is how that table drifted
the last time.

Known limit, commented at the call site: a constraint added to a LIVE sketch is
not on the document undo stack, so Ctrl+Z will not take it back until the sketch
is committed.

snaporca-itp4, snaporca-oyhx, snaporca-l2vm
2026-08-31 22:12:26 +02:00
Leo Lobato ca903faa56 Fix uninitialized first_layer_time on CLI-sliced 3MF (#13429)
PartPlate::store_to_3mf_structure read first_layer_time from the indirect cali_bboxes_data struct,
which the GUI populates at Plater.cpp:10600 but the CLI never writes to. The result was uninitialized
memory leaking into slice_info.config

Read directly from get_slice_result()->initial_layer_time, which is populated by
GCodeProcessor::finalize() in both code paths and matches the pattern already used a few lines
above for gcode_prediction.

Also default-initialize PlateBBoxData::first_layer_time to 0.0f as a defense against any other consumer
reading it without an explicit write.
2026-08-31 16:36:55 -03:00
Kris Austin fcf6f0a3a5 build: clear 54 warnings - dead private fields (#15423)
build: clear 54 dead private fields

54 of the 161 -Wunused-private-field warnings, across 31 files. These are
the ones needing no judgment. Each member is declared once and appears
nowhere else in src/, counting the .mm and .c sources as well as .cpp and
.hpp, so nothing writes them and nothing reads them. Every removal is a
whole line, and no declaration shares a line with another member.

The remaining 107 are left alone. Those members are mentioned elsewhere,
usually assigned and never read, where the fix might be deleting the
member or might be restoring a read that went missing.
2026-08-31 16:36:52 -03:00
SoftFever 507b45431c Keep the CAD recipe in the one 3mf backend that actually runs
Format/3mf.cpp also saved and loaded it, but nothing calls its store_3mf and
its load_3mf only sees files fingerprinted as PrusaSlicer's, which never carry
a recipe. Its round-trip test only exercised that dead loop. The BBS backend,
which every save and load goes through, is untouched.
2026-08-31 18:31:37 +08:00
Ian Chua b585d9f3d9 feat: add new profiles over ota & updater url via app config 2026-08-31 17:45:29 +08:00
SoftFever 5add7a5062 Show Orca Filament Library filaments in AMS and calibration dialogs
These dialogs treated a filament with no compatible_printers as compatible with
nothing, while the rest of the app treats it as compatible with everything, so
the entire Orca Filament Library was missing from the AMS material and
calibration filament lists. They now resolve compatibility the same way the
plater does, and a vendor profile still supersedes the library generic of the
same name.
2026-08-31 16:25:59 +08:00
ExPikaPaka 2ae1e09dc2 Merge branch 'feature/texture_displacement' of https://github.com/OrcaSlicer/OrcaSlicer into feature/texture_displacement 2026-08-31 08:45:38 +02:00
ExPikaPaka ae45d7b78a Add color suport for textures 2026-08-31 07:58:48 +02:00
Lam Wei Lun fff0efdb27 Fixes filament import bug 2026-08-31 13:57:21 +08:00
Clifford GarwoodandClaude Opus 5 855c1b51ac fix: size the destination row before migrating per-variant values
update_values_from_multi_to_multi_2 iterates the destination PRINTER's variant
list while writing into a row taken from the destination PRINT preset. Those two
lengths are maintained independently -- print_extruder_variant against
printer_extruder_variant -- and Tab::load_current_preset() runs the migration
before the print preset is re-selected for the new printer. Opening a project
saved on a single-variant printer and switching to a seven-variant one therefore
wrote six elements past the end of a one-element vector. The corruption stays
silent until the next allocation, so the abort surfaces somewhere unrelated and
the backtrace points at innocent code.

Size the row to the variant count before indexing it. Every write is then in
range, and the result carries one value per destination variant, which is what
the callers consume. Pad with nil rather than a copied value: set_to_index()
skips nil entries, so a variant the object has no opinion about keeps tracking
the print preset instead of being pinned to another variant's number.

The same shape -- a count from one array indexing another -- appears twice more
in this file. update_values_from_multi_to_multi has three of these writes
protected only by assert(idx < old_count), and NDEBUG is defined for every
non-Debug configuration, so those guards are absent from shipping builds.
update_values_from_single_to_multi has the read half. Both are bounded here;
leaving them would fix one third of one defect.

Source reads are bounded too. is_nil(size_t) indexes values[idx] without
checking, so an index past the end was undefined behaviour on that side as well.

Where the row already matches the variant list -- every case that was not
corrupting the heap -- the resize is a no-op and the output is unchanged.

Fixes #15455

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 01:47:45 -04:00
harrierpigeon e5d4ad2aa7 Merge remote-tracking branch 'upstream/main' into haryr/aug25-rebase
# Conflicts:
#	src/libslic3r/Support/TreeSupport.cpp
2026-08-30 23:31:48 -05:00
harrierpigeonandClaude Opus 5 4fab8d0b39 fix: adapt belt sub-layer group emission to upstream m_writer unique_ptr
Upstream changed GCode::m_writer from a value to std::unique_ptr<GCodeWriter>;
the belt mixed_sub_layer_groups path still used value syntax and did not compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ChxXYc6Dp46qAN9c2rQCe
2026-08-30 23:31:21 -05:00
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