The painting gizmos clip the selected instance at the plate, so a sinking
object stopped at the bed with nothing to show how it continues below it, and
the plate disappeared along with the bed mesh. Give every paint tool the same
view of that region:
- Draw the part of the selected instance below the plate at 50% opacity with
depth writes off, so it reads as the object going through the plate.
- Keep the plate and its grid while the bed mesh is hidden, instead of losing
every reference plane in the scene.
- Mark the plate intersection with a 0.4 mm outline rather than an opaque
filled cap, which covered the model and hid the ghost from above.
- Gate the bed mesh and the below-plate pass on the same predicate. Texture
displacement derives from GLGizmoPainterBase like the other painters but was
missing from the gizmo type list in is_bed_visible(), so it kept its bed.
- Add GLVolume::simple_render()'s ban_color_volume so the ghost keeps its alpha
for volumes with MMU segmentation facets, whose sub-models carry their own
opaque filament colours, and stop gating the ghost on is_active: the colour
painter marks the whole instance inactive and draws it in its own pass.
# Description
Slicing performance work, split out of #14662 so it can be reviewed on
its own. No new features and no behaviour change: the same G-code,
produced faster. Nothing here knows about texture displacement.
The trigger was a plate that took about 24 min to slice at 0.1 mm once a
colour texture was baked onto it. The cause is general, not
texture-specific: ClipperLib's sweep line costs grow with the number of
edges crossing a scan line, and any layer cut into tens of thousands of
small pieces (fine relief, dense colour painting, heavily fragmented
models) makes several per-piece loops redo whole-layer work.
## What changed
**Restrict whole-layer work to the neighbourhood that needs it**
- `make_fills`: clip the layer's no-overlap area to each expolygon's box
before intersecting.
- `discover_vertical_shells`: the small-piece filter compares only
against the nearby part of the layer.
- `bridge_over_infill`: whole-layer union/diff/intersection 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.
**Batch booleans by tile instead of per piece.**
`ClipperUtils::tile_expolygons` groups a layer's pieces into tiles, so a
huge clip contour is clipped once per tile rather than once per piece.
`detect_surfaces_type` at 0.1 mm went from about 145 s to about 50 s.
**Parallelize what is already independent:** a layer's regions in
`detect_surfaces_type`, `process_external_surfaces` and
`discover_vertical_shells`; wall generation and solid infill splitting;
colour segmentation and vertical shells.
**Stop copying polygons on move.** `MultiPoint` had no `Points &&`
constructor, so every derived move constructor passing
`std::move(points)` copied them. Added it, marked the moves `noexcept`,
and made `append(vector&&)` move its elements. Note the consequence: a
moved-from `Polygon` or `Polyline` is now really empty where it used to
silently keep its points.
**Seam placement.** `visit_nearby_points` visits candidates as the k-d
tree finds them instead of collecting them into a vector first.
**Travel ordering and sharp-tail detection** test only the pairs whose
boxes can meet, through a uniform grid and a bbox pre-filter, instead of
every pair.
## Results
| | before | after |
|---|---|---|
| Painted plate, 0.1 mm, 2000k triangles | 1447 s | 587 s |
| Same plate, default 750k | 99.5 s | 85.6 s |
| Unpainted, 0.1 mm | 523 s | 430 s |
## Tests
**179 of 179 G-code outputs byte-identical** across 11 models and 17
configurations (default, fine layers, zero and dense infill, no shells,
normal and tree supports, extra bridge layers, interface shells, three
`ensure_vertical_shell_thickness` modes, thick walls, spiral mode, five
seam modes), comparing full library builds of this branch against its
merge base. The painted plate is part of that matrix, and it is the case
whose layers reach the tiling threshold at all.
New unit tests:
- `test_kdtree.cpp`: the visiting and collecting searches return the
same points in the same order, and a radius search returns exactly the
points inside the radius.
- `test_clipper_utils.cpp`: the tiled and plain `diff_ex` and
`intersection_ex` return the same rings with the same coordinates, with
and without the safety offset, compared after rotating each ring to its
lowest point and sorting. The fixture asserts it really is split into
more than one tile, so it cannot quietly stop covering the tiled path.
Don't log a parse error for a vendor profile pruned beside its cache
Packaging deletes <vendor>.json for every vendor that ships a .opc cache, but installable_cache_version still read it to compare versions. Every packaged build logged a JSON parse error per installed vendor at startup. Check that the file exists first, as installed_vendor_version already does. The returned version does not change.
Sets the exclude-object outline tolerance to 0.2 mm in the four AD5M and AD5M Pro base process profiles. Every AD5M, AD5M Pro, AD5X, Creator 5, Creator 5 Pro and Adventurer A5 process inherits it, and so do some Guider 4 processes. Fewer outline points let these Klipper printers fit more objects on a plate without the "Timer too close" error. Also bumps the Flashforge profile version.
Placeholders that GCodeProcessor fills in after the G-code is generated
(print_time_total_sec and the other print_time_* parts,
used_filament_length, and the print statistics such as total_weight)
hold a tag such as @TOTAL_WEIGHT@ while the template is evaluated.
Multiplying one already failed with a type error. Adding a number
joined the tag as text, so {total_weight + 5} printed 123.455 for a
123.45 g print, and a comparison compared the tag as text, so
{if total_weight > 100} was always true. Neither gave an error.
PlaceholderParser::set_deferred() now registers such a value. Printing
it and joining it to text work as before. A calculation, a comparison,
a regex or a condition on it fails with an error that says the value is
known only after the G-code is generated, also after it was copied to a
variable or through regex_replace(). Adding a number or another
deferred value to one is treated as a sum and fails too. Text that
already contains one joins a number as text, like any other string.
filament_change() inside an {if} branch that is not taken now does
nothing. It used to set the current extruder from an uninitialized
value.
The Printer and Filament title bars now toggle on mouse down instead of mouse up. Closing a dialog over a title bar could send it a stray mouse up, which folded the section. A double click is also bound so it still toggles twice, and Label passes double clicks to its parent.
Newer Qidi firmware for the Q2, Q2C, Plus 5 and Max 4 stores a slot's color as a hex list and its filament as a material id, and filament sync crashed on it. Each slot is now read in either form, the filament type comes from the box object, and any other JSON type error fails the sync instead of closing Orca.
The tab switch queued a reload of the host's home page, which ran after the import URL and replaced it. Switch tabs synchronously first, then queue the import URL so it is the last page loaded.
* Stop re-asking for plugin permissions granted in an earlier session
* fix: Allow plugins to read the embedded Python stdlib
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
* Update GMP to 6.3.0 to Avoid the x18 Register on Apple Silicon
GMP 6.2.1's arm64 assembly keeps loop counters in x18, which Apple reserves and
macOS zeroes on every return to user space (syscalls, page faults, preemption).
When that happens inside one of those loops the counter underflows and the
routine walks the stack until the guard page: EXC_BAD_ACCESS / SIGBUS inside
__gmpn_* whenever CGAL falls back to exact arithmetic (texture import boundary
smoothing, text emboss cuts, mesh repair, mesh boolean).
6.3.0 moves those counters to ordinary temporaries (gmp-6.2 changeset
f4ff6ff711ed); no x18 use remains under mpn/arm64. The GCC 15 patch applies
unchanged apart from a line offset. The tarball comes from the GNU mirror, like
MPFR. Windows is unaffected: MSVC copies the prebuilt libgmp-10 and the arm64
job stages MSYS2's GMP.
Built with the deps recipe flags and ran make check on Linux x86_64 (clang 18,
200/200) and macOS arm64 (Apple clang, 199/199).
* Add gmplib.org as a Fallback Download for GMP
Same two-source pattern as MPFR: the GNU mirror first, the project's own
server second, one hash for both.
* Use the OrcaSlicer_deps Mirror as the GMP Fallback Download
SoftFever published gmp-6.3.0.tar.xz in SoftFever/OrcaSlicer_deps, the
same mirror 6.2.1 came from. Both sources serve the .tar.xz release file,
byte-identical (one SHA-256): the GNU server first, the mirror as fallback.
* Stage GMP 6.3.0 for the Flatpak Build
The Flatpak sandbox has no network, so the manifest pre-downloads every
dependency tarball into external-packages/. Point the GMP entry at the
6.3.0 .tar.xz the deps recipe now expects.
Keep a Disabled Filament Override Off After a Restart
A child preset stores "nil" both for an override the user switched off and
for a per-variant column it inherits from its parent, and the loader filled
every nil from the parent. Whenever the parent preset sets Retract Before
Wipe or another override, switching it off lasted only until the next
restart, project open or newer cloud copy.
Treat the keys in is_filament_extruder_override_key, now including the
ironing overrides, as "nil means off": Preset::save and the project-embedded
preset diff write such an option with all its columns, and the loader keeps
its nil columns while still mapping them onto the parent's variants by
name. Every other nullable option keeps nil as "inherit this column". The
cloud diff only force-emits a disabled override whose default is a real
value.
A failed stream extraction writes 0 into its target, so Float, Int, Percent and FloatOrPercent options were overwritten before deserialize reported the error, and Point/Point3 kept a partially scanned value. Parse into a temporary and assign only on success; FloatOrPercent sets its percent flag only then too.
update_material_item_pos() chose the left or right nozzle panel from the printer alone and then read m_filaments_map, which reset_and_sync_ams_list() fills only for a multi-nozzle plate. Clicking Print plate with a plate sliced for one nozzle (A1) and a two-extruder printer selected (X2D) read the empty map and crashed. For a single-nozzle plate the dialog now uses the single panel.
Gentoo writes `ID='gentoo'` to /etc/os-release. The script only stripped double quotes, so `-u` looked for `scripts/linux.d/'gentoo'` and exited. Single quotes are now stripped too, for both `ID` and `ID_LIKE`.
Fixes#16567
Danielsson propagation in EdgeGrid::Grid::calculate_sdf() doubles a negative neighbor offset (-1 or -ncols) with a left shift, which is undefined behavior in C++17. Multiply by two instead.
Adds test_edge_grid.cpp, which checks signed distances inside and outside a rectangle beyond the initial edge band. Built with -fsanitize=shift, it aborts on the old code and passes with the fix.
Show Reload from disk in the right-click menu when several objects or parts are selected, as it already is for a single object or part. reload_from_disk() already handles selections across several objects, the same way Reload All uses it.
Fixes#10349
Add 20P, 26A and 31B to package_dev_id_types so Orca loads the HMS files it already ships for them. Before this, HMS errors on these printers fell through to the cloud lookup and could show as "Unknown error."
The Bambu payload rewrite only matched tray_info_idx, filament_id and filamentId. The H2C nozzle rack puts filament ids in cate (the auto nozzle mapping request) and fila_id (the rack status), so the printer got Orca's OF ids for the filaments and its own ids back for the nozzles in the same request, and the device tab could not name a rack nozzle's filament. Add both keys to one shared list used by the rewrite and its pre-check.
`Plater::record_slice_preset` built a json from the current presets and then discarded it. It also read `full_print_config()` on the UI thread while the slicing thread's filament-map write-back reassigned it. On multi-filament projects this crashed Orca right after a slice started.
Remove the function and its two callers in `Plater::reslice()` and `PrintJob::process`.
Remove `NotoSansKR-Regular.ttf` and `NotoSansKR-Bold.ttf` from `resources/fonts` (12.4 MB). No code loads them. Korean UI text uses NanumGothic. The files came back by accident with the BambuStudio 1.8 merge (#2844), and macOS and the Flatpak still registered them as app fonts.
Emboss text that uses "Noto Sans KR" on macOS or Flatpak now shows the "Can't load exactly same font" warning when edited.
The emboss tool's fallback for a system with no usable TTF font pointed at `NotoSans-Regular.ttf`, which Orca does not ship. It now uses `HarmonyOS_Sans_SC_Regular.ttf`.
Declare the runtime placeholders that filament, layer-change, timelapse, wrapping-detection, machine-end and change-filament G-code receive, so the Edit G-code dialog can list them and a build with ORCA_CHECK_GCODE_PLACEHOLDERS no longer rejects them. Adds the missing toolchange_z definition and wraps the new labels and tooltips in L() so they get translated.
Fix crash when a daily tip has no body text
has_cjk() looped while i < text.size() - 2. For a string shorter than
two bytes the size_t subtraction wraps, so on an empty string the loop
read past the buffer and up the stack until it hit an unmapped page.
The Russian translation of the "Reverse on even" tip had lost the \n
after its title, so render_text() passed an empty body to has_cjk()
every time that tip was shown during slicing. Restore the line break
and bound the loop so a translation like this can't crash the app.
Moonraker discovery asked the printer for its name before announcing
it, on the GUI thread, with a 5 s connect timeout. When the selected
printer was off, the window ignored input for 5 s after every launch
and the Home page stayed white.
The request never produced a name. It read machine_name or hostname
from /server/info, and Moonraker sends neither there. Discovery now
announces the preset model name without asking the printer.
When logged in to Orca Cloud, the plugin fetch at startup no longer refreshes the token a second time while start() is already refreshing it. It now waits for that refresh and checks the token again. The subscribed and owned plugin requests also run in parallel, so the fetch takes about half as long.
* Shorten the travel between extrusion paths
chain_and_reorder_extrusion_entities() builds the print order of the infill,
ironing and support paths of a region from the nozzle position. It took the
greedy chain, which leaves its longest joins for last; it now shortens it:
- Up to eight paths, chain_segments_exact() finds the shortest chain over
every order and allowed reversal, by dynamic programming over the subsets
of paths, with a fixed start and a free end.
- Longer chains are refined by local search: 2-opt, or-opt of up to three
paths, and moving a run of any length next to the nozzle to the front, each
tried only toward the 32 end points nearest a link, which come from a
uniform grid. A move is applied only if it shortens the chain, so the result
is never longer than the greedy one.
- A nearest-next chain is built too, and refined and used instead when it is
already shorter than the refined greedy chain, which happens when the greedy
chain sweeps away from the nozzle and has to come back with a long jump.
The shared greedy chaining also chains two to four segments exactly, which
covers the islands of a layer and the loops of a perimeter collection. Paths
that cannot reverse, such as Grid's, are only reordered, and collections that
keep their order are left alone. The overload without a start point keeps the
greedy chain.
Co-authored-by: Tobias Gloth <tobias.gloth@3dsystems.com>
* Order objects and islands along shorter tours
Objects under print_order Default, and the tour of islands across objects, are
visited in the same order on every layer, so chain_points_with_postprocessing()
orders them as a closed tour. Up to ten points, tsp_exact_cycle() finds the
shortest tour by dynamic programming over subsets, starting at the point
nearest the start point when one is given. Larger sets add or-opt to the
existing 2-opt and crossing removal: runs of up to three points move next to
one of the eight points nearest an end of the run, reversed or not, and
crossing removal runs last so no crossing is left. Snake ordering is
unchanged; Best of strategies gets the shorter Default tour as a candidate.
* Join infill lines into fewer paths
Every separate infill path costs a travel to it, so the fill generators now
leave fewer of them:
- connect_infill() pairs the line ends along each wall in the order that
joins the most lines: besides shortest arc first, it replays arcs of about
the same length taken along each contour, forwards and backwards, including
the last pass that joins neighboring open ends, and keeps the order that
joins the most. Regular patterns no longer leave loose ends where the
pairs along a wall fall out of step, and multiline copies print out and back.
- Ends whose anchors would meet are joined along the wall instead, up to two
anchors and a line width apart.
- An unpaired end anchors toward the nearest open end of another path, where
the next path usually starts, instead of the side with more free wall. With
multiline infill, an anchor toward a copy of the same line stops short of
the copy instead of printing over it.
- Last, sparse infill lines whose ends are at most two line spacings apart are
joined with a straight piece, shortest first, without closing a loop or
crossing a line; with anchors off this is the only joining. Copies of
neighboring multiline groups are joined at the same side, and lines that
cannot reverse only join end to start.
* Describe the travel optimization design
How the extrusion chains, the object and island tours and the infill line joining shorten the travel, and why each part is built the way it is.
---------
Co-authored-by: Tobias Gloth <tobias.gloth@3dsystems.com>
Build without precompiled headers when the compiler is clang-cl, in both the slicer and the wxWidgets dependency, and add a CI check that fails if broken exception throw info comes back.
With a precompiled header, clang-cl can leave the copy constructor out of the throw info it emits for an exception type (llvm/llvm-project#53486, open since 2022 and still present in clang 22.1.3). The linker keeps one throw info per type for the whole binary. When it keeps one from a precompiled header file, a copy of that exception is bitwise, and the copy and the original free the same data.
Mesh Boolean hits this on every operation. mcut destroys a std::promise that was never set, which stores a broken_promise future_error in an exception_ptr. Freeing it corrupts the heap, and Windows ends the process with 0xC0000374. The 2.5.0-alpha release has broken throw info for 64 exception classes, including std::runtime_error, std::bad_alloc and boost exceptions.
scripts/check_exception_throw_info.ps1 runs after the Windows clang build and its uploads, before any deploy. It fails when an exception class has throw info both with and without a copy constructor, when OrcaSlicer.pdb is missing, or when it finds no exception classes at all. scripts/test_check_exception_throw_info.ps1 tests it on canned dumps.
build_win.bat now treats -l as --no-pch. The Windows ccache key drops the cl.exe version, which only mattered for precompiled headers.
The bug came in with #15428, which moved Windows CI to clang-cl. 2.4.2 was built with MSVC.
Fixes#16581
Post-processing scripts now run on a copy of each sliced plate's G-code before it is packed into a .gcode.3mf. This covers Export plate sliced file, sending a .3mf to a print host, and the print dialog for non-BBL printers. BBL printers are skipped, because their G-code is post-processed in place after slicing. Backups are skipped too.
Export plate sliced file no longer reports success when export_3mf() fails.
* Make the exclude-object outline tolerance configurable
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
* fix(config): move exclude_object_tolerance to developer mode
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
* fix(config): move exclude_object_tolerance to expert mode
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
* feat(ui): hide exclude_object_tolerance for non-Klipper firmwares
mend
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
* Test that a larger exclude object tolerance writes fewer outline points
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>
---------
Signed-off-by: Sergei Rozhkov <zmod@zmod.link>