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>
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>
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>
* Add ffmepg dep
* NEW: reimpl wxMediaCtrl from ffmpeg
Jira: none
Change-Id: I46a47118a7649b2a50fcce8911e2888342ef25de
(cherry picked from commit d6c7f08769c8cfdbbf0e80ad280c9b3408a3c27d)
(cherry picked from commit 94d91be60bfe9bbbcdd21f85b46abc3faf126f17)
* FIX: reset bambu lib after restart network plugin
Change-Id: I4a3a4b7420745835ca3fa00c6edebe9d8d98cbf6
Jira: STUDIO-7571
(cherry picked from commit 28d9c6743fae80bfd40e4ee391e30d62cb16d4ab)
* FIX: ffmpeg decoder memory leak
Change-Id: I997572b5730618a969959f9b24c405d80fa9f83c
Jira: STUDIO-7597
(cherry picked from commit 342cea29bd9593fa89cbb33caff58055b46ebeec)
* FIX: install ffmpeg symbolic sos
Change-Id: Ia4a45182cefcf62a7a4b4a5c89c92251609c5a68
Jira: none
(cherry picked from commit b7f8fa1efdbe0ac2cc896ca24f063f5894fe9f90)
* FIX: ffmpeg swscale & frame_size
Change-Id: I9f4cb8c739b726f7e5cdbe0df7ed06b2eb2154d5
Jira: STUDIO-7624
(cherry picked from commit 5a2c75d835fb437667b590a803eef148baa30875)
* FIX: wxMediaCtrl3 idle image & center pos
Change-Id: Ib9652573e31bfd6229f174c0a1388942d9d98822
Jira: STUDIO-7633
(cherry picked from commit d51247c46e26460b151de79c598d81151280e79c)
* FIX: AVVideoDecoder sws_ctx_ == nullptr on zero size
Change-Id: I9698354bb1f341e276ec9780d4ef4fcd9f8a1028
Jira: STUDIO-7706
(cherry picked from commit ff622e25026a8471c39eb308cf5b115c4a9d84aa)
* fix:cannot open shared object file on linux
Change-Id: Ica66500506cfe8932eac3ae0a58fb7ff30d1da9b
jira:none
(cherry picked from commit febd1aeb4d453bc96571fa5e5727e9e10046cb80)
(cherry picked from commit 5ad579f929154779abd84b01438fd235c647dbf5)
* NEW:add ffmepg build Cmake
buildLinuxImage add ffmpeg so file
jira:nojira
Change-Id: I3e1be53aa58a179b8d9ae048ed7538de3ae8d111
(cherry picked from commit 2d70a1bcb6a5ba601525b08a38e7610f018fe106)
* FIX: ffmpeg cmake install error
jira:nojira
Change-Id: I74cc0f7c86b5364e55cad2af2bd9a82306ee6864
(cherry picked from commit 805df79e3bb044dac29ec1c06736751ccf3675f9)
* FIX: decode video to wxImage on Linux
Change-Id: I5e332a1b0622b3dfc70ac5c4c3bfa62b3411ebdc
Jira: none
(cherry picked from commit c787ba921a31f259e8eb23fd59f178e96279caf9)
* FIX: wxMediaCtrl3 enter Stopped state soon
Change-Id: I120e9d4b9f85599a184650d1d95fe2bec42af171
Jira: STUDIO-8280
(cherry picked from commit 7648d96305d510b9e97f22124961de5115cde830)
* FIX: reset decode buffer zero when scale width changed
Change-Id: Iaa2f99111dd5f7228b7b25e1be0a8cbdbfe982a6
Jira: STUDIO-8422
(cherry picked from commit 659ebc7d07a8f6045ba5443141b44277d7257cec)
* slic3r: Fix missing declarations in wxMediaCtrl3.h
src/slic3r/GUI/wxMediaCtrl3.h:80:10: error: ‘condition_variable’ in namespace ‘std’ does not name a type
80 | std::condition_variable m_cond;
| ^~~~~~~~~~~~~~~~~~
src/slic3r/GUI/wxMediaCtrl3.h:27:1: note: ‘std::condition_variable’ is defined in header ‘<condition_variable>’; did you forget to ‘#include <condition_variable>’?
26 | #include "Printer/BambuTunnel.h"
+++ |+#include <condition_variable>
27 |
src/slic3r/GUI/wxMediaCtrl3.h:81:10: error: ‘thread’ in namespace ‘std’ does not name a type
81 | std::thread m_thread;
| ^~~~~~
src/slic3r/GUI/wxMediaCtrl3.h:27:1: note: ‘std::thread’ is defined in header ‘<thread>’; did you forget to ‘#include <thread>’?
26 | #include "Printer/BambuTunnel.h"
+++ |+#include <thread>
27 |
In file included from src/slic3r/GUI/MediaPlayCtrl.h:17,
from src/slic3r/GUI/MediaPlayCtrl.cpp:1:
src/slic3r/GUI/wxMediaCtrl3.h:77:13: error: field ‘m_frame’ has incomplete type ‘wxImage’
77 | wxImage m_frame;
| ^~~~~~~
(cherry picked from commit 727a73333bd67acf5ff2b1c51ff284c2bacdb413)
* slic3r: Fix missing includes in AVVideoDecoder
In file included from src/slic3r/GUI/AVVideoDecoder.cpp:1:
src/slic3r/GUI/AVVideoDecoder.hpp:28:20: error: ‘wxImage’ has not been declared
28 | bool toWxImage(wxImage &image, wxSize const &size);
| ^~~~~~~
src/slic3r/GUI/AVVideoDecoder.hpp:28:36: error: ‘wxSize’ has not been declared
28 | bool toWxImage(wxImage &image, wxSize const &size);
| ^~~~~~
src/slic3r/GUI/AVVideoDecoder.hpp:38:10: error: ‘vector’ in namespace ‘std’ does not name a template type
38 | std::vector<uint8_t> bits_;
| ^~~~~~
src/slic3r/GUI/AVVideoDecoder.hpp:9:1: note: ‘std::vector’ is defined in header ‘<vector>’; did you forget to ‘#include <vector>’?
8 | #include <libswscale/swscale.h>
+++ |+#include <vector>
9 | }
src/slic3r/GUI/AVVideoDecoder.cpp:145:89: error: invalid use of incomplete type ‘class wxBitmap’
145 | bitmap = wxBitmap((char const *) bits_.data(), size.GetWidth(), size.GetHeight(), 32);
| ^
(cherry picked from commit 781ce14e061366da64fdc2d0d592fa35ee57e67e)
* slic3r: Fix missing includes in wxMediaCtrl2
src/slic3r/GUI/wxMediaCtrl2.cpp: In lambda function:
src/slic3r/GUI/wxMediaCtrl2.cpp:170:13: error: ‘wxMessageBox’ was not declared in this scope; did you mean ‘wxInfoMessageBox’?
170 | wxMessageBox(_L("Your system is missing H.264 codecs for GStreamer, which are required to play video. (Try installing the gstreamer1.0-plugins-bad or gstreamer1.0-libav packages, then restart Bambu Studio?)"), _L("Error"), wxOK);
| ^~~~~~~~~~~~
| wxInfoMessageBox
src/slic3r/GUI/wxMediaCtrl2.cpp: In member function ‘void wxMediaCtrl2::Load(wxURI)’:
src/slic3r/GUI/wxMediaCtrl2.cpp:179:5: error: ‘wxLog’ has not been declared
179 | wxLog::EnableLogging(false);
| ^~~~~
(cherry picked from commit 73908d38d8b1f7c8dcae92d55711bc08cbfff23c)
* slic3r: Fix missing wxPaintDC declaration
src/slic3r/GUI/wxMediaCtrl3.cpp: In member function ‘void wxMediaCtrl3::paintEvent(wxPaintEvent&)’:
src/slic3r/GUI/wxMediaCtrl3.cpp:121:5: error: ‘wxPaintDC’ was not declared in this scope; did you mean ‘wxPoint’?
121 | wxPaintDC dc(this);
| ^~~~~~~~~
| wxPoint
(cherry picked from commit 9ab5009235d212699f91e01d7f930f92849ed1e3)
* slic3r: Fix missing BOOST_LOG_TRIVIAL declaration
src/slic3r/GUI/wxMediaCtrl3.cpp:181:23: error: ‘info’ was not declared in this scope
181 | BOOST_LOG_TRIVIAL(info) << msg.ToUTF8().data();
| ^~~~
src/slic3r/GUI/wxMediaCtrl3.cpp:181:5: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope
181 | BOOST_LOG_TRIVIAL(info) << msg.ToUTF8().data();
| ^~~~~~~~~~~~~~~~~
(cherry picked from commit c5c41e20ca2fc7f3b53a4c769961f73df6992008)
* FIX: wxMediaCtrl3 zero size crash
Change-Id: I16a3f7b3afe142bb957a1740b8e8c9820c92b349
Jira: STUDIO-8522
(cherry picked from commit 8cdaea1162ccbcc0bd03ecd99346f3b9cf52cf64)
* FIX: TabCtrl button margin
Change-Id: If8b05a4ef9efb8b57989ee1de6543631e5a3cf90
Jira: STUDIO-8265
(cherry picked from commit 1c5e65707109ad0582b6442cf8e515344f799c27)
* ENH: wxMediaCtrl3 display video frame at pts
Change-Id: I8847236d2307101e5f2befc6477cd20b3691841c
Jira: none
(cherry picked from commit 05328da4612c11d50f6fd90e872b97f5f8f46b1d)
* Fix: fix memory leak caused by ffmpeg decoding
Change-Id: I162ad4ea8d4601c1ffe17a65f292566c9dea6f0b
jira: no-jira
(cherry picked from commit eb20d03186c86b7398b97e3bae0a3c7a7b81c58c)
* ENH: update some missing codes
jira: no-jira
Change-Id: Icb2da53911430ac144b0fb601637a7ad31e7e8db
(cherry picked from commit 13b4213f8a24c76c16e49daf905fa29c0f646a5a)
* Fix build
* Update idle image
* Attempt to fix Windows CI build
* FIX: GTK video window resize ran in a free function without member access
wxMediaCtrl_OnSize referenced wxMediaCtrl2's private m_gtk_video_window,
which does not compile on Linux/GTK. Move the resizing into
wxMediaCtrl2::DoSetSize where the member is in scope.
* Install required tools for Linux
* Install required tools for macOS
* Add ffmpeg to flatpak
* Fix Linux build
* Try fix appimage build
* Fix Linux AppImage bundling of deps-built shared libraries
The AppImage dependency closure resolves each bundled ELF's DT_NEEDED
entries with plain ldd, which cannot resolve the deps-built FFmpeg stack
(libavcodec/libavutil/libswscale) once it is copied into the bundle:
those libs are not installed in any standard loader path and carry no
RUNPATH of their own, so ldd reports the siblings as missing and the
build aborts. Extend the loader path with the bundle directory plus the
source directories of already-bundled files (mirroring
scripts/check_appimage_libs.sh), and key the dedup set on the bundled
file path instead of the source path so dependencies resolved from the
bundle directory are not copied onto themselves.
Co-Authored-By: Claude <noreply@anthropic.com>
* Attempt to fix Linux unit test
* Fix Linux unit tests loading deps-built FFmpeg libraries
The test executables that link libslic3r_gui (which links PkgConfig::LIBAV)
have a load-time dependency on the deps-built FFmpeg shared libraries. The CI
unit-test runner only receives the tests artifact, so those libraries were
unresolvable there (Ubuntu 24.04 ships libavcodec.so.60, not .61). Copy the
libraries next to each affected test executable and give it an $ORIGIN rpath,
mirroring the Windows branch that copies DLLs next to every test executable.
orcaslicer_copy_sos now places the copies in the per-config output directory
for multi-config generators, like orcaslicer_copy_dlls does.
Co-Authored-By: Claude <noreply@anthropic.com>
* Add design doc for macOS FFmpeg player
Co-Authored-By: Claude <noreply@anthropic.com>
* Add implementation plan for macOS FFmpeg player
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: use FFmpeg media player on macOS with static FFmpeg
* build: build static-only FFmpeg for macOS deps
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: remove old BambuPlayer-based media player from macOS
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add static FFmpeg NOTFOUND guard; drop dead wxMediaCtrl2.h include
* build: drop redundant --enable-shared in FFmpeg deps configure
The literal --enable-shared was always overridden by ${_link_cmd}
(--enable-static --disable-shared on Apple, --enable-shared elsewhere)
and FFmpeg configure processes these flags in order, last one wins.
Remove it and the stale comment documenting the workaround.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: remove dead wxMediaCtrl2 player
wxMediaCtrl2 was never instantiated on any platform (USE_WX_MEDIA_CTRL_2
is 0 everywhere); wxMediaCtrl3 replaced it. Delete wxMediaCtrl2.cpp/h,
drop them from the Win/Linux source list and the gettext list.txt, and
collapse the preprocessor-dead #if USE_WX_MEDIA_CTRL_2 gate in
MediaPlayCtrl.h.
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: remove dead GStreamer bambusrc plugin and its build dep
gstbambusrc was the GStreamer source element for the old wxMediaCtrl2
Wayland player, its only consumer (deleted in the previous commit).
The new FFmpeg player handles bambu:/// URIs through the Bambu C API
instead. Drop the plugin and the gstreamer-1.0 / gstreamer-base-1.0
REQUIRED pkg-config dependencies that existed solely for it.
Co-Authored-By: Claude <noreply@anthropic.com>
* build: move FFmpeg media player sources to the common GUI list
wxMediaCtrl3 and AVVideoDecoder are platform-neutral C++ compiled on
all three platforms, so list them once in the common SLIC3R_GUI_SOURCES
instead of duplicating them in the APPLE and non-APPLE branches. The
else() branch is now empty and drops out entirely.
Co-Authored-By: Claude <noreply@anthropic.com>
* deps: disable FFmpeg VideoToolbox/AudioToolbox HW-accel on macOS
The static libavcodec.a/avutil.a compiled the auto-detected
videotoolbox/audiotoolbox objects, which reference VideoToolbox
framework symbols (_VTDecompressionSession*). The app link line
happened to satisfy them transitively, but the orca_stubgen module
link (CI-only) failed with undefined symbols. The player decodes in
software (swscale), so disable both HW-accel paths to keep the
static libs self-contained.
Co-Authored-By: Claude <noreply@anthropic.com>
* Copy the decoded frame instead of aliasing the decoder's buffer
wxImage with static_data set stores the pointer and never copies it, so the
frame handed to wxMediaCtrl3 aliased AVVideoDecoder::bits_. That buffer is
rewritten by the next sws_scale with the mutex released, reallocated by
bits_.resize() when the window grows, and freed outright when the decoder
leaves PlayThread's loop body at end of stream, all while the GUI thread may
be painting from it.
Windows is unaffected either way, since toWxBitmap already copies the bits
into GDI.
---------
Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com>
Co-authored-by: BBL\chuan.he <chuan.he@bambulab.com>
Co-authored-by: MackBambu <yongfang.bian@bambulab.com>
Co-authored-by: Bastien Nocera <hadess@hadess.net>
Co-authored-by: chao.zhang <chao.zhang@bambulab.com>
Co-authored-by: lane.wei <lane.wei@bambulab.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
wxImage with static_data set stores the pointer and never copies it, so the
frame handed to wxMediaCtrl3 aliased AVVideoDecoder::bits_. That buffer is
rewritten by the next sws_scale with the mutex released, reallocated by
bits_.resize() when the window grows, and freed outright when the decoder
leaves PlayThread's loop body at end of stream, all while the GUI thread may
be painting from it.
Windows is unaffected either way, since toWxBitmap already copies the bits
into GDI.
* 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>
append_tcr2 scanned the tower's G-code for a "preheat T<n>" comment and rewrote
its S value to the interface temperature. Nothing it could match was ever there.
That comment has exactly one producer, GCodeProcessor's backtrace injector, and
that runs inside run_post_process() -- a pass over the finished, exported file.
append_tcr2 runs while the file is still being generated, so the text it looked
for did not exist yet and could not.
The loop therefore walked every line, matched none, and swapped the string for
an identical copy. Delete it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The badge is meant to predict whether slicing will be refused, and it delegates
to the same helper for that reason. It was feeding that helper a different
filament list. get_extruders(true) resolves a mixed slot into its physical
components -- right for AMS mapping, which has to know what is actually loaded
-- while Print::validate counts the slot itself.
So a plate holding one two-component blend reads as two filaments to the badge
and one to validate. The badge sees two, decides the plate is fine, and stays
silent; the slice is then refused. It also runs the other way: a plate the user
sees as a single colour draws a multi-material warning, because the expansion
made it look like two.
Give get_extruders an expand_mixed flag, defaulted so every existing caller
keeps the resolved list, and have the badge ask for the authored one.
The badge was also only mirroring validate's multi-color rule, not its first
one -- a mixed filament is unsupported in a parallel mode outright. Without it
the badge stays quiet on exactly the plate validate refuses first.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The rule's comment claimed the plate "is not printable as configured: the
primary tool executes the toolpaths while the flow and temperatures were
computed for a filament it cannot load". That is not what the emitter does. It
never uses the declared primary -- it re-derives an effective one from the
filament actually in use -- so a plate whose only filament sits on a Span tool
sharing the primary's gantry produces coherent G-code and would print.
The refusal is still correct, but it rests on intent rather than physics: a
parallel mode exists to run carriages in parallel, and a single-colour plate
riding one span lane is not that. Left as a physical-impossibility claim, the
rule reads as a false positive to anyone who checks it against the emitter --
a review already flagged it as one -- and the obvious "fix" is to relax it.
Say which it is, and keep the genuinely-broken case distinct: a filament routed
to a head outside the mode's active tools still yields a stuck-hot nozzle, and
that one is not a matter of taste.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
Plater::validate_current_plate() runs the same background_process.validate() as
update_background_process(), and on success clears update_apply_result_invalid(false)
and closes the ValidateError notification -- but it never consulted
imex_placement_violation(). Any event reaching it wiped a live IMEX placement error
and re-enabled the Slice button on a plate the slicer still refused; pressing Slice
then hit the check in reslice() and returned early, so the job simply never started.
Reproduce by clicking the bed with the prime tower overlapping a reserved area.
Plater::select_plate_by_hover_id() -> select_plate() calls validate_current_plate()
unconditionally, even when the clicked plate is already current, and deselects as a
side effect -- which makes it look as though deselecting the tower cleared the error.
Escape and clicks off the bed go through selection_changed(), which only renders and
clears nothing.
Extract the violation-to-message mapping into one helper and call it from both paths.
model_fits is set alongside err.string in validate_current_plate, mirroring the
missing-plugin block below it: the slice is already gated by m_apply_invalid, but
leaving m_ready_for_slice true would trap a future consumer that reads it alone.
These are the only two sites that matter. NotificationType::ValidateError has exactly
three references in the tree and update_apply_result_invalid exactly four; the other
slice-ready writers can only touch m_ready_for_slice, never m_apply_invalid, so they
cannot re-enable Slice on their own.
Three adjacent gaps are left alone, all pre-existing: "Slice all" is hard-coded
always-enabled regardless of plate state; a slice-all batch halts silently at a
violating plate because reslice() returns above the line that queues the advance; and
object_list_changed() computes its own can_slice from geometry, harmless only because
the result is ANDed with PartPlate::can_slice().
This is a hole in the shipped tower-zone check rather than a regression from rotating
the tower hull -- it was simply invisible until a tower could be placed in violation.
No automated gate: Plater is GUI-only, the helper is file-local and unlinkable, and
imex_placement_violation() needs a live wxApp and preset bundle. "Both call sites
consult it" is a call-graph property no unit test can express. The grep for a single
imex_placement_violation reference is a future regression tripwire, not evidence this
refactor happened -- it already returned 1 beforehand.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
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>
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>
* 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.