Belt printer: retire the redundant and unused options (#16236)

Follow-up to #16195 and the review discussion on #14394 (yw4z's note
about the third column on the *Belt tilt* row). Removes the belt options
that are redundant or unused before the branch ships, so they never need
compatibility handling after a release, and fixes supports under a
leading overhang. Every removed key is on `handle_legacy()`'s ignore
list, so existing profiles and 3MFs load silently.

## Removed

- **`belt_slice_rotation_global`**, **`preslice_remap_global`**,
**`belt_preslice_global`** (*Global mesh transforms*) and
**`gcode_back_transform`** — the global mode and the back-transform are
what belt printing is; they are presumed on wherever the flags were
consulted (`PrintObjectSlice`, `BeltBackTransform`, `BeltGCode`,
`Print::process`, `PrintApply`, `GCodeViewer`). The *Belt tilt* row is
axis + angle only; the three `fdm_belt_common.json` drop the keys.
- **`preslice_remap_x/y/z`** — no profile used the pre-slice axis remap;
the belt tilt axis plus the G-code axis remap cover the machines that
exist, and its implementation only agreed with itself for a plain swap.
The forward transform is the rotation.
- **`belt_support_z_offset_mode`** and **`belt_support_floor_mode`** —
the first was never read by a generator; the second's only shipped value
(*Generator only*) is now the behaviour.
- **`first_layer_plane`**, **`first_layer_plane_offset`**,
**`first_layer_plane_thickness`** and `FirstLayerPlane.{cpp,hpp}` — the
first-layer band is measured from the belt surface and is one first
layer height thick.
- `belt_brim_instances_compatible()` and its validation warning:
instances along the belt get their brim.

## Supports under a leading overhang (the clipping at the object's local
Z = 0)

The slicing frame of a belt object started at its lowest vertex, but the
belt under the leading end of an overhang lies below that, so no
generator could reach it: normal supports stopped at the object's lowest
layer, and both tree generators carried extension hacks sized from the
pre-rotation bbox and capped at global Z = 0 (right only for the
trailing half of the belt). The frame now starts at the lowest
belt-floor point under the footprint, less a 10 mm margin along the belt
for the base of a support column, and the extensions are gone:

- **Normal supports** run in the object frame and get the global belt Z
offset shifted onto the result (as organic already did). With the offset
on the object layers, a top contact at negative Z turned the
intermediate-layer count negative and the generator allocated layers
until the kernel killed it — any overhang in the leading half of the
belt did this. The first-layer flange expansion is skipped on a belt
(the first support layer is the leading tip, not a flange).
- **Classic tree** nodes keep dropping until their whole circle is in
the belt, so a branch tapers to a tip on the belt instead of stopping a
radius above it.
- **Organic**: the belt is no longer a support blocker. A blocker is a
collision, and a branch descending onto one slides off it, down the belt
and ahead of the part; the belt is where branches end, which the
per-layer floor clipping already does.

Regression test *Belt supports reach the belt under a leading overhang*:
a cube with a fin whose underside is parallel to the layers, 20 mm ahead
of the cube and up to 41 mm of slicing Z above the belt, for normal,
organic and classic tree supports; the lowest support layer must sit on
the belt beneath its own lines.

The belt object height (the layer range) is now estimated from the box
of the mesh as placed on the bed. `raw_bounding_box()` has the
instance's Z offset removed, which was harmless for the old
rotated-extent estimate but not for one anchored at the belt floor (a
point's rotated z and the floor under it move in opposite directions
under a Z shift): with the first version of this change every part came
out as a wedge, sliced only up to its diagonal, in the GUI and CLI
alike. Caught by a GUI test pass; the leading-overhang test now also
checks that the whole part is sliced.

## Belt brim after the parallel support step

`belt_brim_obstacles()` reads every object's layers and support layers,
which another object's support step rebuilds (and now shifts) at the
same time. The brim is generated sequentially once the parallel step is
over (`PrintObject::generate_belt_brim()`). This is the race behind the
Windows arm64 segfault in *Belt brim of each object precedes its
perimeters on its own filament*.

## UI

- *Belt tilt* is two rows: the angle (Advanced) and the axis (Developer;
a profile-level kinematics choice). A shared line is shown by its first
option's mode, so they cannot share one.
- *Machine frame transforms* is five single-option rows (G-code remap X
/ Y / Z, Decouple machine-frame tilt, Machine-frame tilt angle — the
angle row only appears when decoupled) instead of two multi-column
lines; the remap fields got full labels since they stand alone now.
- The gravity indicator on the bed is a plain line along the up
direction (no cone, 60 % of the axes' length), per yw4z.
- The *Show raw G-code (belt only)* legend/canvas toggle and its `B`
shortcut are gone; the preview is the designed view.

Also carries the two-line `phong.fs` fix from #16226 (merges as a
no-op).

