cooling_filter_enabled existed as a config option but was shown nowhere,
and there was no capability flag to gate it. The cooling filter and air
filtration are alternative accessories sharing the same duct, so a
printer declares one or the other.
- new hidden printer capability flag support_cooling_filter
- "Use cooling filter" toggle in the Accessory group, shown only when
the printer supports it; the air-filtration toggle hides in that case
(no vendor restriction: third-party printers keep air filtration)
- explicit defaults (0) in the common machine base
- H2C declares support_cooling_filter=1 instead of support_air_filtration;
its start-gcode already carries the cooling-filter conditional, so the
toggle is functional. On H2C this drops the two exhaust-fan lines that
air filtration emitted for ABS-class filaments, matching the printer's
actual duct accessory; H2C is new on this branch so no existing user
output changes.
Printers without the flag keep exactly the previous accessory UI and
g-code.
New printer option fan_direction (undefine/left/right/both, default
undefine) declares which side the auxiliary part-cooling airflow comes
from. When set and the printer has an auxiliary fan, auto-orient adds a
yaw rotation so the dominant overhang area faces the airflow, and newly
added primitive shapes are pre-oriented the same way (except the Cube,
whose axis-aligned bounding box the pressure-advance pattern calibration
depends on).
- FanDirection enum + fan_direction printer option (Accessory group,
enabled only with auxiliary_fan)
- orient engine: weighted overhang areas per candidate, yaw-direction
search, vertical rotation applied on top of the primary orientation;
the cooling weights are taken from the candidate actually chosen,
including the flat-bottom tie-break
- orient_for_cooling() for primitive placement
- set fan_direction=left on H2C/H2D/H2D Pro/X1/X1E/P1S 0.4 profiles
(X1C/H2S/P2S/X2D/Qidi X-Max 4 already carried the key, which now
takes effect)
With fan_direction unset or no auxiliary fan the vertical rotation stays
identity and auto-orient results are unchanged; slicing and g-code are
never affected.
The engine already implements prime_volume_mode (Default/Saving/Fast)
but nothing in the UI could set it, leaving prime-saving unreachable on
multi-sub-nozzle extruders and fast purge unreachable on printers that
support it.
- new PurgeModeDialog with selectable Standard/Fast or
Standard/Prime Saving cards depending on printer capability
- "Purge mode" sidebar button next to Flushing volumes; opens the
dialog and stores the choice in the project config
- printer preset-load gating: button shown only when the printer has
multiple sub-nozzles per extruder or sets support_fast_purge_mode;
stale project values the printer cannot honor reset to Default
- enable fast purge on A2L 0.4 (support_fast_purge_mode), explicit
default 0 in the common machine base
- new dialog strings added to OrcaSlicer.pot
Printers without these capabilities never show the button and their
projects keep prime_volume_mode at Default, so slicing output is
unchanged.
The Filament Track Switch (H2-series accessory, product code O2L-FTS) feeds
every AMS to both extruders through a two-track switch. Port full support
across the device layer, project config, and GUI.
Device / config:
- Model the switch-aware AMS binding (the set of extruders an AMS can feed
and which input track A/B feeds it), switch readiness, the O2L-FTS firmware
module, and the fun2 capability bit for checking a slice against installed
hardware.
- Register has_filament_switcher and enable_filament_dynamic_map as project
config that persists with the project and restores from a saved 3mf, and
force both back to false on every project/printer/CLI load path. Live
device sync is the only thing that sets them true.
GUI:
- Sidebar sync activates the switch from live device state, attributes each
AMS to the extruder its input track feeds, shows a floating status icon
(ready / not-calibrated), and surfaces a one-time tip / not-calibrated
warning.
- Send dialog gains a non-blocking slice-vs-hardware mismatch warning and a
blocking error when a slice needs dynamic nozzle mapping but the switch is
missing or not set up.
- AMS load/unload guards, AMS-view routing glyph + un-calibrated banner +
hidden external-spool road, and mapping-popup external-spool lockout.
- Filament pickers collapse the per-extruder split into a single deduplicated
"AMS filaments" group with a smart-assign toggle when the switch is ready.
- Firmware-upgrade panel lists the O2L-FTS accessory and its version.
- Device-provided filament-change steps (ams.cfs) drive the change-step
display when firmware sends them, including the three switch steps.
- "Load current filament" asks which extruder to feed via a
FeedDirectionDialog when the switch is calibrated.
Inert without the accessory: every path is gated on the switch being
installed (MQTT aux bit 29, default off) or ready, both project flags default
false, and the per-extruder AMS attribution is byte-identical, so AMS state,
the send/load UI, and sliced g-code are unchanged for every printer that does
not report a Filament Track Switch.
Multi-nozzle sync widget, AMS rack-nozzle mapping popup, calibration rework, send-dialog nozzle mapping and extruder-count UI. Includes the fix to persist the AMS sync badge on filament cards (H2C/A2L and direct-sync printers).
Adds PluginHostSlicing, which registers the print-graph data model (Print,
PrintObject, Layer, LayerRegion, Surface, ExPolygon, extrusions, ...) into the
orca.host submodule in the same raw-class style as PluginHostApi's Model/Preset
graph, with shared helpers in PluginBindingUtils. SlicingPipelinePluginCapability
is trimmed to the capability surface (the standalone SlicingNumpy helper is folded
away). Adds the Twistify example plugin next to Inset and broadens the binding,
hook, and plugin-install tests.
Introduces a plugin capability that runs Python at the seams of Print::process(),
letting a plugin read and rewrite slicing state as it is computed.
- New slicing_pipeline_plugin config option; selected plugin refs are serialized
into the print manifest.
- Print gains an injectable hook fired at each pipeline step (posSlice,
posPerimeters, posInfill, ...). It is a no-op when unset, fires only on genuine
(re)computation, and never on the use-cache path.
- orca.slicing submodule: SlicingPipelineCapabilityBase plus a trampoline and a
Step enum. Capabilities read the live graph through zero-copy int64 numpy views
(contour/holes geometry with unscaled coordinates, flattened toolpath data) and
edit it through 2D-geometry mutators with cache-invariant refresh.
- GUI dispatcher runs capabilities during slicing under the GIL, turns plugin
errors into slicing errors, honors cancellation, and adds the plugin picker.
- Ships the InsetEverySlice sample plugin and binding/hook tests.
* feat: add support for 3MF file format in printer configurations and export options
* fix file extension
* enable 3mf for X Max 4
* disable use_3mf for X Plus 4
* Fixed an issue where `label_object_enabled` was not properly propagated to 3mf
* enable exclude object for Max 4
* remove hardcoded use 3mf for flashforge, move them to the new printer profiles config
* Fix junction deviation and jerk settings behavior
Process settings now follow the selected printer's junction deviation
configuration. When machine_max_junction_deviation is enabled,
default_junction_deviation is shown and jerk settings are hidden. When
junction deviation is disabled, jerk settings are restored and
default_junction_deviation is hidden.
Fix a validation issue where junction deviation mismatch warnings could
be reported even when machine_max_junction_deviation was set to 0.
Warnings now apply only when junction deviation is active and point
directly to default_junction_deviation.
Also simplify Motion ability page visibility checks by reusing local
firmware-flavor booleans.
* GUI tweak
- separate Junction Deviation segment
- JD and Jerk stay visible
* Add Optimized Gyroid infill (auto-tuned wavelength + amplitude)
New infill geometry derived from FillGyroid. Two parameters are
auto-computed per-region from density, line spacing, and layer height
(no user inputs):
omega = sqrt(density_adj) / sqrt(1 + layer_height/spacing)
clamped to [0.5, 2.0]
-- Euler-Bernoulli buckling: critical load ~ 1/L^2,
so shorter wavelength under higher load (denser infill)
raises buckling resistance.
amplitude = 0.55 / omega^2, clamped to [0.20, 0.65]
-- Curved-beam bending stress: peak stress ~ A * omega^2,
so amplitude is reduced as omega rises to keep peak
fiber stress bounded while preserving stiffness.
Files:
- src/libslic3r/Fill/FillOptimizedGyroid.{hpp,cpp} (new)
- src/libslic3r/Fill/FillBase.cpp (factory case)
- src/libslic3r/Fill/Fill.cpp (switch case)
- src/libslic3r/Layer.cpp (switch case)
- src/libslic3r/PrintConfig.{hpp,cpp} (enum + label)
- src/libslic3r/CMakeLists.txt (build sources)
User-facing: appears as "Optimized Gyroid" in the Fill Pattern dropdown.
Density still chosen by user; omega/amplitude are internal.
* Fix build: layer_height is in FillParams, not Fill base
* Add ipOptimizedGyroid to multiline infill list in ConfigManipulation
* Refactor: replace ipOptimizedGyroid enum with gyroid_optimized boolean
Per @RF47's review feedback, fold the optimized wave math into FillGyroid
itself behind a per-region boolean instead of a separate infill enum.
What changes:
- New ConfigOptionBool "gyroid_optimized" on PrintRegionConfig (default
false). When unchecked, gyroid behavior is byte-identical to before.
- Optimized wave math (compute_omega_factor, compute_amplitude_factor,
f_opt, make_*_opt, make_optimized_gyroid_waves) lives inside
FillGyroid.cpp. _fill_surface_single branches on params.gyroid_optimized.
- FillParams gains a bool gyroid_optimized field, populated in Fill.cpp
from region_config alongside fill_multiline.
- UI checkbox added under Strength > Infill in Tab.cpp, label
"Optimize gyroid wave (experimental)". Toggle is hidden by
ConfigManipulation when sparse_infill_pattern != ipGyroid.
- "gyroid_optimized" added to s_Preset_print_options for preset I/O.
What goes away:
- ipOptimizedGyroid enum value, factory case, switch cases, dropdown
label, string key.
- FillOptimizedGyroid.cpp / FillOptimizedGyroid.hpp (math moved into
FillGyroid.cpp).
- Net diff drops by ~250 lines.
Existing profiles using gyroid are unaffected.
* Wire gyroid_optimized through SurfaceFillParams to FillParams
Linux build failed because line 921 in Fill.cpp populates a
SurfaceFillParams (the dedup struct), not FillParams directly.
Add the field there, in operator< / operator==, and copy it to
FillParams at both conversion sites.
* Use toggle_line for gyroid_optimized: hide row when pattern != gyroid
* Account for multiline wall thickness in omega correction (per @RF47)
When fill_multiline = N, each gyroid wall is N lines thick, so the
geometric scale fed into the buckling correction term should be
spacing * N rather than spacing. Increases omega (tighter wavelength)
when multiline is enabled, consistent with the thicker wall being
more buckling-resistant.
* Optimized gyroid via marching squares on the implicit scalar field
Per @RF47 review: replace the analytical f_opt / make_one_period_opt
wave generator (which had visible kinks at vertical-horizontal
transitions) with a marching-squares iso-extraction on the gyroid
scalar field, modeled on FillTpmsFK.cpp.
- New marchsq::GyroidField in FillGyroid.cpp evaluates
F(x,y,z) = sin(fx*x)cos(fy*y) + sin(fy*y)cos(fz*z) + sin(fz*z)cos(fx*x)
where fx = omega * baseline (anisotropic in x), fy = fz = baseline.
- get_gyroid_polylines() runs marching squares at iso=0 and converts
rings to polylines.
- _fill_surface_single() optimized branch now builds GyroidField,
runs marching squares, and skips the bb.min translate (field
output is already in absolute coords).
- Dropped: f_opt, make_one_period_opt, make_wave_opt,
make_optimized_gyroid_waves, compute_amplitude_factor. Amplitude
has no clean analog in iso-zero extraction.
- Standard (non-optimized) gyroid path unchanged.
* Mass calibration: compensate period by cbrt(omega) for x-anisotropic field
Per @RF47: optimized vs standard gyroid had different masses at the
same sparse_infill_density setting. Cause: scaling fx by omega while
leaving fy=fz at the baseline raised the surface-area-to-volume ratio
by approximately omega^(1/3) (the geometric mean of the three
frequencies).
Fix: multiply the base period by cbrt(omega) so the geometric mean of
(fx, fy, fz) returns to the standard baseline. Net effect:
fx = omega^(2/3) * baseline_orig
fy = fz = omega^(-1/3) * baseline_orig
which preserves total mass at the same density setting while
preserving the load-direction anisotropy this PR introduces.
* Switch optimized gyroid anisotropy from X to Z (per @RF47)
Z is the typical compression-load axis for FFF parts and is not at
delamination risk under compression — so the dominant failure mode
is column buckling of the vertical strands themselves. Tightening
fz directly shortens the effective vertical strand length, which
improves Z-axis buckling resistance.
Mass calibration via cbrt(omega) period compensation still applies
(scaling exactly one of three frequencies by omega; the geometric-
mean preservation argument is symmetric across axes).
* Update src/slic3r/GUI/Tab.cpp
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
* Address review feedback (Copilot + @RF47)
- Fill.cpp: gate params.gyroid_optimized on (params.pattern == ipGyroid)
so non-gyroid surfaces don't differ in SurfaceFillParams by an
irrelevant flag (would unnecessarily split fill batching).
[Copilot suggestion, RF47 confirmed correct]
- PrintConfig.cpp: drop "amplitude" from the tooltip; only wavelength
is parameterized (the marching-squares iso=0 extraction is invariant
to a uniform field scale, so amplitude has no effect).
- FillBase.hpp: shorten gyroid_optimized comment to match the actual
carried state (no amplitude term).
- FillGyroid.cpp: shorten the marchsq namespace comment block; the
ODR concern was overstated (FillTpmsFK uses the same pattern fine).
* Drop redundant marchsq bb expansion (Copilot)
bb is already offset by 10 * scale_(spacing) above for edge-artifact
margin; the second offset on bb_field doubled the raster area for no
geometric benefit and hurt CPU time on large parts.
* Update src/slic3r/GUI/Tab.cpp
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
* Fix density mismatch + rename to Z-buckling bias optimization
Issue (per @ianalexis): at the same sparse_infill_density setting,
the optimized branch produced denser fill than standard. Verified via
Python sim (sim_gyroid_compare.py) using marching squares on the
implicit field across multiple z slices.
Root cause: the omega formula was inverted from the buckling-physics
intent. The naive sqrt(density_adj) factor produced omega < 1 at
typical print densities (10-30%), which LENGTHENED the Z wavelength
instead of shortening it -- net loss in both mass and strength.
Fix:
- compute_omega_factor: invert to sqrt(1 / density_adj), clamp to
[1.0, 2.0]. Now omega = 2.0 at low density (long strands need
most help) and clamps to 1.0 above ~30% density (no-op, since
standard gyroid is already short enough).
- Remove the cbrt(omega) period compensation. Empirically (sim
table embedded in FillGyroid.cpp comment) the inverted formula
keeps line length per area at ~1.000 of standard across all
densities with no period scaling needed.
Predicted gains (sim, Z-axis Euler buckling proxy):
density line/std strength/std
10% 1.000 2.84x
15% 1.000 1.89x
20% 1.000 1.42x
30%+ 1.000 1.00x (no-op)
Rename per @ianalexis: "Optimize gyroid wave" oversells (now no-op
above 30% density and Z-only). Renamed user-facing label to
"Z-buckling bias optimization (experimental)" with updated tooltip
that scopes to vertical compression and discloses the density cutoff.
Internal config key (gyroid_optimized) unchanged for diff size.
Real-world Instron compression tests at Brown's Prince Lab to follow.
---------
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>