* Save device url in all cases and load printer url after hot-reload finishes
* Recreate web view from scratch as only URL fix seems not robust enough
* Add the same robust browser recreation for WebViewDialog
It should eliminate possible issue with blank Home and other pages
in the same way as Printer page
* Remove redundant fallback leftover
* Fix webview reset state and replay Project info on page reload
The first-show webview reset now runs only on Windows, reloads the last
printer URL and resets the Project page's ready state. The Project tab
replays its 3MF info whenever the page reloads, so it no longer goes
blank after a theme switch or a slow first load. The Device tab no longer
loads an extra time on first open, and the Home tab no longer navigates
twice. NeedsRecreateOnShow() logs is_recreating_gui so one language
switch shows whether the reset ever fires.
* Build plugin pages on first show
A language switch rebuilt every plugin page's browser while the main
window was being recreated, which left plugin tabs blank on Windows.
Plugin pages are now lazy pages, never prebuilt, and are removed left
to right so removing pages never builds one only to destroy it.
A plugin page's script now starts when its tab is first opened;
messages posted before that are dropped.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
* Add Missing Includes Across src/libslic3r
Every libslic3r source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, with libslic3r headers spelled libslic3r/... so they resolve outside the library's private include paths. MultiMaterialSegmentation.hpp, Support/SupportParameters.hpp and Format/STEP.hpp are made self-contained by hand.
* Make the libslic3r Headers Compile on Their Own
Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Left out: I18N.hpp, which errors on purpose when included from GUI code, and VoxelizeCSGMesh.hpp and SLA/bicubic.h, which nothing includes and which no longer compile at all.
* Add the Includes Missing From the Hand-Fixed libslic3r Headers
clang-tidy would not edit these headers while they failed to compile on their own, so the first pass skipped them. With the headers now self-contained, a second pass adds the rest.
* Keep Windows Setup Ahead of the Added libslic3r Includes
Print.cpp and Thread.cpp open with a _WIN32 block that has to come first; without the precompiled header, Print.cpp otherwise reaches windows.h through OCCT with NONLS defined and boost/regex fails. OpenVDBUtils.cpp and SLA/SupportTreeBuilder.cpp had includes inside #ifndef NOMINMAX, which libslic3r defines on Windows, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory.
* Re-Add libslic3r Includes After the Clipper2 2.0.1 Migration
Rebasing onto main took main's version of the files the Clipper2 migration rewrote, so their added includes are restored here, along with includes for main's new code. Clipper2's individual headers are now ignored by clang-tidy: they only build the Z variant through clipper2_z.hpp, which defines USINGZ first, so including clipper.core.h and the like directly broke ClipperZUtils.cpp.
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy
Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp.
* Ignore minilzo's Config Headers in clang-tidy
lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes.
* Add Missing Includes Across the Remaining Sources and Tests
Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo.
* Make the GUI and Test Headers Compile on Their Own
Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone.
* Keep Windows and nanosvg Setup Ahead of the Added Includes
OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build.
* Add the GUI Includes the First Pass Missed
Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass.
* Keep the Added Test Includes Below the NOMINMAX Guard
test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
* Fix gizmo checkbox styling and Texture Displacement resize
Use shared BBL checkboxes in Texture Displacement and restore white toolbar checkmarks so other gizmos keep proper checkbox contrast. Also fix Texture Displacement resizing only after mouse movement by requesting additional frames while its layout is still changing.
* Fix gizmo checkbox styling and Texture Displacement resize
Use shared BBL checkboxes in Texture Displacement and restore white toolbar checkmarks so other gizmos keep proper checkbox contrast. Also fix Texture Displacement resizing only after mouse movement by requesting additional frames while its layout is still changing.
* Update GLGizmoTextureDisplacement.cpp
---------
Co-authored-by: yw4z <ywsyildiz@gmail.com>
Since main moved to Clipper2, parts of this branch no longer gave the same
G-code as main:
- bridge_over_infill dropped expand(limiting_area, 0.3 * flow.spacing()) as
a no-op. The offset is below one unit, but Clipper2 still unites its
result, which splits and merges touching polygons and so changes the
anchor lines. Running it on the polygons next to the bridge gives main's
anchors without the whole-layer pass.
- tsp_remove_crossings stopped at the first repeated ordering, where main
runs on to its pn * pn cap. The loop is periodic from that point, so it
now takes only the steps to the ordering main stops on.
- With a single tile, the tiled booleans now make the plain call instead of
cutting the clip to the tile first.
The tiled boolean test compared rings exactly. With the safety offset a tile
unites only the clip polygons near it, and Clipper2 can then round a
crossing 1 unit differently, so that case allows 1 unit.
Comments that named ClipperLib now say Clipper, and
docs/HLSD/polygon-clipping.md describes the tiled booleans.
G-code of the five handy models in four configurations and of a baked
texture relief is byte-identical to main. Colour-painted models still
differ: segmenting each island on its own splits a colour's region into
different pieces than one diagram over the layer, which on one model also
changes the first layer's tool order.
The timings and the runs that never finished belong in the commit
messages, where they can be read against the change; a reader of the
code cannot check them. Kept the cost that still explains the design.
MultiPoint also spells out the consequence: a moved-from Polygon or
Polyline is now really empty where it used to silently keep its points.
The two wall spacing comments the parallel loop reindented are plain
ASCII now, so the whole file is.
Area alone would pass on a result whose pieces were merged across tiles
or which kept the cut edges of the clip. The rings are compared after
rotating each to its lowest point and sorting, so only the ordering is
free. The fixture now also asserts it really is split into more than one
tile, which is the path being tested.
append(const ExtrusionEntity &) clones; the collection each island
produced was deep-copied into the layer's loops and then thrown away.
The no-overlap areas are moved as well.
The Windows build stopped on test_kdtree.cpp: it calls std::iota without
including <numeric>, which libstdc++ happens to pull in anyway. Added
there, and the same for <limits> and <algorithm>/<cmath> where the
recent changes rely on them being included by something else.
bridge_over_infill's helper for splitting polygons by proximity named
its locals near and far. The Windows headers define both as macros that
expand to nothing, so "Polygons near;" declared nothing and the uses of
it did not compile. Renamed; no behaviour change.
Collecting every candidate within the radius into a vector cost more
than the search itself. Same order, so the seams are unchanged;
align_seam_points ~19.6 s at 0.1 mm / 2000k, was ~21.
detect_surfaces_type, process_external_surfaces and the vertical shells
each waited on their own heaviest layer in turn. The LOTR map plate
slices in ~10.5 min at 0.1 mm / 2000k, was ~11.5; ~87 s at normal
settings, was ~97.
MultiPoint had no rvalue constructor, so the derived move constructors
bound to the const reference and copied; append reserved exactly, so
collecting pieces one by one was quadratic. Colour segmentation ~3 s at
0.1 mm / 2000k, was ~40, and ordinary prints gain too.
Only the slices within the deepest shell offset decide the result, so
the work is done per tile of the face. Top and bottom segmentation
~130 s at 0.1 mm / 2000k, was ~180.
ClipperLib slows down with the number of edges on a scan line, and a
layer cut through a fine relief has tens of thousands of pieces.
detect_surfaces_type ~50 s at 0.1 mm / 2000k, was ~145.
The merge took anything from 3 to 38 minutes at 0.1 mm / 2000k, now
~2.5. Every region is grouped with the islands it overlaps, so the
result is the same.
A colour texture baked at 0.1 mm / 2000k made the top layers thousands
of islands and slicing never finished. Colour segmentation runs per
island, the merge subtracts piece by piece, the support check tests only
nearby islands, and the travel ordering finds crossings through a grid.
A layer split into ~1000 colour fragments (a colour texture baked over a
large top face) made several per-fragment loops redo whole-layer ClipperLib
work, so slicing took ~33 min; it now takes ~3 min with the same output.
- make_fills: clip the layer's no-overlap area to each expolygon's box
before intersecting
- discover_vertical_shells: small-piece filter compares only against the
nearby part of the layer
- bridge_over_infill: whole-layer union/diff/intersections restricted to the
candidate's neighbourhood; fill boundary expanded once per spacing; anchor
tree built only from lines crossing the scan range; bbox pre-check in the
collision test; limiting outline taken directly instead of through
expand(..., 0.3 * flow.spacing()), which offsets by 0.135 scaled units
(flow.spacing() is in mm) and only cost a whole-layer pass per candidate
notarytool submit --wait has no timeout. On 2026-10-02 it hung for
over 5 hours in a main build. Since #16044 a new push no longer
cancels a running main build, so nothing stopped it and six waiting
main runs were replaced without starting.
Over the last 30 days the step succeeded 206 times, with a median of
4.3 minutes and a maximum of 14.8.
* Fix debug build after qhull upgrade
We upgraded qhull from 8.0.1 to 8.0.2 in 504a5d3b70, which contains a commit qhull/qhull@16159c648c `use same CMake target name for Debug and non-Debug`, so this target name check is no longer required
* Fix issue like `IMPORTED_LOCATION not set for imported target "opencv_world" configuration "RelWithDebInfo".` when build Debug config
* Keep User Preset Values on Extruder Variants They Don't List
A user preset stores the variant list its parent had when it was saved.
When the parent later gains variants, update_diff_values_to_child_config
matched variants by name only and left the new ones at the parent's
value, so the user's settings were silently replaced there, and a
re-save wrote the system values into the user's file.
An unmatched parent variant now takes the child's first variant of the
same extruder, the rule slicing already uses in get_config_index_base.
A child without a variant list covers the parent's first extruder. The
name match also no longer indexes the child's extruder ids when it has
none.
* Share One Variant Column Rule Between Slicing, User Presets and Projects
Three places chose which variant column a value comes from, each with
its own copy of "the same variant and owner, else the owner's first
column": get_config_index_base when slicing, the user preset merge in
update_diff_values_to_child_config, and normalize_filament_values_to_variants
for projects and the CLI.
find_variant_column now holds that rule and map_variant_columns applies
it to a variant list, so a change to how missing variants are filled
reaches all three. Each caller keeps its own copy step. There is no
behaviour change: G-code is identical before and after. The one
relaxation is that get_config_index_base no longer reads past a short
id list when its two lists differ in length, which its assert already
rules out.
* Rename variant column helpers to variant index
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
* fix: include bundled UV binary for arm64
* fix: update unit test CI
* Install unit-test numpy only with the bundled uv
* Simplify the unit-test script's uv lookup
---------
Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
The ARM64 jobs pinned CMake 3.31 because CMake 4 dropped pre-3.5 policy
compatibility and its ARMASM support broke Boost.Context. Both are
handled now. deps/CMakeLists.txt sets CMAKE_POLICY_VERSION_MINIMUM on
CMake 4, and Boost.Context uses the winfib implementation on ARM64, so
nothing assembles with armasm.
CMake 3.31 also predates VS 2026. Its InstallRequiredSystemLibraries
treats the v145 toolset as v143, searches only the VS 2017-2022 install
directories and finds no runtime, so the ARM64 installer ships without
msvcp140.dll and vcruntime140.dll. CMake 4.2 and newer find the VC145
redistributable.
get-cmake also installs Ninja, so the ARM64 jobs now use its latest
release instead of the one already on the runner, as x64 does.
The install now fails when InstallRequiredSystemLibraries returns no
msvcp140.dll or vcruntime140.dll, after a configure warning naming the
CMake and MSVC versions. A CMake that predates the Visual Studio in use,
on a developer machine or after the next runner image update, then
stops the installer build instead of shipping one that cannot start.
The build_win.bat prerequisite installer drops its matching 3.31.8 pin.
This is the bulk of the profile changes for U1 that led to the cooling
catiant connection.
The ugly end gcode is used by the printer's UI to complain if normal
nozzle is installed but the file was sliced for highflow.
[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
* Make Painted Multi-Material Slicing Deterministic
Painted (multi-material) models sliced to slightly different G-code on
every run: ±1 µm wall coordinates and reordered islands. Hashing each stage
of the segmentation across runs showed the projected painted lines and the
per-layer Voronoi segmentation were stable; the raw top/bottom projections
from slice_mesh_slabs() were not. Three causes, all thread-order dependent:
- slice_slabs_make_lines() appends each slab's intersection lines from a
parallel facet loop and never restored a canonical order, so the loop
start vertices and polygon order from make_slab_loops() depended on
scheduling. Sort every slab's lines with the same key slice_make_lines()
already uses.
- segmentation_top_and_bottom_layers() wrote a layer's shell projections
into neighbouring layers' vectors from the parallel loop, relying on a
parity double-buffer that assumes TBB ranges are exactly one group wide
and aligned, which blocked_range does not guarantee; two threads could
append to the same vector. Each source layer now records its projections
in its own slot and they are gathered per target layer in source order.
- The painted-line sort in post_process_painted_lines() was not a total
order: projections of one span from facets of different colours tied on
every key and the first one won the span. Colour and end points now break
the tie.
Three multi-threaded runs of each painted fixture now give one G-code;
unpainted output is unchanged.
* Test That Slab Slicing Does Not Depend on the Thread Schedule
Projects a dense, tilted sphere with slice_mesh_slabs() on one thread and
then three times multi-threaded, and requires the polygons to match exactly,
vertex order included. Fails without the canonical line sort, passes with it.
* Add Missing Includes Across src/slic3r/GUI
Every GUI source and header now directly includes the headers declaring what it uses, rather than relying on the precompiled header or transitive includes. Generated with clang-tidy misc-include-cleaner, plus one hand edit making CalibrationPanel.hpp self-contained.
* Drop the OS-Specific Includes Added Outside Their Platform Guards
GLib, GTK, D-Bus and POSIX headers are only used inside platform #if blocks, which already include them. Added unconditionally at the top of the file they broke the Windows build.
* Add the clang-tidy Configuration That Generated These Includes
Only misc-include-cleaner's missing-include check, with the headers it must never suggest: per-platform, internal and OS-specific ones that would break other platforms or are not meant to be included directly.
* Match Windows Paths in the clang-tidy Ignore List
Header paths use backslashes on Windows, so every / in a pattern is now [/\\]. The Windows SDK headers are ignored alongside the other OS-specific ones, and the list is one pattern per line. Suggested by @raistlin7447 from a Windows clang-cl run.
* Lock Config and Preset Files Across Instances and Write Them Atomically
Every running instance shares one OrcaSlicer.conf and one user preset
tree, and nothing kept their writers apart. Two instances saving at the
same moment, or the cloud preset sync thread writing while the GUI thread
saved, could interleave, and a reader in another instance could open a
preset JSON or .info file between truncate and close and get a partial
file, dropping that preset for the session with a parse error.
Add InstanceLock, a scoped guard that serialises the threads of one
process through a recursive mutex and other processes through an advisory
OS file lock: flock on POSIX, held on the guard's own descriptor so no
other close in the process can drop it, and LockFileEx on Windows. The
outermost guard opens the lock file and closes it on release, so nothing
stays open between saves and a data dir can be removed once nothing is
saving into it; the file itself is kept, since deleting it would let a
third instance lock a fresh file while the second still holds the old
one. It is best effort: when the lock file cannot be opened or locked, or
another instance still holds it after a second, the guard logs once and
lets the write proceed, then leaves the file alone for ten seconds, so a
hung instance never blocks every other one and a holder stuck in a
debugger does not cost a stall per save. The guard sits at the leaf
readers and writers: set_sync_info_and_save() calls save_info() under the
preset collection mutex, so a batch lock around save_user_presets() would
invert the order against the sync thread. The user preset scan reads its
files on worker threads without the guard, since the mutex would
serialise them, and takes it per file in the serial commit step, so a
save never waits for the whole scan. Each read keeps the bytes of the
preset and its .info as they were before parsing; commit compares them
with the disk under the guard and reads a file that changed again, so it
never deletes or writes back over another instance's newer save, nor
installs a .json and .info from two different saves; a preset another
instance removed in the meantime is not installed. Without the guard, in
a cool-down, the scan still loads the presets but leaves their files
alone: an unreadable file stays for the next scan, and a derived
compatible printer is not written back. Read-only scans, which is what
the CLI does, take no lock and create no lock file.
AppConfig holds OrcaSlicer.conf.lock in load() and save(); load is
included because the Windows path restores from the .bak copy. Every
user preset writer and reader holds user.lock: Preset::save(), which
writes no .info when the preset itself could not be written, since an
.info without its preset reads as a cloud deletion request, save_info(),
reload() and remove_files(), each preset the scan commits, the
bundle metadata reads and write, the .info removal after a cloud-confirmed
delete, the orphaned-.info scan on the sync thread, the bundle folder
removal on unsubscribe and the physical printer writers and delete
paths. A bundle import extracts under cache/ into a folder per process
and per import, where no scan reads.
Preset JSON, .info, bundle metadata, physical printer and config files,
and the caches and state files that already used a temporary by hand,
now go through write_file_atomically(), which writes <file>.<pid>.<n>.tmp
beside the target and renames it over, so a reader that never waits sees
a complete old or new file. A symlink is followed; a target that is not
a regular file is written in place; and when no temporary can be created
beside an existing target, or the rename itself is refused, by a Windows
reader holding the file open or a mount that cannot replace in one step,
the helper writes in place as before, since losing the save is worse
than a torn read. On POSIX the rename replaces the
target atomically where the old code removed it first and left a window
with no file at all; only a mount that refuses a one-step replace gets
the old remove-then-rename. A crash between temporary and rename leaves
the temporary behind, which no scan reads. Preset::save() returns
whether it wrote the preset, so the scan counts a compatible printer it
could not write back as an error. A failed config write keeps
the config dirty, and the idle handler waits ten seconds before retrying
while an explicit save always tries.
* Run the Cross-Process Lock Test on Every Platform
The test that checks the guard yields to a lock held elsewhere forked a
child to hold it, so it was left out on Windows. The OS lock belongs to
the handle on Windows and to the open file description elsewhere, so a
second handle in the same process is refused like another instance
would be. The test now holds the lock that way and runs everywhere.
extrude_infill() and extrude_support() reversed the layer's extrusion
entities in place while chaining them, so each export started from the
previous one's reversed toolpaths, and each copy of an object from the
copy before it. The export-time region lists now hold const pointers,
and chaining reverses a clone instead.
The display-name tests call set_user_session(), which persists the session.
The agent they built was in keychain mode, so every run saved a fake
OrcaSlicer/Auth session into the system keychain of whoever ran the tests,
replacing their real Orca Cloud login on any desktop with a working keychain.
Move them next to the other agent tests and build the agent the same way:
encrypted-file mode with a throwaway config directory.
An ironing line spacing of 0 reached the fillers from a 3MF, the CLI or
the per-filament override, which has no GUI guard. Concentric ironing
then never finished slicing, because a zero inset never shrinks the
region, and rectilinear ironing was silently dropped. Tiny positive
values produced an unprintable number of lines.
Top surface and support ironing now clamp the spacing to the 0.05 mm
floor the process GUI guard already enforces, so these configurations
iron at that spacing. Spacings at or above the floor, including every
shipped profile, are unchanged. The concentric filler also returns early
on a non-positive step so no other caller can hang it, and the filament
settings page now resets a too-small override the same way the process
page does.
# Description
With Xcode 27, building deps on macOS 26 fails at the Python install
step with a segfault, and a libpython built with Xcode 27 crashes on
macOS 12–26 the first time anything calls `os.pipe()`, which every
plugin `subprocess` call does. The macOS 27 SDK declares `pipe2()` and
`dup3()` as macOS 27-only, and CPython 3.12 calls them without a runtime
check once configure finds them, so on older systems they resolve to
NULL. This keeps CPython on the `pipe()`/`dup2()` fallbacks it already
uses with older SDKs; upstream fixed it in 3.13+
([python/cpython#153711](https://github.com/python/cpython/issues/153711))
but not in 3.12.
No change for builds with Xcode 26 or older, or on Linux and Windows.
# Screenshots/Recordings/Graphs
<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->
## Tests
Rebuilt deps with Xcode 27 on macOS 26.6: the Python install step now
completes, the installed libpython no longer imports `pipe2`/`dup3`, and
CPython's `test_os`, `test_subprocess` and `test_posix` pass, apart from
one test that needs `_testcapi`, which `--disable-test-modules` leaves
out. Running CPython's configure against the macOS 26.5 and 27.0 SDKs
gives a byte-identical `pyconfig.h` for 26.5 with and without this
change, and for 27.0 with it. The x86_64 cross-build path was configured
on arm64 to confirm both of its configure runs pick up the change.
<!--
> 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)