Part 2.6: Add belt floor support clipping for all support types

- Fix support clipping z-shift calculation by removing coordinate-space
  mismatch and sync belt_floor_z_shift with global_z_offset; fix
  invalidation so posSupportMaterial no longer resets slicing params
- Add belt floor polygon clipping to non-organic tree support
  (slim/strong/hybrid) with collision surface integration in
  TreeSupportData, belt extension layers, and first-layer brim
  suppression
- Add belt floor clipping to organic tree support pipeline with virtual
  belt raft layers, per-layer polygons in TreeModelVolumes, and
  post-generation layer trimming; fix pre-existing processing_last_mesh
  bug in calculateCollision()

Fix belt floor support clipping: z-shift, invalidation, and global offset

- Fix support clipping z-shift calculation by removing coordinate-space
  mismatch (raw_bounding_box min.z vs trafo_centered m_belt_min_z) and
  sync belt_floor_z_shift with global_z_offset in global shear mode
- Fix invalidation so posSupportMaterial no longer resets slicing params,
  preventing the exact posSlice z-shift from being overwritten by the
  bounding-box approximation on support-only setting changes
- Remove double-counting of global z_offset on support layers — support
  already inherits the offset from object layers during generation

This Work Was Co-Authored-By Claude Opus 4.6 (1M context) <noreply@anthropic.com>

UI: gray out inactive belt sub-options, rename to mesh transforms, move to Advanced

Fix mesh clipping through build plate after belt shear/scale transform

Generalize G-code viewer designed-view toggle for full belt transform

Clip support layers to transformed belt floor plane

Supports below the tilted build plate (Z = shear_factor * from_axis - min_z)
are now clipped via half-plane intersection after generation. Belt floor
parameters stored in SlicingParameters and populated in both update_slicing_parameters()
and the static slicing_parameters() overload.

Make belt G-code viewer toggle more prominent, add B keyboard shortcut

- Add separator + teal "Belt Printer" header in legend panel
- Append [B] hint to checkbox label
- Add B key shortcut in GLCanvas3D to toggle designed/machine view
- Read belt_printer_angle from loaded G-code headers to enable belt view

Add per-axis global transform option for belt printer shear

New belt_shear_{x,y,z}_global bool configs. When enabled, shear incorporates
instance shift so objects at different bed positions get position-aware
transform (Z += factor * instance_shift_on_from_axis).

Fix global shear: use layer Z offset instead of mesh transform, add config invalidation

- Global shear offset applied as post-slicing layer print_z adjustment
  instead of mesh transform (which was absorbed by min_z normalization
  or shifted mesh out of slice range)
- Register all belt transform options in Print::invalidate_state_by_config_options
  to trigger posSlice re-slicing (the fallback only invalidated Print steps,
  not PrintObject steps — belt changes had no effect without manual re-slice)
- Belt gcode remap options added to steps_gcode (gcode-export only)
- Skip empty-first-layer check for belt objects with global Z offset

WIP: split instances for global shear, relative Z offsets, debug logging

- PrintApply: when belt global mode active, prevent instance grouping by
  adding unique Z perturbation to trafo — each copy becomes its own
  PrintObject with independent layers
- PrintObjectSlice: compute global Z offset relative to minimum Y shift
  across all PrintObjects (lowest-Y object stays at Z=0)
- Debug logging (warning level) for belt global shift values and offsets

Known issues:
- Cached posSlice results cause stale offsets when mixing copies with
  individually-added objects — need to compute min baseline outside slice()
- Supports still generate to Z=0 instead of object's global Z offset

Fix global shear for copied objects: disable shared-object layer optimization

When belt global Z shear is active, each object needs unique layer Z
values based on its bed position. The shared-object optimization was
causing copies to reuse the source object's layers (and its Z offset)
instead of computing their own position-based offset.

started work on getting supports to work properly

one step forward, one step back

this version didn't quite work.  Getting somewhere though

about to add UI controllable tests