## Verification

- `libslic3r_tests` 1116 passed (92 648 assertions); `fff_print_tests`
351 passed (561 696 assertions).
- `scripts/clang_tidy_diff.py --base upstream/belt-printer`: no
findings.
- `scripts/orca_profile_tool.py check`: no profile references a removed
key.
- GUI target builds; a scripted GUI pass (xdotool) checked the settings
groups in every mode, slicing, export, instances, the purge tower,
calibration dialogs, the wizard, printer switching and 3MF round-trip.

The wiki pages (OrcaSlicer/OrcaSlicer_WIKI#374) get a follow-up dropping
the removed sections once this is in.
This commit is contained in:
Joseph Robertson
2026-10-07 00:44:41 -05:00
committed by GitHub
46 changed files with 530 additions and 1544 deletions
+22 -86
View File
@@ -5250,77 +5250,24 @@ void TabPrinter::build_fff()
// Belt tilt: the sole mesh-side transform and the single source of truth for
// the physical tilt (drives bed rendering and support gravity tilt too).
// Isometric rotation, no distortion; the back-transform inverts it before the
// machine-frame remap.
{
Line line = { L("Belt tilt"),
L("Belt tilt axis and angle, applied as a mesh rotation before "
"slicing. Also drives bed rendering and support gravity tilt. "
"Isometric (no distortion); the back-transform inverts it before "
"the machine-frame remap.") };
line.label_path = "printer_basic_information_belt_printer#belt-tilt";
line.append_option(belt_og->get_option("belt_slice_rotation"));
line.append_option(belt_og->get_option("belt_slice_rotation_angle"));
line.append_option(belt_og->get_option("belt_slice_rotation_global"));
belt_og->append_line(line);
}
{
Line line = { L("Pre-slice axis remap"),
L("Remap model axes before slicing so the slicer's coordinate system matches "
"the physical bed orientation. For belt printers whose bed is NOT in the XY plane, "
"use this to swap axes so layers are stacked in the correct physical direction.") };
line.label_path = "printer_basic_information_belt_printer#pre-slice-axis-remap";
line.append_option(belt_og->get_option("preslice_remap_x"));
line.append_option(belt_og->get_option("preslice_remap_y"));
line.append_option(belt_og->get_option("preslice_remap_z"));
line.append_option(belt_og->get_option("preslice_remap_global"));
belt_og->append_line(line);
}
belt_og->append_single_option_line("belt_preslice_global", "printer_basic_information_belt_printer#global-mesh-transforms");
belt_og->append_single_option_line("gcode_back_transform", "printer_basic_information_belt_printer#g-code-back-transform");
{
Line line = { L("First layer plane"),
L("Reference plane used to decide which extrusions get first-layer "
"settings (no fan, slow speed, deferred temperature drop). On belt "
"printers, Auto resolves to the tilted belt-shear plane so that "
"first-layer treatment follows perpendicular distance from the belt "
"surface, not slicing layer index.") };
line.label_path = "printer_basic_information_belt_printer#first-layer-plane";
line.append_option(belt_og->get_option("first_layer_plane"));
line.append_option(belt_og->get_option("first_layer_plane_offset"));
line.append_option(belt_og->get_option("first_layer_plane_thickness"));
belt_og->append_line(line);
}
// Support floor: split across lines so each setting's own mode controls
// its visibility (floor_mode = Develop, floor_offset = Advanced, z_offset_mode = Expert).
// machine-frame remap. The angle is what a user checks against the machine;
// the axis is a profile-level kinematics choice, so it is Develop-only. They
// are separate rows because a shared line is shown by its first option's mode.
belt_og->append_single_option_line("belt_slice_rotation_angle", "printer_basic_information_belt_printer#tilt-angle");
belt_og->append_single_option_line("belt_slice_rotation", "printer_basic_information_belt_printer#tilt-axis");
belt_og->append_single_option_line("belt_support_floor_offset", "printer_basic_information_belt_printer#support-floor-z-offset");
belt_og->append_single_option_line("belt_support_z_offset_mode", "printer_basic_information_belt_printer#z-offset-mode");
belt_og->append_single_option_line("belt_support_floor_mode", "printer_basic_information_belt_printer#floor-mode");
// Machine-frame transform: the shear (tan) + scale (1/cos) that map
// Machine-frame transform: the shear (cot) + scale (1/sin) that map
// Cartesian G-code into the printer's physical machine frame are derived
// from the belt tilt angle. Only the post-slice axis remap and the expert
// decouple override are exposed here.
// decouple override are exposed here, one option per row.
{
auto mf = page->new_optgroup(L("Machine frame transforms"), L"param_advanced");
{
Line line = { L("G-code axis remap (post-slice)"), L("Remap slicing-frame axes to machine axes in G-code output. Applied AFTER slicing, during G-code generation.") };
line.label_path = "printer_basic_information_machine_frame_transforms#g-code-axis-remap";
line.append_option(mf->get_option("gcode_remap_x"));
line.append_option(mf->get_option("gcode_remap_y"));
line.append_option(mf->get_option("gcode_remap_z"));
mf->append_line(line);
}
{
Line line = { L("Machine-frame tilt"),
L("The machine-frame shear (tan) and scale (1/cos) are derived from "
"the belt tilt angle. Enable 'Decouple' to set an independent "
"machine-frame angle when the physical gantry tilt differs from "
"the slicing rotation.") };
line.label_path = "printer_basic_information_machine_frame_transforms#machine-frame-tilt";
line.append_option(mf->get_option("belt_frame_tilt_decouple"));
line.append_option(mf->get_option("belt_frame_tilt_angle"));
mf->append_line(line);
}
mf->append_single_option_line("gcode_remap_x", "printer_basic_information_machine_frame_transforms#g-code-axis-remap");
mf->append_single_option_line("gcode_remap_y", "printer_basic_information_machine_frame_transforms#g-code-axis-remap");
mf->append_single_option_line("gcode_remap_z", "printer_basic_information_machine_frame_transforms#g-code-axis-remap");
mf->append_single_option_line("belt_frame_tilt_decouple", "printer_basic_information_machine_frame_transforms#machine-frame-tilt");
mf->append_single_option_line("belt_frame_tilt_angle", "printer_basic_information_machine_frame_transforms#machine-frame-tilt");
}
option = optgroup->get_option("thumbnails");
@@ -6366,41 +6313,30 @@ void TabPrinter::toggle_options()
bool expert_or_above = (m_mode >= comExpert);
toggle_line("belt_printer_infinite_y", is_belt);
// Belt tilt: the sole mesh-side belt transform (visible by default in belt mode).
toggle_line("belt_slice_rotation_angle", is_belt);
toggle_line("belt_slice_rotation", is_belt);
// Remap, back-transform, and global mesh-transforms toggles are gated by belt
// mode here; finer mode-based visibility is handled by each option's
// ConfigOptionMode in PrintConfig.cpp. Both axis remaps are Develop-only: a
// printer profile sets them once for its kinematics, and a wrong value sends
// ConfigOptionMode in PrintConfig.cpp. The axis remap is Develop-only: a
// printer profile sets it once for its kinematics, and a wrong value sends
// the gantry outside the machine.
for (auto el : {"preslice_remap_x", "gcode_remap_x", "gcode_back_transform"})
for (auto el : {"gcode_remap_x", "gcode_remap_y", "gcode_remap_z"})
toggle_line(el, is_belt);
toggle_line("belt_preslice_global", is_belt);
bool belt_global = is_belt && m_config->opt_bool("belt_preslice_global");
// preslice_remap_global: superseded by belt_preslice_global
toggle_option("preslice_remap_global", is_belt && !belt_global);
// Rotation is the only mesh-side belt transform. Gray out its angle/global
// sub-options when no rotation axis is selected.
// Rotation is the only mesh-side belt transform. Gray out its angle when no
// rotation axis is selected.
auto rot_axis = m_config->option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation")->value;
toggle_option("belt_slice_rotation_angle", is_belt && rot_axis != BeltRotationAxis::None);
toggle_option("belt_slice_rotation_global", is_belt && rot_axis != BeltRotationAxis::None);
// Machine-frame transform: derived from the belt tilt. Only the expert
// decouple override is exposed; its angle is enabled only when decoupled.
// decouple override is exposed; its angle is shown only when decoupled.
toggle_line("belt_frame_tilt_decouple", is_belt && expert_or_above);
toggle_option("belt_frame_tilt_angle",
is_belt && expert_or_above && m_config->opt_bool("belt_frame_tilt_decouple"));
toggle_line("belt_frame_tilt_angle",
is_belt && expert_or_above && m_config->opt_bool("belt_frame_tilt_decouple"));
// First-layer plane: visible alongside the rest of belt-printer settings.
toggle_line("first_layer_plane", is_belt);
toggle_option("first_layer_plane_offset", is_belt);
toggle_option("first_layer_plane_thickness", is_belt);
for (auto el : {"belt_support_floor_mode", "belt_support_floor_offset", "belt_support_z_offset_mode"})
toggle_line(el, is_belt);
toggle_line("belt_support_floor_offset", is_belt);
const bool support_parallel_printheads = printer_cfg.opt_bool("support_parallel_printheads");
toggle_line("parallel_printheads_count", support_parallel_printheads);