Skip to content

FOrcaSlicer v2.3.2-fos.8.4

Choose a tag to compare

@jiyang1018 jiyang1018 released this 14 Jul 17:52

Fix: Support, Tree Support and the Prime Tower Were All Sized for Nozzle 1

This release is a single theme. Under mixed nozzle sizes, three features computed
their geometry from nozzle 1 no matter which tool actually printed them. Everything here
is a no-op on a uniform-nozzle machine.

Problem

Assign support to filament 2 on a 0.2 / 0.4 / 0.6 / 0.8 machine and the slicer picks the
right nozzle - then tells it to lay a 0.22 mm line, which is nozzle 1's width. The
0.4 mm tip is asked for a bead thinner than itself.

The prime tower was worse. Every tool wiped at 1.0 mm and rammed at 2.0 mm - the
0.2 mm nozzle included, at 5x and 10x its own diameter.

None of this is a rounding error. These are extrusions a nozzle physically cannot lay.

Root Cause

The rule already existed. Nothing outside walls and infill was using it.

Absolute line widths in a process preset are authored against nozzle 1, the reference
nozzle the preset was resolved with. Any feature printed by a different tool must have
that width re-derived by ratio. PrintRegion::flow() has done exactly this for
frExternalPerimeter / frPerimeter / frInfill / frSolidInfill / frTopSolidInfill
all along:

// config_width may have been resolved against nozzle 1 already;
// recompute using actual extruder's nozzle
float ref_nozzle = float(print_config.nozzle_diameter.get_at(0));
config_width.value = (config_width.value / ref_nozzle) * nozzle_diameter;

Support roles are not in that list, and support never passes through PrintRegion::flow()
at all - it goes through free functions in Flow.cpp that pick the right nozzle and then
hand it an unadjusted width.

Tree support was wrong in a second way. support_extrusion_width - which sizes the
tree base - was read from support_interface_filament's nozzle, not the base's. Two
further sites hardcoded nozzle_diameter.get_at(0), pinning branch spacing and collision
resolution to nozzle 1 outright.

The prime tower kept a single scalar and overwrote it. WipeTower2::m_perimeter_width
is assigned inside set_extruder(), which is called once per filament - so the last
filament configured won
, by accident of iteration order. On 0.2/0.4/0.6/0.8 that is
0.8 x 1.25 = 1.0 mm, for everyone. The code states the assumption out loud:

m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio;
// all extruders are now assumed to have the same diameter

Meanwhile m_filpar[idx].nozzle_diameter had been populated all along and never read -
its comment says "to be used in future with (non-single) multiextruder MM".

Changes

src/libslic3r/Flow.hpp / Flow.cpp

  • New fos_width_for_nozzle() - the nozzle-1 -> printing-nozzle ratio rule, exported so
    support, SupportParameters and TreeSupport all derive widths identically rather
    than three different ways. Percent widths are passed through untouched;
    get_abs_value() already ratios over the nozzle it is given.
  • support_material_flow, support_material_1st_layer_flow and
    support_material_interface_flow now route through it.

src/libslic3r/Support/SupportParameters.hpp

  • support_extrusion_width now comes from support_filament (was
    support_interface_filament) and is ratio-derived. It sizes the support base, so it was
    taking its dimensions from the wrong tool.

src/libslic3r/Support/TreeSupport.cpp

  • Two hardcoded nozzle_diameter.get_at(0) reads replaced with the support filament's
    nozzle, ratio-derived. These drive tree collision resolution and branch spacing - a tree
    printed by a 0.6 mm tool was being built to 0.2 mm dimensions.

src/libslic3r/GCode/WipeTower2.cpp / .hpp