added configuration options for supports

tweak CLAUDE.md to be more aggressive for my machine.  This commit should probably be pulled out before contributing upstream

still chasing down some bugs

moving objects between slices no longer results in improper Z-height because of caching

added more data to the debug logs

Z offset is getting more global again

still not quite there, I think there's a fundamental logic flaw?

hunting for bugs

finally have a functional fix

Add belt floor clipping to tree supports (organic and non-organic)

- Add belt floor polygon clipping to non-organic tree support
  (slim/strong/hybrid) in draw_circles() and terminate nodes at the
  belt surface instead of the horizontal build plate
- Add belt floor clipping to organic tree support pipeline with virtual
  belt raft layers for sub-floor branch generation, per-layer belt
  floor polygons in TreeModelVolumes, and post-generation layer trimming
- Fix pre-existing processing_last_mesh bug in TreeModelVolumes that
  prevented m_anti_overhang (support blockers) from ever being applied;
  skip empty first layer check for belt printers

Commits:

current approach: make a face surface to build supports to

closer!

supports now terminate on shear plane, now need to get shear plane to correct Z height

nearly there

chasing down logic issues still

committing for checkpoint, this still does not work

still got logic problems...

cull support clipping

stashing changes for now.  Going to focus on getting the global shear OFF support generation dialed first.

beginning per object shear calcs

Local shear transform is on correct Z offset now

local shear finally works now and needs more testing

global shear works now, needs thorough testing

debugging non-45 degree angles

debugging part 2

supports at all angles work now

remove debug logging

Add belt floor collision to non-organic tree support pipeline

- Integrate belt floor as a collision surface in TreeSupportData so
  branches route around the belt naturally, replacing the explicit
  termination checks in drop_nodes()
- Add belt extension layers below the object after draw_circles() to
  allow support geometry to extend to the diagonal belt surface instead
  of terminating at a horizontal first layer
- Fix coordinate overflow in belt floor polygons (scale_(1e4) exceeds
  int32), skip first-layer brim expansion for belt printers, and
  extend empty first layer check bypass to all belt modes

add debug logging, Z translate for tree supports

still not seeing any cutoff surface yet

adding debug options

attempt #2 at trees

if hit Z buildplate stop but don't set to_buildplate true

getting closer

tree support almost there, just need to get rid of the circles at the beginning

getting closer

belt / shear plane clip works, need to figure out the buidlplate plane issues

more logic, added debugging logs

supports now extend somewhat below Z=0 in global shear mode

fix bad alloc, add 10mm below build plate

fully works now

shear transform + prusa tree support generation works now.

