exussum12 on PR #15238: "Esc hardly ever works". He was right, and the word
that matters is "hardly" — it works while you are mid-entity and stops working
the moment you finish one.
The branch asked whether a DRAW TOOL was armed, not whether a SKETCH SESSION
was open:
if (key == WXK_ESCAPE && m_viewport && m_viewport->is_sketching())
is_sketching() is DesignSketchTool::is_active(), true only between arming a
tool and finishing with it. Commit an entity and you are left at
ui_mode=Sketch with no tool armed, and from there Esc did nothing at all, no
matter how many times you pressed it — the Cancel button was the only way out.
Reproduced on the headless rig with SNAPORCA_KEYTRACE, which is what settled
it rather than reading: the key ARRIVES and focus is fine,
[KEYTRACE] key=27 ui_mode=1 is_sketching=0 in_text=0 inline_busy=0 focus=w
so this was never the focus problem it looks like from the outside.
Gate on the session instead. request_exit() is already layered — abort the
in-progress entity, else drop the tool to Select, else exit to Feature — so
widening the gate adds no new behaviour, it just lets the ladder be reached
from its own last rung. is_sketching() stays in the condition as an OR, so
nothing about the armed-tool path changes.
This is the same mistake snaporca-0ud fixed thirty lines above in the same
handler, where gating the sketch key MAP on is_sketching() made all 17 keys
read as dead. The comment there now has a sibling.
VERIFIED ON THE RIG, before and after, same sequence (Design > XY > Shift+S >
click canvas): before, two Escapes left the toolbar on SKETCH with zero pixels
changed; after, the toolbar reads FEATURES — one Esc drops the armed tool to
Select, the second exits the session.
Kernel suite green, 2568 assertions in 191 test cases. libslic3r_gui builds
clean on both forks. Parity 17 identical / 8 diverging as expected.
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.
The tab is now gated on a new enable_cad_feature app-config key, off by
default, exposed in Preferences under General > Features. When off, the
page is never created, so the Design-only camera option is hidden too.
Takes effect on restart, like the other feature toggles.
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.
Building the Design tab on first use (1750c52211) leaves m_design_panel null
until a human selects the tab. McpControl::handle_on_main refused every verb
while it was null, so start_mcp_control_if_enabled() opened the socket and
then answered "Design panel not ready" to everything — for the whole session
if nobody clicked. That is precisely the headless case the socket exists for:
the click-test rig drives this app over it with no window manager and no user.
MainFrame::ensure_design_panel() now builds the panel on demand and returns
it; the tab activation and the MCP dispatcher both go through it, so there is
one construction site rather than two. Safe to build wx controls there: that
handler is dispatched on the main thread by CallAfter, as its own comment
says.
The startup saving is untouched — a launch that never opens the tab and never
speaks MCP still builds nothing.
Verified: libslic3r_gui builds clean 745/745; fork parity green with snaporca,
which carries the same fix (McpControl.cpp is a byte-identical shared source).
* build: remove std::move that blocks copy elision
std::move wrapped around a temporary, or around a local being returned,
stops the compiler constructing it in place. Each edit is the fix clang
suggests, which is to delete the std::move call and keep its argument.
Three of the 39 sites save a move, the two return std::move(local) in
Print.cpp and TreeSupport.cpp:2749. The rest are equivalent either way
and match how the codebase already writes this elsewhere.
Clears 39 -Wpessimizing-move warnings.
* build: drop null checks on references and this
A reference cannot be bound to null and this cannot be null, so the
compiler folds these conditions to true and drops the guard. Seven are
if (&bitmap && bitmap.IsOk()), where IsOk() already does the work; two
test this directly. The guarded code runs either way, so removing the
dead operand changes nothing.
Clears 11 -Wundefined-bool-conversion warnings.
* fix(deps): build the dependencies from scratch with clang-cl
Six dependencies fail once the superbuild compiles them with clang-cl instead
of cl:
- OpenSSL never goes through CMake. Its VC-WIN64A makefile only works with cl,
and an unquoted clang-cl path with spaces produces no .obj files at all, so
the lib step dies with LNK1181. Pin the upstream toolchain.
- Boost.Container's bundled dlmalloc passes int* to the Interlocked API. cl
warns, clang rejects it.
- curl 7.75's configure probes rely on C laxness clang rejects. The results
flip and nonblock.c ends up in the AmigaOS IoctlSocket branch.
- OCCT installs RelWithDebInfo into bini/libi while find_package looks in lib.
It also prepends -Wl,-s to the shared linker flags for every Clang build,
which the MSVC-style linker gets as an argument it does not know. Both
patched hunks sit inside if (MSVC) in the OCCT sources.
- wxWidgets lands in lib/clang_x64_lib, so wxWidgetsConfig.cmake falls back to
the layout that exists instead of assuming vc_x64_lib. It tries the derived
path first, so a cl-built tree consumed by clang-cl keeps resolving the way
it does today. The patch step also resets the one file it touches, so it can
run again after an interrupted build or after the patch itself changed.
- wxInspector goes through FindwxWidgets, which only searches lib/vc*_lib
because _WX_TOOL is hardcoded to vc. It now gets the root and lib dir
derived the same way wxWidgetsConfig.cmake derives them.
Eigen is the seventh, and it breaks on the generator rather than the compiler.
Its test, lapack and blas/testing subdirectories all call
enable_language(Fortran), and they default to ON because the dependency
configures as its own top-level project. Whether that hurts depends on what
CMake finds: the Visual Studio generator supports no Fortran and finds nothing,
clang-cl sits next to the LLVM toolset's flang and works, while MSVC with Ninja
finds Strawberry Perl's MinGW gfortran, which this build already requires for
OpenSSL, and hands it the MSVC-style /machine:x64 that MinGW's ld reads as a
missing input file. The configure dies there and takes every dependency still
in flight with it. Only the headers are consumed here, so the three subprojects
are off.
* fix(deps): honor the superbuild's generator and compiler in sub-builds
orcaslicer_add_cmake_project pinned every dependency sub-build to the Visual
Studio generator whenever MSVC was true, which is also true for clang-cl. That
generator selects its compiler by toolset and ignores the CMAKE_C_COMPILER and
CMAKE_CXX_COMPILER this file already forwards, so the dependencies were built
with cl.exe no matter which generator or compiler the superbuild was given.
Key the three affected decisions on the generator instead: which generator the
sub-builds use, whether CMAKE_BUILD_TYPE is forwarded, and /m versus -j. A
Visual Studio superbuild is unchanged, so the default path and CI behave
exactly as they do today.
build_release_vs.bat now accepts -l to select clang-cl, alongside the existing
-x for Ninja, so the generator and the compiler can be chosen independently. On
the Visual Studio generator -l reaches the slicer only, through the ClangCL
toolset, because the dependency sub-builds have no toolset to inherit; a deps
build in that combination says so rather than quietly using MSVC.
* fix(deps): use upstream wxWidgets compiler layout fix
The compiler-prefix layout fix now comes from SoftFever/Orca-deps-wxWidgets#7, so remove the duplicated local patch and apply step.
* fix(deps): stop Assimp enabling ccache on the RC rule
ASSIMP_BUILD_USE_CCACHE defaults on and applies the launcher through the
global RULE_LAUNCH_COMPILE property, so it wraps the resource-compiler rule
as well. Under Ninja that rule goes through cmcldeps, which does not survive
being launched by ccache, and the build fails with clang-cl reporting /fo as
a missing file.
The superbuild already forwards CMAKE_<LANG>_COMPILER_LAUNCHER, which CMake
applies per language and so keeps clear of the RC rule.
---------
Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
Co-authored-by: raistlin7447 <kris.austin@gmail.com>
SLIC3R_CAD=OFF now builds without SolveSpace or OCCT's ModelingAlgorithms
module, leaving the dependency set identical to upstream's, and the Design
tab's mate connector preference no longer appears in builds without the tab.
When the deps prefix and the project disagree about the option, the configure
fails naming the cause, rather than failing at link time or at first launch
on Windows. The Windows packaging step stages exactly the toolkits libslic3r
links.
The Camera is Plater-owned and shared by every canvas, and Design sits
outside the panel switch that saves and restores it for Prepare,
Preview and Assemble — so orbiting in Design moved what the editor
tabs showed, and Design lost its own view on every switch. Trade the
live camera for a parked one on the way in and back on the way out, so
Design keeps its view the way Assemble already does.
New Project and Open Project went through Plater::priv::reset, which
drops ModelObjects but not the Model-level recipe, and never touched
the Design panel's document at all — so the previous design stayed
loaded and its next edit wrote itself into the new project.
A design that has not been committed to the plate has no ModelObjects,
so the project also read as clean: no autosave, and no unsaved-changes
prompt before the reset threw the design away.
DesignPanel's constructor creates several hundred controls and its own GL
canvas, which every launch paid for whether or not the user ever opened the
tab. The notebook page is now an empty placeholder and the panel is built
into it the first time the tab is selected, so m_design_panel stays null
until then.
This also lifts the ordering constraint that had pushed the plater's
background colour and Hide() below the panel construction; they go back
where they were.
build: clear 295 -Woverloaded-virtual warnings in GUI widgets
Turns three hidden base virtuals into real overrides, clearing 295 of
the 553 -Woverloaded-virtual warnings and taking a full clang-cl build
from 1,264 to 969. Part of #15374.
Search.hpp: SearchDialog::Popup and SearchObjectDialog::Popup took a
wxPoint that neither body ever read, hiding the virtual
wxPopupTransientWindow::Popup(wxWindow*). Both bodies clear the input,
call the base, set focus and refill the list, and SearchObjectDialog
also guards re-entry, so hiding meant none of that ran when the window
was popped through a base pointer. They now override and forward focus.
LabeledStaticBox::SetFont and ScrolledWindow::SetBackgroundColour hid
their base virtuals the same way, so the label metrics recompute and
the child colour propagation only ran for callers holding the concrete
type. Both now override.
Marking a member override makes clang flag every other unmarked
override in the same class, so seven sibling declarations needed the
keyword too. Left unmarked they were worth 481 warnings, which would
have made this a net loss.
MSWDismissUnfocusedPopup is declared only inside #ifdef __WXMSW__ in
wx/popupwin.h, so off Windows there is no base virtual to override and
the keyword would not compile. Both the declarations and the definitions
are guarded, which is how wxWidgets itself declares MSWWindowProc in
wx/nativewin.h and how this repo already handles it in BBLTopbar,
MainFrame, Button, ComboBox and TabCtrl.
ScrolledWindow's constructor left m_userPanel and m_scroll_win
uninitialised unless the style requested a vertical scrollbar, while
SetBackgroundColour dereferences both. No caller hits that today since
every instantiation passes wxVSCROLL, but the override widens who can
reach them, so they are now initialised alongside their siblings.
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
* 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.
Update perimeter traversal to pass each extrusion's closed/open state into `apply_fuzzy_skin`. This lets fuzzy skin logic distinguish contours from closed loops when processing perimeters.
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
build: drop dead private fields, close malformed comments (227 warnings)
Clears 227 of the clang-cl warnings tracked in #15374, taking a full
Windows build from 1,491 to 1,264. Five of the six changes are in
headers, which are re-diagnosed in every translation unit that includes
them, so the count is large for a 14-line diff.
Tabbook.hpp: delete two private fields, unread since the 2022 import.
m_parent also shadowed wxWindowBase::m_parent.
GUI_Utils.hpp: the wxEVT_SYS_COLOUR_CHANGED lambda body is empty on
Windows, so its `this` capture is unused there. (void) this; leaves the
handler bound, which is what stops the event propagating.
DevFirmware.h: mark m_owner [[maybe_unused]]. The class is never
instantiated, and the file tracks BambuStudio, so this is the smallest
divergence.
Eight DeviceTab/ files, AMSItem.cpp and SelectMachine.cpp: block
comments malformed so that they read as a nested /*.
No behavior change. -Wcomment goes to zero, and only the three intended
categories move.
* Fix contour cleanup across coplanar triangles
Avoid generic collinear simplification after slicing. Skip only junctions created by shared edges between coplanar faces so contours stay stable without altering shallow geometry.
Fixes#15364
* Fix contour cleanup across coplanar triangles (code review fixes)
---------
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Call update_dynamic_filament_list() alongside update_mixed_filament_list() in two places: after editing a mixed filament slot and when the filament count doesn't change (e.g., adding a mixed/virtual slot). This ensures per-feature filament lists reflect the updated blended colour and type without requiring a full filament count change.
* 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.
# Description
This PR ports the color mixing feature from BambuStudio.
The port is based on the previous work by @ianalexis in #15231.
This PR completes the port and fixes various bugs.
Several improvements were also made during the porting process.
WIP
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->
<!--
> A guide for users on how to download the artifacts from this PR.
-->
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
* chore: mark every declaration that overrides a base virtual
clang-cl reports 42 member functions across 28 files that override a
base virtual without being marked `override`, inside classes that
already mark their other overrides. That is every occurrence of
-Winconsistent-missing-override in the tree, so the category drops to
zero and -Werror=inconsistent-missing-override becomes available as a
guard against it coming back.
Behaviour is unchanged. Each keyword goes only where clang had already
resolved the declaration to a base virtual, so it records what the
compiler already worked out and cannot affect overload resolution or
dispatch. If any of these signatures had not really overridden a base
method, the build would have failed rather than warned.
Where a declaration already carried `virtual` it is left alone and the
keyword appended, matching the surrounding declarations. Plain
`override` is used rather than the wxWidgets `wxOVERRIDE` macro, which
wx/defs.h defines as `override` beneath a comment marking it obsolete,
and which the rest of src/slic3r already avoids by 1742 occurrences to
113.
A full clang-cl build takes -Winconsistent-missing-override from 1,146
warning lines to 0. Those 42 declarations produce that many lines
because a header is re-diagnosed in every translation unit that
includes it. CalibrationWizardStartPage.hpp alone accounts for 336 of
them from 4 declarations.
* chore: drop unused lambda captures in GUI/Widgets
clang-cl reports 10 lambda captures in src/slic3r/GUI/Widgets that are
never read. Removing them changes nothing at runtime.
Every capture removed is `this` or a raw pointer. clang does not report
a capture whose type has a non-trivial destructor, since such a capture
can be held purely for its effect on an object's lifetime, so nothing
that owns or extends a lifetime is touched. The std::weak_ptr captured
beside the removed `this` in MultiNozzleSync.cpp stays.
This clears the category in GUI/Widgets only. A full clang-cl build
takes -Wunused-lambda-capture from 312 warning lines to 302, leaving
235 sites in other directories for a follow-up.
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