The scalar was doing two jobs. They are now separate:

  • Structure width - footprint, box margins, brim spacing, and the tower's perimeter
    and sparse fill - is bound to wipe_tower_filament, the tool that genuinely prints
    them. (set_extruder() marks that filament as the only non-soluble one, so
    first_toolchange_to_nonsoluble() lands finish_layer on it.) Previously this value was
    whichever set_extruder() call happened to run last.
  • Wipe and ramming now extrude at each tool's own width and flow, from
    m_filpar[tool].nozzle_diameter.
  • Depth planning follows. plan_toolchange() and save_on_last_wipe() reserve depth
    using the same per-tool widths. Width and spacing have to move together - fix the width
    alone and the purge block over- or under-fills, and the reserved depth stops matching
    the lines emitted.

Ramming's E is volumetric (ramming_speed * time / filament_area), so only the line
length changes. Purged volume is unaffected.

src/slic3r/GUI/*, src/libslic3r/Print.cpp - "Default" is gated

Support/raft base, Support/raft interface and Wipe tower offer a
Default option meaning whichever filament is already loaded prints this. Under mixed
nozzles that is not a valid choice, and it cannot be made one:

Support paths are generated once, at slicing time, with a single flow. The tool is not
chosen until G-code export - where ToolOrdering reassigns the tool on
already-generated extrusions
and nothing re-renders them. A path built for a 0.2 mm tip
can be handed to a 0.8 mm tip.

So when nozzle diameters differ:

  • Those three dropdowns show "Empty" instead of "Default".
  • The slicer refuses to slice until you choose a filament, and names which dropdown is
    blank.

The unset value is deliberately not auto-filled with filament 1 - silently landing on
nozzle 1 is precisely the bug being fixed. On a uniform-nozzle machine, "Default" behaves
exactly as before.

Version

  • FOS_VERSION -> 2.3.2-fos.8.4; installer VIProductVersion 2.3.2.8 -> 2.3.2.9.

Result

Verified in G-code on a 0.2 / 0.4 / 0.6 / 0.8 machine, support base on filament 3,
interface on filament 2, wipe tower on filament 2:

before after
Support (0.6 mm tool) 0.22 mm 0.66 mm
Support interface (0.4 mm tool) 0.22 mm 0.44 mm
Tower wipe, tools 0-3 1.0 / 1.0 / 1.0 / 1.0 0.25 / 0.5 / 0.75 / 1.0
Tower ramming, tools 0-3 2.0 / 2.0 / 2.0 / 2.0 0.5 / 1.0 / 1.5 / 2.0
  • Purge block stays inside the tower wall - depth planning tracks the new widths ✓
  • Tree support branch spacing now scales with the support nozzle ✓
  • Uniform-nozzle slices are unchanged. A 0.6 / 0.6 / 0.6 / 0.6 job produces the same
    support width (0.62 mm), the same tower widths (0.75 / 1.5 mm) and the same extrusion
    counts as before ✓

Upgrade Note

If you use mixed nozzles and support or a prime tower, you will be asked to pick a
filament for Support/raft base, Support/raft interface and Wipe tower the
first time you slice. Projects saved before 8.4 may open with these blank; the slicer will
tell you which one it needs. This is intentional - previously those settings silently
resolved to nozzle 1.

Uniform-nozzle users are unaffected and need do nothing.

Known Issues

Support layer height still follows nozzle 1. Consistent with the 8.x rule that all
nozzles follow nozzle 1's process settings, but it means a 0.8 mm nozzle can be asked to
print 0.1 mm support layers. A layer-height clamp would be a new feature, not a fix, and
is deferred.

Ramming-to-wipe alignment. The Y offsets that align the ramming block to the first
wipe line still use the tower's structure width, so with per-tool widths they can be off
by up to ~0.4 mm. Cosmetic; it does not affect purge volume or tower geometry.

Thick internal bridges (carried from 8.2/8.3, not a regression). An internal bridge
extrudes at nozzle_diameter * sqrt(bridge_flow) with no clamp against layer height. This
is upstream OrcaSlicer behavior - stock Orca with uniform 0.8 mm nozzles at 0.24 mm layers
produces the same effect. Mixed nozzles change the magnitude, not the mechanism. Workaround:
disable Thick internal bridges when the solid-infill nozzle is much larger than the
layer height.