pull out debug logging
This commit is contained in:
harrierpigeon
2026-03-30 13:25:40 -05:00
committed by Joseph Robertson
parent 719af2d81d
commit 7ff6bc42b1
27 changed files with 1337 additions and 137 deletions
+74
View File
@@ -3366,6 +3366,36 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
// this struct is used to easy retrieve setting. No other function except those in TreeModelVolumes and generate_initial_areas() have knowledge of the existence of multiple meshes being processed.
//FIXME this is a copy
// Contains config settings to avoid loading them in every function. This was done to improve readability of the code.
// Belt printer: add virtual "belt raft" layers below the object so
// organic branches can extend below the model's first layer and
// terminate at the belt surface instead of creating a flat base at Z=0.
{
PrintObject &po = *print.get_object(processing.second.front());
const auto &sp = po.slicing_parameters();
const auto &pcfg = po.print()->config();
const double sf = sp.belt_floor_shear_factor;
if (std::abs(sf) > EPSILON && std::abs(po.belt_global_z_offset()) > EPSILON
&& pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) {
// z_shift_local is the belt surface height at Y=0 in local coords.
// Extend below the belt so the base expansion and build-plate
// termination happen inside the belt region and get clipped.
// Use the distance from the pre-shear bbox min Z to the part's
// post-shear min Z, plus 10mm for base expansion headroom.
double bb_min_z = std::abs(po.model_object()->raw_bounding_box().min.z());
double extra_depth = bb_min_z + 10.;
int num_extra = std::max(0, (int)std::ceil(extra_depth / sp.layer_height));
if (num_extra > 0) {
// Insert belt raft layers at the front, from lowest Z to highest.
std::vector<coordf_t> belt_layers;
belt_layers.reserve(num_extra);
for (int i = num_extra; i >= 1; --i)
belt_layers.push_back(sp.first_object_layer_height - i * sp.layer_height);
// Prepend to existing raft_layers (if any).
auto &rl = processing.first.raft_layers;
rl.insert(rl.begin(), belt_layers.begin(), belt_layers.end());
}
}
}
const TreeSupportSettings &config = processing.first;
BOOST_LOG_TRIVIAL(info) << "Processing support tree mesh group " << counter + 1 << " of " << grouped_meshes.size() << " containing " << grouped_meshes[counter].second.size() << " meshes.";
auto t_start = std::chrono::high_resolution_clock::now();
@@ -3561,6 +3591,43 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
if (layer) layer->polygons = intersection(layer->polygons, volumes.m_bed_area);
});
// Belt floor: clip ALL organic support layers (including intermediate/base
// fill) against the belt surface. The branch slices were already clipped
// in organic_draw_branches(), but intermediate layers generated between
// branches and the build plate need clipping too.
// Compute the belt floor polygon directly from each layer's print_z
// rather than mapping to a layer index (avoids index mismatch issues).
{
const auto &sp = print_object.slicing_parameters();
const double sf = sp.belt_floor_shear_factor;
const double z_shift = sp.belt_floor_z_shift - print_object.belt_global_z_offset();
const double floor_off = print_object.print()->config().belt_support_floor_offset.value;
const int from_axis = sp.belt_floor_from_axis;
if (std::abs(sf) > EPSILON
&& print_object.print()->config().belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) {
tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) {
if (!layer || layer->polygons.empty())
return;
double cutoff = (layer->print_z - z_shift - floor_off) / sf;
coord_t cutoff_sc = scale_(cutoff);
coord_t big = scale_(1e4);
Polygon belt_poly;
if (from_axis == 0) {
if (sf > 0)
belt_poly.points = {{cutoff_sc,-big},{big,-big},{big,big},{cutoff_sc,big}};
else
belt_poly.points = {{-big,-big},{cutoff_sc,-big},{cutoff_sc,big},{-big,big}};
} else {
if (sf > 0)
belt_poly.points = {{-big,cutoff_sc},{big,cutoff_sc},{big,big},{-big,big}};
else
belt_poly.points = {{-big,-big},{big,-big},{big,cutoff_sc},{-big,cutoff_sc}};
}
layer->polygons = diff(layer->polygons, Polygons{belt_poly});
});
}
}
print.set_status(69, _L("Generating support"));
generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
@@ -3816,6 +3883,10 @@ void organic_draw_branches(
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i) {
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
slices[i] = intersection(slices[i], volumes.m_bed_area);
// Belt floor: clip branch slices against the belt surface plane.
LayerIndex belt_idx = layer_begin + i;
if (belt_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[belt_idx].empty())
slices[i] = diff(slices[i], volumes.m_belt_floor[belt_idx]);
}
size_t num_empty = 0;
if (slices.front().empty()) {
@@ -3850,6 +3921,9 @@ void organic_draw_branches(
//double support_area_min = 0.1 * support_area_min_radius;
for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
// Belt floor: clip propagated support at belt surface.
if (layer_idx < LayerIndex(volumes.m_belt_floor.size()) && !volumes.m_belt_floor[layer_idx].empty())
rest_support = diff(rest_support, volumes.m_belt_floor[layer_idx]);
double rest_support_area = area(rest_support);
if (rest_support_area < support_area_stop)
// Don't propagate a fraction of the tree contact surface.