Commit Graph
334 Commits
Author SHA1 Message Date
SoftFever 8aa5b1130a Allow nozzle variants to specify different cooling parameters (#16007)
# Description

Include variant-aware validation, temperature and pressure controls,
named nozzle selection, and filament sidebar refresh.

## Support nozzle-specific filament cooling and tuning

Filament presets can require different cooling and tuning for Standard
and High Flow nozzle variants. This change makes part and auxiliary
cooling, pressure advance, temperature limits, and multitool ramming
settings follow the selected variant, with matching UI controls and
profile validation.

The Snapmaker U1 profiles in #15755 / Snorca illustrate why this
matters:

| Profile | Standard cooling | High Flow cooling |
|---|---|---|
| Snapmaker ABS | Part fan: 15–15% | Part fan: 10–60% |
| Snapmaker PETG HF, 0.4 mm | Part fan: 20–40% | Part fan: 30–60% |
| Snapmaker PETG-CF | Part fan: 0–20%; auxiliary: 0% | Part fan: 5–40%;
auxiliary: 20% |
| Snapmaker PLA Matte | Auxiliary: 80% | Auxiliary: 100% |

These differences need to survive variant selection, editing, and
slicing. Shared single values continue to apply across variants, and
short filament arrays fall back to element zero, matching the loader.

### Additional changes

- Preserve named nozzle selections when refreshing the diameter
selector.
- Enable or disable the pressure-advance input for the selected variant.
- Initialize profile-validation slicing with the printer’s declared
nozzle volume type.
- Refresh filament controls after preset loading.

Profile changes remain separate in `u1-hf`.

### Validation

- All 13 focused variant-tool tests passed.
- Broader checks encountered an existing Windows path-separator
assertion failure, reproduced with upstream code.
- C++ build and GUI validation have not been run.

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-10-01 13:53:22 +08:00
SoftFever ba361d9882 Texture displacement (#14662)
## Summary

Adds a new paint-style **Texture Displacement** gizmo that stamps
grayscale
height-map textures onto a model's surface and turns them into real
relief -
engraved or embossed detail - either as a live preview or baked into
actual mesh
geometry.

You paint where a texture applies, stack up to **8 blended texture
layers**
(image-editor semantics: Add / Subtract / Multiply / Divide), choose how
each is
projected onto the surface (Triplanar / Cylindrical / Spherical / LSCM
unwrap /
From-view), and - for the LSCM projection - lay the charts out by hand
in a new
dockable 2D **UV Editor** pane. Coarse models can be **Subdivided** or
**Remeshed** first so there are enough vertices to carry fine detail,
and the
result is committed with **Bake**, restricted to the painted area only.

The tool only ever affects the **painted** region; everything left
unpainted
keeps its original surface, and bake blends the relief seamlessly into
it with no
remeshing or hole-filling at the seam.

---

## User-facing features

- **Paint the affected area** with Brush (circle/sphere), single-Face,
or
Connected-area flood fill; or **Select whole model** - reusing the
existing
`TriangleSelector` / `FacetsAnnotation` painting machinery, one full
mask per
  layer slot.
- **Up to 8 texture layers**, each with its own paint mask, texture, and
  parameters, combined in slot order like image layers.
- **Per-layer controls:** Depth, Tile size, Rotation, Midlevel
(bidirectional
  emboss/engrave), Smoothing, Edge smoothing, Invert, Blend mode, Tile
  (Repeat / Mirrored-repeat / decal), and Projection.
- **Projection methods:** Triplanar (blended, seam-free across sharp
edges),
  Cylindrical, Spherical, **Unwrap (LSCM)** - a real CGAL conformal
  parameterization - and **From view** (slide-projector decals).
- **UV Editor pane** for LSCM layers: move / rotate / scale / snap / cut
/ join
islands, average texel density, Checker and Distortion overlays, manual
mark-
  seam workflow, live model update while dragging.
- **View modes:** Normal (true displaced geometry = what Bake produces),
Fast
(GPU bump-shaded approximation of the active layer), Checker, Distortion
  heatmap, and an independent Wireframe toggle.
- **Mesh prep:** Subdivide (1–5* uniform 1->4 split) with cyan-wireframe
preview,
  and CGAL isotropic **Remesh** to a target edge length.
- **Texture library:** 10 shipped seamless 512×512 grayscale height
maps, plus
import of any PNG/JPG/BMP (converted to an 8-bit grayscale map and
copied into
  the user data dir so app updates can't clobber it).
- **Bake** runs in the background (off the UI thread); the preview is
free to
  explore and only Bake changes the real mesh.

---

## How it works (implementation)

- **Bake is accumulate-then-displace and topology-preserving.** The
output mesh
  has exactly the input's vertices and triangles in the same order; only
displaced vertex positions differ. Each layer is evaluated against the
**base
mesh** and folded per-vertex into a shared accumulator via its blend
mode, then
every touched vertex moves once along its precomputed undisplaced
normal. This
replaced an earlier sequential re-mesh-per-layer design that was the
root cause
of the "second layer never applies" bug and made blend modes impossible.
- **Boundary vertices are pinned.** Any vertex shared with an unpainted
triangle
is never displaced, which is what keeps bakes seamless with zero
remeshing.
- **LSCM unwrap** uses a new `MeshBoolean::cgal::parameterize_lscm()`
built on
CGAL's already-vendored `Surface_mesh_parameterization` package - **no
new
  external dependency**.
- **Fast GPU preview** perturbs the shading normal from the height
gradient
(analytic mm-per-mm slope for triplanar; Mikkelsen's
screen-space-derivative
method for the conformal LSCM path), so apparent depth matches the bake.
- **Background jobs:** preview compute and bake both run off the UI
thread on the
shared job worker (queued, not `replace_job`, so a preview never cancels
an
  in-flight bake); a generation counter discards stale results.
- **UV Editor** shares the app's single real `wxGLContext` and reuses
the
registered `flat`/`flat_texture` shaders; island drags update one affine
matrix
  per island rather than re-uploading geometry.


---


## Backward compatibility & constraints

- **Feature is fully gated behind the new gizmo** - it adds no new
default
behavior and does not touch existing slicing, profiles, or defaults.
Models
  that never open the tool are unaffected.
- **Cross-platform** - pure `libslic3r` / `libslic3r_gui` /
`libslic3r_cgal`
code; no new dependency and no `deps/` rebuild. (Built and tested on
Windows;
  no platform-specific APIs introduced.)
- **`.3mf` compatibility:** **baked** relief round-trips fine, since it
becomes
ordinary mesh geometry via the existing serialization path. **Unbaked**
paint
masks and layer definitions are **not yet serialized** - see
Limitations. No
  existing project data is affected.

---

## Testing

Unit tests in `tests/libslic3r/test_texture_displacement.cpp` - **run
and
passing** (7 cases, 116 assertions). Coverage:

- `decode_height_texture` round-trip
- Empty-layer no-op
- Full-cube uniform displacement
- Boundary-vertex pinning on a hand-built fan mesh
- Regression: a **second layer over the same area actually contributes**
(the
  bug the bake rewrite fixed)
- Table-driven check of all four blend modes
- The lowest painted layer ignoring its blend mode

`BUILD_TESTS` is `OFF` in the checked-in cache; enable to run:

```bash
cmake -S . -B build -DBUILD_TESTS=ON
cmake --build build --config Release --target libslic3r_tests -- -m
./build/tests/libslic3r/Release/libslic3r_tests.exe "[TextureDisplacement]" --order rand
```

---
2026-10-01 12:40:33 +08:00
SoftFever 059e171954 Merge branch 'main' into nozzle-variant-cooling 2026-10-01 11:21:59 +08:00
Kris Austin 865e9963c3 perf: speed up G-code export by 7-26% via typed config apply (#16026) 2026-09-30 18:58:22 -03:00
SoftFever 35a4d941b8 Sync the AMS recommended temperature range with the tray's nozzle variant 2026-10-01 02:54:18 +08:00
SoftFever c03f4925a9 Apply per-variant cooling, ramming and temperature range on nozzle-rack slices
Fan speeds, multi-tool ramming, the tower interface and flush temperature
fallbacks and the custom G-code placeholders now use the extruder variant a
filament prints with on each layer, instead of reading by filament id.
2026-10-01 02:54:18 +08:00
SoftFever e493289b62 Merge branch 'main' into nozzle-variant-cooling 2026-10-01 00:34:32 +08:00
Eric McCannandCodex 2d1e6d5b96 Merge U1 profile updates and require explicit cooling variant values
Remove permissive singleton and short-array validation. Cover cooling array widths, preserve tuning with explicit variant entries, and bump the Snapmaker bundle version.

Co-authored-by: Codex <codex@openai.com>
2026-09-30 07:47:43 -04:00
SoftFever 1e39a36a25 Bake a second painted region as finely as the first
Refinement now tapers off away from the paint, and the unpainted surface is kept out of
simplification, so what one bake leaves unpainted is still the model's own triangles when a later
bake paints there. A second bake used to refine the slivers and long triangles the first one left
around its stroke, and came out many times denser, with walls off the texture's lines.
2026-09-30 19:32:01 +08:00
SoftFever 36ebe0cde5 Let Cancel stop a texture bake while it simplifies
Once the budget is met the progress fraction stops moving, and a cancel was only checked when it
moved, so Cancel did nothing until the flat-face merging finished. It is now also checked every 16k
steps.
2026-09-30 19:31:54 +08:00
SoftFever 7c0a3ab916 Warn about the texture bake budget only when it cost detail
Meeting the triangle budget by merging flat faces alone no longer raises the warning, and the
warning now quotes the budget instead of the triangle count left after the flat faces were merged.
2026-09-30 19:31:48 +08:00
HanifKoh 94266c2819 Fill Settings Missing From a CLI Project From Its System Presets (#15953)
* Fill Settings Missing From a CLI Project From Its System Presets

A project saved before a printer or process option existed has no value
for it. The GUI takes such keys from the project's system preset; the
CLI left them at the option default, so e.g. extruder_clearance_dist_to_rod
sliced as 40 instead of the P1S's 33.

The CLI now resolves the project's system printer and process presets by
name and copies the keys the project lacks, skipping preset bookkeeping,
print-host keys, the extruder variant layout and keys the legacy handler
drops. PresetBundle::resolve_system_preset finds the vendor through its
manifest or preset cache, so it also works in release builds, which ship
vendors as caches only.

* Load a CLI Project's Printer and Process Settings as the GUI Does

The CLI filled only the keys a project lacked from its system preset.
The GUI builds a project preset differently: the project's values go
over the default preset, without the print-host keys, and every key
the project does not list in different_settings_to_system is refreshed
to its base system preset's current value. After a profile update the
two sliced the same project differently.

That step now lives in Preset::load_external_config, which takes plain
configs and gets the base preset from a callback, so the collection
lookup stays in PresetCollection. PresetBundle::project_different_keys
builds the kept-key set from a project's escaped entry, adding the
preset bookkeeping keys, and is used by both the GUI and the CLI.
PresetCollection::load_external_preset calls the shared step with no
change in behaviour.

The CLI now builds the project's printer and process configs with the
same step, passing the system preset from resolve_system_preset. That
drops the hand-kept skip list for print-host and variant-layout keys
and the legacy-key check: the shared step already excludes print-host
keys and maps per-variant values onto the base preset's variant layout.
The --uptodate path and a printer or process given on the command line
are left as they were.

Because the GUI's kept-key set always holds the bookkeeping keys, the
refresh runs for every project that names a system preset, so the CLI
now loads that preset's vendor on every such run.
2026-09-30 18:14:28 +08:00
SoftFever 1e4489eb16 Merge branch 'main' into feature/texture_displacement 2026-09-30 15:28:08 +08:00
Eric McCannandCodex d58c3b0d89 Keep downstream customization limited to printer profiles
Extract non-profile changes into a separate development line while preserving all profile content.

Co-authored-by: Codex <codex@openai.com>
2026-09-29 22:42:50 -04:00
Eric McCannandCodex 0eb6e6814e Preserve nozzle variant tuning and cooling controls
Include variant-aware validation, temperature and pressure controls, named nozzle selection, and filament sidebar refresh.

Co-authored-by: Codex <codex@openai.com>
2026-09-29 22:42:30 -04:00
Eric McCannandCodex 9590d71fd9 Merge upstream updates and reconcile nozzle variant controls
Co-authored-by: Codex <codex@openai.com>
2026-09-29 22:42:05 -04:00
Kris AustinandRodrigo Faselli f5679ad343 perf: load presets in parallel, cutting preset load time by over 60% (#15943)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-29 18:13:44 -03:00
HanifKoh 2769b12ce7 Give OBJ Quad and Flipped Faces the Texture Coordinates of Their Own Corners (#15977)
load_obj emits the second triangle of a quad from corners 0, 2 and 3,
but read its texture coordinates from corners 0, 1 and 2, so half of
every textured quad sampled the wrong part of the texture. The corner
indices are now passed down to where the coordinates are read.

A mesh with inward-facing triangles is flipped after loading, which
swaps corners 1 and 2 of every face. The texture coordinates were left
as they were. They are now swapped along with the corners.
2026-09-30 03:13:29 +08:00
HanifKoh 203bc63f35 Escape Project Metadata in the Project Page and Restrict Accessory Opening (#15956)
* Escape Project Metadata in the Project Page and Restrict Accessory Opening

The Project page rendered the model and profile name, author, description
and accessory file names from the 3MF as live HTML. Names, authors and file
names are now set as text, and the file list is built from DOM nodes with
bound click handlers instead of concatenated markup. Descriptions can
legitimately carry rich-text HTML, so they are rebuilt from an inert
DOMParser document, keeping only plain formatting tags, http(s) links and
http(s) images, with every other attribute dropped.

Opening an accessory from the page now only launches regular files that
lie inside the project's extracted auxiliary directory. The containment
check is a new libslic3r helper, is_absolute_path_within_root, built on
is_path_within_root so symlinks leading out of the root are rejected too.

* Tighten Project Page Description Rendering and Keep More Formatting

Link and image URLs in descriptions must now start with an http or https
scheme as written and parse as such with the URL parser. Preview images are
built as DOM nodes like the file list, and accessory names show their full
text as a tooltip.

Descriptions keep more plain formatting: del, ins, figure, figcaption, dl,
dt, dd, caption, q, abbr, kbd and wbr, plus alt, title, width and height on
images, colspan and rowspan on table cells and start on ordered lists.
Numeric attributes must be plain integers. Embedded YouTube players become
a link to the video.

* Confirm Before Opening Program Attachments and Load Only HTTPS Images

Opening a project attachment whose type runs as a program or script
(executables, installers, shortcuts, shell and PowerShell scripts, macOS
command files and apps, Linux desktop entries) now asks for confirmation
first. The check lives in libslic3r as is_executable_file_name and ignores
the trailing dots and spaces Windows strips from file names.

Images in project descriptions are kept only when they load over https,
so opening the Project tab no longer issues plain-http requests.

* Open Project Attachments Through One Guarded Helper

The Edit Project Info view launched attachments directly, without the
checks the project page has. Both now call
desktop_open_project_attachment, which checks that the file is inside
the auxiliary directory, asks for confirmation where needed and then
opens it.

The auxiliary root was built through encode_path, which returns code
page bytes on Windows, while boost::filesystem reads a narrow string as
UTF-8. With a non-ASCII temporary directory the root never matched and
no attachment opened. It is now built from the UTF-8 path directly.

The list of program extensions could not be kept complete and let
unknown types open without a prompt. It is replaced by
is_safe_to_open_file_name, a list of plain document, image, model and
video types that open directly. Everything else asks first.
2026-09-30 00:39:30 +08:00
HanifKoh ba468c842d Confine Updater and Plugin Archive Extraction to the Target Directory (#15957)
* Confine Updater Archive Extraction to the Target Directory

The preset updater extracted downloaded archives by appending each entry
name to the cache directory, and the network plugin installer did the same
for the plugin folder, without checking that the result stays inside it.

Move the updater's extraction into libslic3r as extract_archive_confined,
which validates every entry with is_path_within_root before writing
anything and fails the whole archive if one entry resolves outside the
target. The plugin installer now rejects such an entry the same way. Well
formed archives extract exactly as before.

* Harden Archive Extraction Against Symlinks

The plugin installer now creates a symlink entry only when its target is
relative and, joined to the link's own directory, passes
is_path_within_root, via the new is_symlink_target_within_root helper.
Before writing any entry it checks the destination with symlink_status, so
an existing symlink, dangling or not, is replaced rather than followed, and
it creates parent directories inside the existing error handling.
extract_archive_confined replaces a symlink at a destination file the same
way.

is_path_within_root now ignores a trailing separator on the root, which
previously made every path fail the check.

* Validate Plugin Symlink Targets Before Replacing Existing Files

A symlink entry's target is now read and checked before anything already
at its destination is removed or renamed aside, so an archive rejected
for its link target leaves the installed plugin files in place.

* Reject Paths with an Embedded NUL When Confining Extraction

is_path_within_root compared each component with "..", so a name such
as "..\0" passed the check. The filesystem calls stop at the NUL and
act on a shorter path than the one that was checked: a symlink target
read from a plugin archive as raw bytes was created as "..", pointing
out of the plugin directory.

A path containing a NUL is now rejected before anything touches the
filesystem, which covers every caller, including entry names taken from
the Unicode Path extra field.
2026-09-29 23:40:12 +08:00
SoftFever 1504bd7153 Fix command-line slicing when a filament leaves out a per-variant setting 2026-09-29 21:09:20 +08:00
Eric McCannandCodex 7cbea5f454 Consolidate U1 nozzle variants while preserving filament tuning
Support variant-specific cooling, pressure advance, purge, ramming and temperature ranges. Keep shared U1 values as singletons and verify element-zero fallback for missing variant entries. Preserve preset migration aliases and validate high-flow selections using their declared nozzle volumes.

Co-authored-by: Codex <codex@openai.com>
2026-09-29 08:24:37 -04:00
SoftFever ef0c656932 Tune pressure advance separately for each extruder variant
Pressure advance, adaptive pressure advance and its model can now take a
different value for each extruder variant of a filament, such as Standard and
High Flow nozzles, like the other per-variant filament settings. Projects
saved with one value per filament apply it to every variant of that filament,
and the addnorth BBL filaments in the Orca Filament Library are updated to the
per-variant layout.
2026-09-29 17:52:13 +08:00
ExPikaPaka 6f62fe374c Move the slicing optimizations to their own branch
They stand on their own: nothing in the texture displacement feature
calls them, and nothing in them knows about textures. Reviewing a
slicer-wide parallelization next to a new gizmo helped neither.

They now live in perf/slicing-optimizations, based on current main.
2026-09-29 10:38:48 +02:00
ExPikaPaka 75b6175988 Merge remote-tracking branch 'origin/main' into feature/texture_displacement 2026-09-29 08:36:11 +02:00
HanifKoh 8ffd3e514e Harden OBJ and DRC Import Against Malformed Files (#15948)
* Validate OBJ Texture-Coordinate Indices

load_obj read the texture coordinates of a face without checking the
vt index, so a face referencing a vt past the end of the list read out
of bounds and crashed, and a face vertex with no vt read index -1.
Out-of-range or missing indices now fall back to a zero UV. The face
keeps its entry in the per-face UV list, so the following faces stay
aligned, and the geometry loads as before.

Negative (relative) vt indices were also rebased by dividing the float
count by 3, but each vt stores two floats.

* Reject DRC Meshes Without Positions or with Invalid Face Indices

load_drc dereferenced the POSITION attribute without checking that the
mesh has one, and trusted the decoded face indices, which the Draco
decoder does not check against the point count. Both now fail the load
cleanly. A failed vertex conversion is treated the same way.

The libslic3r tests link Draco so they can encode the malformed meshes
in-test.

* Keep OBJ Texture Coordinates That Carry a W Component

The vt parser stopped reading the optional third component when texture
coordinates were cut down to u and v, but the check that nothing is left
on the line stayed. A legal "vt u v w" line was therefore rejected and
silently dropped, shifting every later texture index. The w component is
parsed again and discarded.

The texture coordinate stride is now a named constant, OBJ_TEXCOORD_LENGTH,
used by the parser and the importer, so the relative-index rebase cannot
drift from the storage layout again.
2026-09-29 12:25:16 +08:00
SoftFever 9688f0ae62 Slice-validate every custom G-code and filename_format in system profiles (#15966)
# Description

Follow-up to #15950, where a `filename_format` using
`initial_no_support_extruder` shipped broken because the profile
validator's slice sweep never expands `filename_format`. The sweep now
expands every custom G-code and `filename_format` text shipped in any
system profile. Each printer's slice also fires the pause, template
custom G-code and clumping-detection hooks and names the output file,
and the first printer shipping a `printing_by_object_gcode` also slices
by object. Beyond each printer's default process and filament, every
compatible system process and filament carrying a template text no
earlier slice has expanded is sliced once, which takes the sweep from
1,110 to 1,248 slices (about 49 s locally, up from 43 s). While the
sweep runs, the placeholder parser also resolves variable names inside
`{if}` branches a slice does not take, so one expansion checks every
branch.

The stricter sweep found two profile bugs, fixed here: the Anycubic
Kobra X filament change G-code carried an unreachable block reading
variables only Anycubic's own slicer defines, and the Wanhao France D12
template custom G-code had an unterminated `{if}`, so adding a template
custom G-code on those printers failed the slice. It also fixes a parser
bug where a declaration such as `{local a = layer_height + 1}` failed to
parse inside a branch that is not taken.

No change to slicing output for templates that already worked: the
untaken-branch check is enabled only by the validator's slice mode, and
the parser fix only lets previously rejected templates parse.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

New placeholder-parser cases cover the parse fix and the untaken-branch
check: off by default it changes nothing; on, it rejects undefined names
in branches not taken, accepts names declared there, and leaves boolean
expressions (compatibility conditions) alone. The full sweep passes on
all system profiles, and planting an undefined variable in an untaken
branch of a printer's start G-code, of a non-default filament's start
G-code or of a non-default process's `filename_format`, or reverting
#15950, each fails it and names the preset. `scripts/check_profile.sh`,
`libslic3r_tests` and `fff_print_tests` pass.

<!--
> 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)
2026-09-29 10:39:17 +08:00
HanifKoh d35ea27ea5 Validate Zip Entry Sizes Before Parsing 3MF XML (#15958)
The 3MF importers read XML entries into a single expat buffer whose size is
an int, while the archive extraction used the entry's 64-bit declared size.
The two could disagree for entries declaring more than INT_MAX bytes.

Reject such entries before allocating, and use one size for the buffer, the
extraction and the parse. This applies to the BBS importer, the PrusaSlicer
importer and the PrusaSlicer fingerprint probe. The load now fails with an
error instead.
2026-09-29 02:32:50 +08:00
HanifKoh 490d134507 Harden 3MF Loading Against Malformed Plate IDs and Paint Data (#15959)
* Reject 3MF Plate IDs Below 1 Instead of Indexing Before the Plate List

The plate importer copied each plater_id from model_settings.config into the
1-based plate list after checking only the upper bound, so plater_id="0"
wrote to plate_data_list[-1] and crashed on load. Both copy sites now reject
ids below 1 with the same "invalid plate index" error already used for ids
past the end.

* Drop Malformed 3MF Paint Data Instead of Reading Past the Bitstream

Painted facets are decoded from a bitstream a nibble at a time with no bound
check, so a truncated or corrupt paint string in a 3MF (for example split
codes with no children behind them) read past the end and crashed on load and
slice. A one- or two-side split naming side 3 also indexed past the triangle's
vertices.

Every nibble read now goes through a bounds-checked reader. Loading validates
each triangle's tree and drops a malformed one with a warning, so the stored
data, used extruder states and later decoding all agree. deserialize() also
unwinds and clears any triangle whose tree is incomplete or malformed, and
has_facets() stops at a truncated triangle. Valid streams decode unchanged.
2026-09-29 02:31:26 +08:00
HanifKoh 41eeaf3883 Sanitize Server-Supplied Download File Names (#15955)
* Sanitize Server-Supplied Download File Names

The URL downloader used the file name from the Content-Disposition header
as given, without the cleaning and unused-name search applied to the
URL-derived name.

Reduce the header name to a sanitized base name with the new
sanitize_file_basename helper, which splits on both path separators and
rejects names made only of dots and spaces. Run the result through the
same unused-name search as the URL-derived name, now shared in
find_unused_filename, and fall back to the URL-derived name when nothing
usable remains.

* Sanitize Download Names Before Choosing an Unused One

The unused-name search probed the name as given and sanitized the
result afterwards, so a name whose special characters are replaced
could be mapped onto a file that already exists.

Move the search into libslic3r as find_unused_filename, sanitize first
and probe the name that is actually written. The download marker path
is shared through download_marker_path. Restore the last tried name in
the error reported when no free name is found, and cover the search
with unit tests.

* Keep Downloads on an Unused Name Until They Complete

When the server supplied the name, the download marker stayed under the
URL-derived name, so the adopted name was not reserved against other
downloads. The final rename also replaced any file that took the name
while the download ran.

Move the marker to the adopted name before any data is written, and
check the name again right before the final rename, picking the next
free name if it is taken by then.

* Sanitize the File Name of Model Import Links

The model import took the file name from the link as given and only
avoided an existing file with a substring match on the folder listing.

Reduce the name to a sanitized base name, falling back to untitled.3mf,
choose the name with the shared unused-name search, and check it again
before the final rename.

* Handle Filesystem Errors When Finishing a Model Import Download

Choosing the final name and moving the downloaded project into place
could throw from inside the download callback. Any such error now removes
the temporary file and reports the existing import failure message.
2026-09-29 02:19:24 +08:00
SoftFever 9f34f37c27 Merge branch 'main' into feature/slice-sweep-full-placeholder-coverage 2026-09-29 01:57:28 +08:00
SoftFever 9d8257d2eb Support extruder variants on every printer (#15964)
# Description

Extruder variants (Standard, High Flow, extra high flow) now work on any
printer. Any vendor profile can declare them, and a multi-variant
filament picks up the right variant on every printer. In the sidebar,
users can switch the printer variant and set the nozzle volume type of
each extruder on multi-extruder printers. This also fixes a later
filament printing at the first filament's temperature on a P1S or X1C
with a High Flow nozzle. The profile checks now reject variant arrays of
the wrong size and outdated variant strings. Every shipped vendor
profile passes them, and the orca-profiles skill documents the rules.

Slicing output changes only where the wrong variant was used before. 
# Screenshots/Recordings/Graphs
<img width="393" height="218" alt="Screenshot 2026-09-28 at 11 28 07 PM"
src="https://github.com/user-attachments/assets/8bee4b0e-c38c-4039-8c11-096ace6fbad0"
/>
<img width="448" height="267" alt="Screenshot 2026-09-28 at 11 28 37 PM"
src="https://github.com/user-attachments/assets/e767cd97-a5d0-4728-b010-c8ea2bc94ca7"
/>
<img width="746" height="603" alt="Screenshot 2026-09-28 at 11 28 54 PM"
src="https://github.com/user-attachments/assets/23c8af3b-c679-46e5-9fd0-2e43df0449b3"
/>




https://github.com/user-attachments/assets/10d75d72-86a3-42e8-8a95-b1627bd58e91



## 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)
2026-09-29 01:57:01 +08:00
SoftFever 185cfe4323 Slice-validate every custom G-code and filename_format in system profiles 2026-09-29 01:53:30 +08:00
1846407e93 Add Precise Seam placement feature (#12974)
Co-authored-by: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com>
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-09-28 14:40:55 -03:00
SoftFever 7a2daaa351 Make all vendor profiles pass the updated profile checks 2026-09-28 23:02:04 +08:00
SoftFever 71bb400e46 Add multi-variant support to the app 2026-09-28 19:08:35 +08:00
Ian Bassi ea280ba6f6 Wipe tower sparse layers combination (#15841) 2026-09-26 16:27:15 -03:00
SoftFever 2b602943a5 Load profile includes so synced Bambu Lab presets get their G-code back 2026-09-24 13:22:37 +08:00
ExPikaPaka 4504f315ee Include what the new code uses
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.
2026-09-23 16:16:46 +02:00
ExPikaPaka 944f01c68a Merge branch 'feature/texture_displacement' of https://github.com/OrcaSlicer/OrcaSlicer into feature/texture_displacement 2026-09-23 14:08:47 +02:00
ExPikaPaka 9327e7770b Rename the fast preview to the shaded preview
Its shader files, the name they are registered under, its members and
the panel text now match what it does: shading only, no geometry.
2026-09-23 09:51:08 +02:00
ExPikaPaka 6e88ad7f52 Visit seam candidates as the search finds them
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.
2026-09-23 02:04:49 +02:00
ExPikaPaka 5baefd8a4e Tile the booleans on layers of many pieces
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.
2026-09-22 18:06:54 +02:00
SoftFever 211dd7daaa Merge branch 'main' into feature/texture_displacement 2026-09-22 14:46:36 +08:00
ExPikaPaka 7aef3d1215 Merge remote-tracking branch 'origin/feature/texture_displacement' into feature/texture_displacement
# Conflicts:
#	src/libslic3r/TextureDisplacement.cpp
2026-09-21 09:00:18 +02:00
ExPikaPaka cef6527f9d Texture displacement: per-layer bake, UV pane redesign, unwrap and layer view fixes
- Bake: each layer is sampled only on its own painted area; analytic
  projections used to stack every layer over every painted region, so the
  top layer's texture showed on all of them (colour sampler too)
- Auto resolution follows the texture's texel size and sharpness again
- Unwrap: charts cut by each face's own normal (a cube gives 6 islands, not
  12 triangles); non-disk charts (tubes, closed shells) are split until they
  flatten; connected nets test real triangle overlap, grow from the largest
  chart and are packed side by side
- UV edits are stored per unwrapped copy, so dragging a seam vertex no
  longer moves its copies in neighbouring islands
- UV pane: tool strip with unwrap settings moved in from the panel, sharp
  HiDPI icons, clearer island/edge/selection drawing with hover, texture
  picker from the thumbnail, texture no longer lost on reopen (GL state
  from the 3D view, background upload retries)
- Panel: whole-model select/erase as icons in the tools row; inactive
  layers' paint shown muted; colour textures shown in colour in the picker
- Built-in displacement texture library
- Tests for unwrap segmentation, connected nets, UV edits and per-layer
  sampling
2026-09-21 08:59:26 +02:00
SoftFever c168d0c8db Stop estimating a prime tower for a single used filament (#15760)
# Description

A plate using one filament no longer reserves or draws a prime tower in
the plater just because multiple filament slots are configured. The
shared footprint estimate gated "no tower" on the purge volume being
zero, but on non-Bambu printers with the shipped defaults that volume
comes from the SEMM flush matrix, which reads every configured slot and
stays nonzero even when only one filament is used — so the preview
showed a tower the print could never contain. The estimate now decides
from the filament count alone, with wrapping detection and smooth
timelapse staying the only reasons a single-filament plate keeps a
tower, matching `Print::has_wipe_tower()`. The gate also moved above the
flush-volume scan so the lone-filament path no longer pays for it.

No change to slicing output — toolpath generation already omitted the
tower for a single used filament; this removes the phantom preview,
placement reservation, and validation footprint.

# Screenshots/Recordings/Graphs
Before the fix:


https://github.com/user-attachments/assets/c5b0ea61-127d-408e-bafe-f1f76c58b996

After the fix:


https://github.com/user-attachments/assets/ac6632b3-5d44-4013-aca3-73f6cfd1139c



## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

Added a shipped-defaults single-filament case to
`tests/libslic3r/test_wipe_tower_estimate.cpp`: it estimates a 5.04
mm-deep tower before the fix and zero depth after. `[WipeTowerEstimate]`
and the fff_print `[WipeTower]` suites pass.

<!--
> 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)
2026-09-19 12:46:00 +08:00
Kris Austin 49c811ceb6 fix: H2D extruder sync does nothing on the Motion ability page (#14931) 2026-09-18 20:54:43 -03:00
SoftFever 60ebbf7daa Stop estimating a prime tower for a single used filament
The no-tower case was gated on there being no purge volume, but the SEMM flush matrix reads every configured slot and is nonzero even when only one filament is used, so the plater preview drew a tower the print would never contain.
2026-09-19 00:38:22 +08:00
SoftFever 533b68ed9f Fix colour textures loading upside down and crashes on damaged PNGs
Colour height maps now displace the same way up as grayscale ones. A
truncated or corrupt PNG in the texture folder or a project file now
loads as an empty texture instead of aborting the app, and a 16-bit
colour PNG is converted on load rather than displacing as noise.
2026-09-18 20:37:35 +08:00