Commit Graph
850 Commits
Author SHA1 Message Date
Hanif Koh 8e330f951a Merge Main into Belt Printer
Merge origin/main (00429da739) into belt-printer.

Conflicts resolved:
- src/CMakeLists.txt: keep both wxInspector workarounds.
- GCodeProcessor.cpp: keep the belt compare_pos / z_for_height lines.
- PrintObjectSlice.cpp: the belt bbox-Z guard also covers main's
  printable_region_ids bookkeeping.
- TreeSupport.cpp: the belt-floor check runs before main's PendingNode
  queueing.
- Tab.hpp: keep the belt fields, drop the removed upload description
  fields.
- tests/libslic3r/CMakeLists.txt: keep both test files.

Also included:
- eSUN PLA belt presets declare their own filament_id (OFkrxQC4) and
  scripts/filament_id_snapshot.json is regenerated, as main's filament_id
  check requires.
- Custom.json version bumped to 02.04.00.05 so the belt entries reach
  existing installs.
- Fix the ambiguous WithinRel call in the belt apron width test, which
  otherwise breaks the fff_print build.
2026-09-14 16:33:08 +08:00
HanifKoh 31f6eb2718 Keep the First Value When a Per-Filament Variant Option Is Too Short (#15639)
update_values_to_printer_extruders_for_multiple_filaments picks each
filament's value from the flattened (filament x variant) columns of every
per-filament variant option. When a column index fell past the end of the
option's values, it skipped that filament and left the zero the output
vector was created with.

The GUI always hands this function full columns, but the CLI does not:

- a CLI override of a single value, such as --nozzle-temperature=211 on a
  four-filament project, came out as 211,0,0,0, so three filaments would
  print at 0 C;
- loading fewer filament presets than the project has filaments left the
  remaining filaments' columns missing, so filament_cooling_before_tower
  came out as 10,10,0,0 and filament_ramming_volumetric_speed as -1,-1,0,0.

An out-of-range column now keeps the option's first value, the fallback
get_at() and the sibling gather step already use. The seven per-type copies
of the loop are replaced by that same gather_option_values helper, moved
above the function; it now takes its caller's name for its log lines. An
empty option, which has no first value, is given one registered default per
filament first; it used to be replaced with zeros.

On a partial load a filament whose preset was not loaded takes the first
filament's value rather than its own preset's, which the CLI does not load;
for the options seen in practice those agree.
2026-09-14 14:26:32 +08:00
Kris Austin 7888452666 build: clear 7 warning categories across 26 sites (#15615)
* build: clear 2 warnings - cast the NSTextField the class check already proved

mainframe_text_field is NSTextField* and was assigned a bare NSView*, which
Clang reports as -Wincompatible-pointer-types. Both assignments sit inside
if ([viewObject class] == [NSTextField self]), so the runtime type is already
guaranteed, and the line above the second one casts the same variable the same
way to call setTextColor. macOS only, since nothing else compiles this file.

* build: clear 6 warning categories from the clang-cl inventory

-Wmissing-braces (9). Aggregates whose first member is itself an aggregate.
GUID's fourth member is BYTE[8], so the trailing eight bytes take their own
braces. The others were reaching for zero-initialization with {0} and say {}
now. bbs_3mf's backup Task ends in an anonymous union, which needs braces of
its own; those braces initialize the union's first member rather than the one
named at the call site, so the RemoveBackup site says so in a comment.

-Wmacro-redefined (11). SendMultiMachinePage.hpp defines five names that
Preferences.hpp, PresetBundleDialog.hpp, ExportPresetBundleDialog.hpp and
TroubleshootDialog.hpp also define with different values, so the value in
force depended on include order. All nine of this file's DESIGN_ macros take
the SEND_ prefix it already uses for its own macros, values unchanged, so a
DESIGN_ name added elsewhere later cannot collide with it again. They read as
one page-local palette, a 900 to 400 gray ramp plus sizes, so the four with
no current readers stay: dropping them would leave gaps in a named scale. test_marchingsquares.cpp defines NOMINMAX,
which libslic3r already passes as a PUBLIC compile definition, so it takes
the #ifndef guard the other suites use.

-Wbraced-scalar-init (3). Two PushStyleVar calls resolve to the float
overload, so the braces were initializing a scalar. ConfigOptionFloatsNullable
already takes an initializer_list, so the inner braces did the same thing.

-Wmicrosoft-goto (2). Both gotos in copy_file_gui jump forward over the
initialization of size, dwRead and dwWrite, which only MSVC accepts. Those
declarations move up to join the others at the top of the function.

-Wunused-private-field (3). Every use of ColourPicker's m_clrData and
m_picker_widget is behind !defined(__linux__), so on Linux they are written
and never read; the members now carry the same guard. ParamsPanel's
m_size_move is read nowhere. Tab has its own, which is the one Tab.cpp uses.

-Wnonportable-include-path (2). BaseException.h asked for "stackwalker.h"
and the file on disk is StackWalker.h.
2026-09-10 07:39:14 -03:00
Rodrigo FaselliandIan Bassi 85dc866425 Spiral Inset infill (spiral-concentric infill) (#15085)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-09-06 11:36:23 -03:00
HanifKoh df30e22427 [CLI]: CLI Crash Guards (#15477)
# Description

<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->

Part 1 of 3 of the CLI-mode bug sweep, split out of #15452 per review
feedback there. This PR contains the crash fixes.

## Fixes

- **`--outputdir`/`--datadir` with a missing parent directory aborted**
via unguarded `create_directory`. Directories are now created
recursively, with a graceful early exit and a specific error message if
creation fails.
- **`--slice` + `--export-3mf` segfaulted on a from-scratch slice**:
`ConfigOptionVector::get_at()` on an empty vector is `.front()` of an
empty vector (UB). Guards added for `filament_color`/`filament_id` at
the CLI call site, and inside `DynamicPrintConfig::get_filament_type`
for `filament_type`/`filament_is_support`/`filament_id`. Only *empty*
vectors are treated as missing — the existing clamp-to-front behavior
for merely out-of-range indices is preserved, so GUI callers are
unaffected.
- **OOB heap write from stale `filament_self_index` on
`--load-filaments`** (fixes #14181): a 3MF carrying more
`filament_self_index` entries than loaded filaments wrote past the end
of `old_variant_counts`. The guard validates both bounds — entries `>
filament_count` *and* non-positive entries (`< 1`), since a single `0`
in an otherwise-valid array indexes `old_variant_counts[-1]`.
- **Wrong printable-area check** for non-rectangular beds: use the
printable area's bounding box instead of a naive vertex calculation
(fixes #15363).
- **`nozzle_height` and `align_center` were not read into the arrange
config** in CLI mode.

# 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.
-->
- Repro'd each crash on CLI before the fix; all resolved after.
- `tests/libslic3r` suite passes; full binary builds clean on Linux.
- Added `get_filament_type` unit tests 

<!--
> 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-04 15:46:03 +08:00
HanifKoh 57ce18d70d [CLI]: CLI Argument Parsing Fixes (#15478)
* Reject invalid CLI argument values instead of silently accepting them

* Add read_cli accept/reject tests

* Update Option Type for LogFile argument

* Add read_cli vector option tests

* Accept common bool spellings on the CLI, cover --logfile in tests

* Add unit tests for truthy bool parsing
2026-09-04 11:31:42 +08:00
Alexandre Folle de Menezes 640fd9f454 Fix misc. errors on GUI strings (#15468)
* Fix misc. errors on GUI strings

* Adding context to single-letter unit strings

* Fix duplicate strings on po files
2026-09-02 09:36:54 -03:00
CliffordandClaude Opus 5 b6ef6cf1be fix: out-of-bounds write migrating per-variant values when switching printers (#15456)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-01 19:29:11 -03:00
harrierpigeon e341a9b84e Merge remote-tracking branch 'upstream/main' into haryr/aug25-rebase
# Conflicts:
#	src/libslic3r/GCode/ToolOrdering.cpp
#	src/libslic3r/Print.cpp
#	src/libslic3r/PrintApply.cpp
#	src/libslic3r/PrintConfig.cpp
#	src/slic3r/GUI/Tab.cpp
2026-08-30 23:30:51 -05:00
harrierpigeon d289478618 Merge remote-tracking branch 'upstream/main' into haryr/aug25-rebase
# Conflicts:
#	resources/profiles/Custom.json
#	src/libslic3r/Brim.cpp
#	src/libslic3r/GCode.cpp
#	src/libslic3r/GCode.hpp
#	src/libslic3r/Preset.cpp
#	src/slic3r/GUI/3DScene.cpp
#	src/slic3r/GUI/ConfigManipulation.cpp
#	src/slic3r/GUI/GLCanvas3D.cpp
#	src/slic3r/GUI/Plater.cpp
2026-08-25 06:50:46 -05:00
SoftFever fcdfcae427 Port mixed filament engine fixes from BambuStudio 2026-08-23 22:11:49 +08:00
SoftFever 9d733e50f9 Fix prime tower and by-object brim with mixed filaments 2026-08-23 22:11:48 +08:00
Ian Bassi 8fea099d99 BBL Port Color Mix Base 2026-08-23 22:11:48 +08:00
Ian Bassi 322dc9b6a6 Smooth more patterns (#15205) 2026-08-18 12:19:27 -03:00
Kiss Lorand fd23b74b99 Fix single-value object overrides on multi-nozzle printers (#15221) 2026-08-13 17:09:10 -03:00
harrierpigeon bcfb09481c pull purge tower into its own files, make purge tower semi-transparent like other purge towers 2026-08-08 16:35:55 -05:00
harrierpigeon 88726d76e8 Purge tower part 1 2026-08-08 16:35:54 -05:00
harrierpigeon 8b2e28817d fix non 45 degree slicing methods after regression created while cleaning up UI 2026-08-08 12:33:41 -05:00
harrierpigeon 55b4dca9bc Belt printers: brim laid onto the tilted belt, with a leading apron
A belt printer slices in a rotated frame, so the belt surface is a tilted
plane rather than the Z=0 bed plane.  Each slicing layer touches the belt
only along a narrow strip at its leading edge - about 0.2mm at 45 degrees -
so a part's first layer is really a first line, with almost no contact patch
to hold it down while the belt drags it forward.  Brim was hard-disabled on
belt printers, leaving no remedy at all.

Generate the brim on the belt plane instead.  The object's belt footprint is
the union over layers of each slice clipped to that layer's contact band; the
brim is offset from it in a "flattened" frame where the shear axis is
stretched by 1/cos(tilt), so ordinary Clipper offsets measure true on-belt
distance.  It is emitted as cross-belt lines, one per layer band, anchored to
a fixed fraction of the band so every line shares a nozzle-to-belt clearance
and therefore comes out the same width; flow is matched to the resulting band
pitch, keeping the sheet uniform and gap-free.

Three new controls, all belt-only:

  * Leading brim length - extends the brim ahead of the part along the belt,
    on every downhill-facing edge of its contact area.  This apron necessarily
    prints BELOW the object's first layer, since layer 0 is the part's leading
    contact, so it needs brim-only bands of its own.
  * Extra brim width - widens the brim sideways across the belt only.
  * Brim type "Leading edge only" - brim at the part's first belt contact and
    nothing after it.  Appended last in BrimType so no existing value shifts;
    degrades to an outer brim off belt printers, with a warning.

The apron bands are lightweight records rather than a Layer subclass, so no
fabricated Layer::id() can leak into initial-layer temperature selection, the
spiral vase probe, cooling or gradual interpolation.  They are generated in
posSupportMaterial because their print_z values must exist before ToolOrdering
is built at psWipeTower, and they are emitted from a short dedicated branch in
process_layer that runs before any layer pointer is dereferenced.

The footprint is closed before offsetting outwards: a belt contact patch is
often a broken-up strip, and the merged offset rings of two islands closer
than 2 x brim_width would otherwise fill the space between them - space that
lies under the part.

Also fixes a pre-existing bug where PrintObject::get_first_layer_bbox()
overwrote a valid bbox with an unassigned one on any belt printer with a brim
configured, because has_brim() was true while make_brim() returned early.

Belt brim is refused alongside the prime tower and spiral vase, and requires
one instance per PrintObject - translating an instance along the belt axis
changes its physical belt-floor Z.  Untilted belt printers are unchanged: they
still get no brim, since the plate brim is emitted out of skirt_brim_groups(),
which _make_skirt() never builds for a belt printer.
2026-08-06 01:08:44 -05:00
SoftFever 408db4b3b0 Wait for the toolchange temperature on the wipe tower
Adds a printer option that picks up the new tool without a blocking temperature
wait, travels to the wipe tower, and waits there right before purging, parked
beside the tower so the ooze from the heat-up lands next to it rather than on the
model. The incoming filament's target is raised ahead of the tool change, so the
heat-up overlaps both the change itself and the travel to the tower.

Off by default, and only offered for multi-extruder printers using a Type 2 wipe
tower; the generic toolchanger profile enables it.
2026-08-06 12:24:00 +08:00
SoftFever 6312caaf13 Add filament_retract_length_toolchange/filament_retract_restart_extra_toolchange config and update tool changer printer's profiles (#15039)
* update snapmaker profiles. largely ported for Snapmaker Orca fork

* update prime volume

* set precise_outer_wall to 1

* Update per-material multi-tool ramming to the filament library

* Add per-filament overrides for toolchange retraction

* Set toolchange retraction per filament for Snapmaker U1

* set default support type to tree

* format snapmaker profiles
2026-08-05 00:13:27 +08:00
Mikhail f. Shiryaev 7b404596e9 Add Skip G-code config block to exclude the config comments from G-code files (#12455)
Add feature to skip CONFIG_BLOCK in G-code files
2026-08-03 15:10:01 -03:00
harrierpigeon f563df04f6 belt: default first_layer_plane to Auto, not BeltAffine
BeltAffine activates the FirstLayerPlane evaluator unconditionally, so on a
non-belt printer on_first_layer(point) stopped agreeing with the legacy
slicing-layer-0 test. Every per-path first-layer call site in _extrude then
took the non-first-layer branch, and first-layer speeds were skipped: brim
came out at the volumetric fallback (24.6 mm/s) instead of initial_layer_speed
(10 mm/s). This is the shared speed path, so it affected all printers on this
branch, not just belt ones.

Auto resolves to BeltAffine only when belt_printer is set with a non-zero
slicing rotation, and to XY (evaluator inactive, legacy behaviour) otherwise --
exactly what the option's own description already promised.

Caught by "Brim uses first layer speed" (upstream #14616), which arrived with
the upstream merge; the bad default dates back to a9bae54f20 (#30). Verified
against a pristine upstream/main build, which passes the same test.

tests/fff_print: 100/100 test cases, 1085 assertions (was 99/100).
Both belt regression tests still pass, confirming Auto still resolves to
BeltAffine for belt printers.

Note: this changes a config default. Projects and profiles that stored
first_layer_plane explicitly are unaffected; those relying on the default will
now get correct first-layer speeds on non-belt printers, so their G-code
changes accordingly.
2026-08-03 01:52:43 -05:00
harrierpigeon 175075fd08 Merge upstream/main into belt-printer
Brings the belt-printer work up to date with 591 upstream commits.

Conflict resolutions (12 files, 42 hunks):

- GCode.cpp: adopted upstream's per-filament/per-nozzle config refactor
  (get_filament_config_index, NOZZLE_CONFIG), the extracted
  generate_timelapse_gcode + farthest-point timelapse, and the
  ConfigOptionFloatsNullable calibration options. Re-applied the belt
  hooks on top: init_belt_writer / axis remap / FirstLayerPlane setup,
  on_set_origin, the belt-corrected calib_z for the volumetric speed
  tower, and path_on_first_layer (belt's per-path first-layer test) in
  place of upstream's layer-index on_first_layer() in the acceleration,
  jerk and overhang-detection paths. Swept upstream's new m_writer.
  uses to m_writer-> since belt holds the writer by unique_ptr.
- interpolate_value_across_layers: kept upstream's banded stepping and
  belt's object-Z-span ratio; dropped upstream's duplicate ratio decl.
- Plater.cpp: took upstream's guarded add_model(...) early-returns and
  the VFA vfa_layer_height plumbing; kept the belt temp-tower path,
  _calib_apply_belt_mode and belt_calib_flip_ringing_tower. Dropped the
  VFA "cut upper" block, superseded upstream by model scaling.
- Brim.cpp: upstream's ObjectInstanceID-keyed brimAreaMap, keeping the
  belt early-return.
- 3DScene.cpp: kept both the belt build-plate tilt up_direction and
  upstream's per-extruder printable-height shading.
- GCodeViewer.cpp: kept upstream's dim-previous-layers setup and belt's
  exemption from the same-result early return.
- TreeSupport.cpp: upstream's >= 0 roof-layer fix inside belt's
  belt-floor branch.
- calib.cpp / GCode.hpp / GCodeWriter.{cpp,hpp} / Print.hpp: upstream's
  additions adapted to belt's pointer-held writer and helpers.
- Custom.json: kept profile version 02.04.00.03 (belt) over upstream's
  02.04.00.01; both bumped from 02.04.00.00.

Building this tree needs the wxInspector dependency, which upstream
added in the interim (python3 and wxWidgets 3.3.2 were already present
in the shared deps prefix).
2026-08-02 16:09:27 -05:00
maddavo 13ae3a1c90 Add outer-only mouse ears and align ear radius controls (#15015)
Improve mouse ear brim controls
2026-08-02 10:54:16 +08:00
Valerii Bokhan fb36d5e73b Feature: Smooth Factor for the Hilbert Curve sparse infill (#14969) 2026-08-01 17:58:04 -03:00
SoftFeverandCopilot Autofix powered by AI 6a2a57bdf4 Bring the rib wall and skip-points prime tower to all printers (#15035)
* Sync WipeTower from BambuStudio(through ca1881761)

* Fix post-slice self-invalidation on custom multi-extruder printers

* Complete the rib wipe tower port in WipeTower2

The rib tower is now always square (prime_tower_width is ignored, as the
GUI already implies), carries the rib origin offset like the BBL tower so
the rib tips sit inside the configured position, clamps the rib length to
the tower diagonal, and extends the ribs for short towers.

* Use the squared rib tower size in arrange estimates

estimate_wipe_tower_polygon reserved the arrange footprint and clamped the
tower X position with the raw prime_tower_width, under-reserving space
whenever the rib wall squares the tower to a different width.

* Print the WipeTower2 shell with a non-support, non-soluble filament

Like the BBL tower: the layer's sparse infill, wall, and brim go to the
first toolchange to a non-support/non-soluble filament, or are printed
with the incoming filament before any toolchange. The minimal-purge
clamp now also covers toolchanges that get no finish-layer saving.
Output is unchanged when no support/soluble filament is used.

* Port the skip-points gap wall to WipeTower2

prime_tower_skip_points was stubbed for Type2 towers: the wall call
hard-coded skip_points=false, the gap cutter received an empty vector,
and append_tcr2 never routed the entry travel. Now the toolchange entry
positions are precomputed from the finalized plan, the wall is cut open
at each entry, and the entry travel approaches around the tower bounding
box through the opening when it starts outside the tower. The geometry
helpers are re-synced with the BBL versions (add_extra_point guards,
per-point side selection). The cone wall keeps its separate path, where
the option stays inert.

Behavior change: non-BBL towers now honor the (default-on) checkbox with
gap walls and routed entries; with the option off the output is
unchanged, and the BBL tower path is untouched.

* Route the in-place toolchange tower entry through the skip-point gap

On multi-tool printers without ramming the tool changes away from the
tower and the entry travel is the tcr's own positioning move, which went
straight across the printed wall. Append the avoid-perimeter path to the
change-filament gcode instead, so the head approaches around the tower
and enters through the wall opening (append_tcr parity).

* Iron the purge start out through the skip-point gap in WipeTower2

Port the BBL tower's entry line ironing: extrude the first 3 mm of the
purge, retract, drag the nozzle 1.5x back out through the wall gap at
F600, creep back at F240 and unretract, so the toolchange start blob
ends up in the gap instead of on the wall. Fires only when the purge
starts at the left-edge entry heading right (in-place toolchangers);
SEMM ram/cooling wipes start mid-box and the priming line has no wall,
so both keep their previous output.

* Reserve WipeTower2 toolchange depth to match the printed purge

The planner reserved ramming rows gated only on enable_filament_ramming and
sized them with the SEMM 0.25s time step, while toolchange_Unload rams on
(semm && enable_filament_ramming) || filament_multitool_ramming with the
multitool time step. Disabling multitool ramming therefore left ~3 unprinted
rows per toolchange as blank bands in the tower. Without ramming the first
wipe line also needs reserved depth of its own (it no longer rides the last
ramming row), plus the y_step/2 offset the wipe start inherits from the
ramming start position - otherwise the tightened boxes truncate the ordered
purge at the box edge.

* Tile WipeTower2 purge rows contiguously across toolchange blocks

Without ramming, each purge block reserved one wipe pitch more than its
rows occupy (ceil+1 rounding plus the ram-geometry start offset), and the
wipe began a full pitch inside the block, leaving a blank band of exactly
two pitches between adjacent blocks. Plan the block as whole wipe rows,
start the first row so the row lattice continues across the block
boundary, and fill the reserved box instead of stopping at the ordered
volume, mirroring how the BBL WipeTower keeps planned depth identical to
printed rows. Ram-printing toolchanges (SEMM with ramming enabled,
multitool ramming) are unchanged.

* Scrub the WipeTower2 toolchange entry with the BBL flat-ironing spiral

The entry scrub now matches the BBL tower's toolchange_wipe_new sequence:
after the ironing drag the retracted nozzle runs a dry expanding-square
spiral centred on the wall-gap entry point before resuming the purge row.
The spiral runs whenever the gap wall is on (disable per filament via
filament_tower_ironing_area = 0); WipeTower2 no longer reads
prime_tower_flat_ironing.

* Restart the WipeTower2 wipe at the box boundary after multitool ramming

With the gap wall on a multi-tool printer, quantize the ram band up to its
whole reserved rows (as the BBL tower does for the old-tool purge) and start
CP TOOLCHANGE WIPE at the left-edge boundary on a fresh row below it instead
of continuing from wherever the ram serpentine ended. The entry scrub then
runs at the wall gap on ram toolchanges too, and the wipe box is whole rows,
so it is filled completely like the no-ram case. SEMM and skip-points-off
behavior is unchanged.

* Move the WipeTower2 wall gap to the wipe start row for ram toolchanges

* code cleanup

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix typo

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-31 18:08:55 +08:00
Ian Bassi 33dfb66aa5 Cyclic ordering improvement (#14784) 2026-07-27 19:58:54 -03:00
Maksym PyrozhokandIan Bassi ef7bfeda9c Cyclic ordering (#13578)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-27 19:52:29 -03:00
47ccca7f72 Full Orca translation via AI (tagged) (#14970)
Co-authored-by: Felix14_v2 <75726196+Felix14-v2@users.noreply.github.com>
Co-authored-by: π² <189209038+pi-squared-studio@users.noreply.github.com>
2026-07-27 19:31:33 -03:00
SoftFever bc016af1c9 Fix post-slice self-invalidation on custom multi-extruder printers 2026-07-27 00:49:08 +08:00
SoftFever 9a72dda79a Fix plugin configuration not taking effect, and improve the plugin config UI (#14944)
# Description

Changing a slicing plugin's configuration had no effect on the sliced
result until you forced a re-slice some other way; it now applies
immediately. Print, printer and filament presets also keep their plugin
configuration separately, so configuring a plugin on one no longer wipes
out what you set on another.

A plugin's custom configuration page gets the same round of improvements
in both the Plugins dialog and the per-preset dialog: it follows the
app's light/dark theme, keeps its state while you edit instead of
resetting under the cursor, and can tell whether it is being edited
globally or for a preset, so "Restore defaults" can be labeled for what
it will actually do. The two bundled examples show this off — Twistify
now ships a custom configuration UI, and Inspector is themed, groups

# Screenshots/Recordings/Graphs



https://github.com/user-attachments/assets/02ca062a-5143-49a3-abe0-a2a040b3a928



## 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-07-25 20:00:56 +08:00
SoftFever 7db0a59cc1 fix: H2C carousel - port BBS NozzleStatusRecorder for per-slot purge tracking (#14800)
## Problem

On H2C (carousel) printers, the wipe tower purge volume calculation in
`_make_wipe_tower()` tracks filament state **per-extruder** (2 slots).
Since H2C has up to 7 carousel nozzle slots on a single extruder, all
filaments sharing that extruder are collapsed into one tracking slot.
This causes:

Test:

[5cubes.3mf.zip](https://github.com/user-attachments/files/30092551/5cubes.3mf.zip)

- **Massive redundant AMS flushing** every filament change on the
carousel triggers a full purge against the "previous" filament, even
when the target nozzle slot already has the correct filament loaded
- **60.9g total weight** instead of ~17g (**3.5× material waste**)
- **3h09m print time** instead of ~1h57m (**60% longer**)

## Root Cause

The code uses `nozzle_cur_filament_ids[extruder_id]` (a 2-element array)
to track which filament was last used for each extruder. BambuStudio
uses `NozzleStatusRecorder`, which tracks per `group_id` (physical
carousel slot 0..6).

## Changes

| File | Change |
|---|---|
| `Print.cpp` | Replace `nozzle_cur_filament_ids` with
`NozzleStatusRecorder`. Use `get_nozzle_for_filament()` to resolve the
physical carousel slot per layer. Select `filament_prime_volume_nc` for
nozzle changes, `filament_prime_volume` for filament changes. |
| `PrintConfig.hpp` | Add `ConfigOptionFloats filament_prime_volume`
(per-filament EC prime volume, missing from upstream but present in BBS
and H2C profiles) |
| `PrintConfig.cpp` | Register `filament_prime_volume` with default
45mm³ (matching BBS) |
| `Preset.cpp` | Add `filament_prime_volume` to preset keys |

Also includes `tests/compare_analyzer/` - two standalone Python tools
for G-code slice comparison and temperature timeline analysis (stdlib
only, no dependencies).

## Test Results (5-color H2C Hybrid print, same 3mf project)

| Metric | Upstream (broken) | **Fixed** | BBS (reference) |
|---|---|---|---|
| **Total weight** | 60.90g | **16.20g**  | 17.47g |
| **Print time** | 3h09m | **1h57m**  | 1h51m |
| **Filament changes** | 105 | 105 | 140 |
| **Tool changes** | 35 | 35 | 35 |
| **Critical discrepancies vs BBS** | ⚠️ YES |  None | — |


## Analysis Tools (`tests/compare_analyzer/`)

Two standalone Python tools (stdlib only, no dependencies) for deep
G-code comparison:

- **`compare_slices.py`** - comprehensive .3mf slice comparison:
filament usage, nozzle mapping, tool change sequences, prime tower
analysis, temperature timeline, retract parameters, and automatic
critical discrepancy detection (weight/time anomalies)
- **`show_temp_plot.py`** - interactive HTML temperature timeline
plotter for visualising heater profiles during multi-nozzle prints
(supports single-file and side-by-side comparison)

Usage:
```bash
python3 tests/compare_analyzer/compare_slices.py file1.3mf file2.3mf --labels "Upstream" "Fixed"
python3 tests/compare_analyzer/show_temp_plot.py file1.3mf file2.3mf
```


## Screenshots

### OrcaSlicer Upstream (unfixed) - 60.90g, 3h09m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 22 58"
src="https://github.com/user-attachments/assets/3efb2bff-ff1e-43db-9669-feafa5921b51"
/>


### OrcaSlicer Fixed - 16.20g, 1h57m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 23 08"
src="https://github.com/user-attachments/assets/c1d36dc5-9b01-4691-80ef-7364540e1f4e"
/>

### BambuStudio Reference - 17.47g, 1h51m
<img width="1512" height="982" alt="Screenshot 2026-07-16 at 15 24 52"
src="https://github.com/user-attachments/assets/5c0b11e2-64f4-40e9-8c7c-3f42519786c3"
/>

### Temperature Timeline: Upstream vs Fixed
<img width="1511" height="829" alt="Screenshot 2026-07-16 at 15 23 35"
src="https://github.com/user-attachments/assets/926c2cb5-dfd4-4ce0-bb40-82e6165eb134"
/>


### Temperature Timeline: Fixed vs BBS

<img width="1512" height="825" alt="Screenshot 2026-07-16 at 15 23 51"
src="https://github.com/user-attachments/assets/85c72dda-e779-4aa6-8118-fb17e5f8482d"
/>


## Compatibility

Safe for non-carousel printers: when each extruder has a single nozzle,
`group_id == extruder_id`, so `NozzleStatusRecorder` behaves identically
to the original per-extruder tracking. The `filament_prime_volume`
default (45mm³) matches the existing global `prime_volume` default.

## Reference
BambuStudio `Print.cpp` `_make_wipe_tower()` L3341-3392 -
`NozzleStatusRecorder` pattern.
2026-07-22 12:59:17 +08:00
Alexandre Folle de Menezes b7a6583eea Fix punctuation, spacing and uppercasing issues (#14848) 2026-07-20 09:47:33 -03:00
Alexandre Folle de Menezes 73f6bca9fe Use the appropriate Unicode char for °C (Celsius) (#14849) 2026-07-20 09:46:29 -03:00
SoftFever 9b5eb76478 Misc updates and clean up
- WipeTower: use filament_ramming_volumetric_speed(_nc) for ramming, falling back to
  max_vol_speed only when nil; gate precool temps on enable_pre_heating
- ToolOrderUtils: disable the inter-layer forecast in the per-nozzle base reorder so
  H2D/H2C ordering is unchanged
- PrintConfig: stop stripping filament_prime_volume in handle_legacy; document that
  prime_volume drives the Type2 wipe tower and filament_prime_volume the Type1 one
- GCodeProcessor: exclude post-print end-gcode M400 dwells from the M73 estimate and
  drop the dead air-filtration state
- Trim verbose BambuStudio source-location comments across the port
2026-07-20 17:34:13 +08:00
SoftFever 65ab9b61a8 Merge branch 'main' into feature/h2c_support_clean_fixes 2026-07-20 00:02:43 +08:00
SoftFever a152c7bcad Name the plugin capability config option consistently
The preset option was plugin_preference_overrides while the GUI field type
that renders it was GUIType::plugin_config, for one and the same thing.
Settle on "config": the store, the dialog and the Python hooks all say config
already, so renaming that way touches 4 files instead of the whole plugin
subsystem and the public plugin API.

Keep the _overrides suffix — the option is the preset's override layer over
the base PluginConfig store, a distinction EffectiveCapabilityConfig tracks.

Also wrap the printer tab's group heading in L(); it was the only one of the
three missing it, and was therefore untranslatable.
2026-07-17 18:34:15 +08:00
Ian Chua 4c60781cf4 fix: merge branch feat/plugin-feature 2026-07-17 14:02:10 +08:00
SoftFever b663f851fe Merge branch 'main' into feat/plugin-feature 2026-07-17 11:30:34 +08:00
Alexandre Folle de MenezesandIan Bassi 979e56f1d0 Standardize the Unicode chars for unit strings (#14631)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-16 17:37:09 -03:00
denis svinarchuk 407c78fb30 fix: port BBS NozzleStatusRecorder pattern for per-carousel-slot purge tracking
The upstream _make_wipe_tower() tracked purge volumes per-extruder (2 slots),
which collapsed all H2C carousel filaments into one slot and caused massive
redundant AMS flushing (~40g instead of ~0.4g).

Changes:
- Print.cpp: Replace per-extruder nozzle_cur_filament_ids with BBS
  NozzleStatusRecorder that tracks per group_id (carousel slot 0..6).
  Use get_nozzle_for_filament() to resolve physical slot per layer.
  Select filament_prime_volume_nc for nozzle changes, filament_prime_volume
  for filament changes (BBS pattern).
- PrintConfig.hpp/cpp: Register filament_prime_volume (per-filament EC prime
  volume, default 45mm³) matching BBS PrintConfig.
- Preset.cpp: Add filament_prime_volume to preset keys list.

Safe for non-carousel printers: group_id == extruder_id when each extruder
has one nozzle, so NozzleStatusRecorder behaves identically to the original
per-extruder tracking.

Reference to BBS: BambuStudio/src/libslic3r/Print.cpp _make_wipe_tower() L3341-3392
2026-07-16 14:54:46 +01:00
Mqrius 6d1974a662 Restore flow calibration special order, overridable via fill order (#14786) 2026-07-16 09:10:15 -03:00
Ian Chua cd8c42b125 fix merge conflicts 2026-07-15 21:04:42 +08:00
SoftFever 0849c44ae0 Merge branch 'main' into dev/ams-heat 2026-07-15 19:45:27 +08:00
SoftFever 8cb0cb7693 Merge branch 'main' into feat/plugin-feature
Resolve five conflicts, all of which needed both sides rather than a pick:

- BackgroundSlicingProcess: ours was a pure tabs->spaces reformat of base, so
  keep main's per-filament volume/nozzle map read-back (its only change here).
- GUI_App: main's #12506 else-if attached to an `if` this branch deleted;
  re-expressed onto the same-agent early-return path (the agent factory caches
  per id, so pointer equality is the same predicate).
- MainFrame: both sides relocated Sync Presets independently; keep main's
  push_notification plus the branch's Plugins menu items.
- Tab: the "TODO: Orca: Support hybrid" blocks were unchanged base, not a branch
  decision; take main's enabled Hybrid to match the already auto-merged siblings.
- test_config: union of both sides' cases (6 plugin + 9 multi-nozzle).
2026-07-15 17:18:46 +08:00
MqriusandIan Bassi c84d5c7943 Add top/bottom surface fill order control (Outward/Inward) for Concentric, Archimedean Chords and Octagram Spiral (#14179)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-14 15:34:13 -03:00
Valerii BokhanandIan Bassi 50768e0716 Feature: Retract amount after wipe (#11015)
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-07-14 14:42:16 -03:00
Ian Chua fbed6f7dc6 cleanup 2026-07-14 13:10:42 +08:00
Ian Chua 796080ff76 fix: naming 2026-07-14 11:34:06 +08:00