From a2f5a8ce2bb4bfd9e3089cc51b1fac632c9aad51 Mon Sep 17 00:00:00 2001 From: harrierpigeon Date: Wed, 7 Oct 2026 00:10:24 -0500 Subject: [PATCH] Belt printer: size the layer range from the on-bed box The belt object height is estimated from a bounding box swept through the tilt rotation. raw_bounding_box() has the instance's Z offset removed, which did not matter while the estimate was the box's rotated Z extent (a Z shift moves every corner alike), but the frame now starts at the lowest belt-floor point under the footprint, and a point's rotated z and the floor under it move in opposite directions under a Z shift: the offset box under-estimated the height by twice the object's height above the bed, so the layers stopped at the part's diagonal and every part came out as a wedge (GUI and CLI alike; the unit tests never checked the top). Use the box of the mesh in the frame it is sliced in (trafo_centered(), Z as placed on the bed), and have the leading overhang test check that the whole part is sliced. Co-Authored-By: Claude Fable 5.1 --- src/libslic3r/PrintObject.cpp | 19 ++++++++++++++++++- tests/fff_print/test_print.cpp | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 583a72766c..4f46921c3f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4141,7 +4141,17 @@ void PrintObject::update_slicing_parameters() BeltTransformPipeline::BeltFloorParams belt_floor; const auto &pcfg = this->print()->config(); if (pcfg.belt_printer.value) { - const BoundingBoxf3 bb = this->model_object()->raw_bounding_box(); + // The box of the mesh in the frame it is sliced in: XY centred and Z as + // placed on the bed (trafo_centered()). raw_bounding_box() has the + // instance's Z offset removed, and the belt floor is not invariant to a + // Z shift (a point's z and the floor under it move in opposite + // directions under the rotation), so an offset box under-estimates the + // height by twice the shift and the layers stop part way up the object. + BoundingBoxf3 bb; + const Transform3d trafo = this->trafo_centered(); + for (const ModelVolume *v : this->model_object()->volumes) + if (v->is_model_part()) + bb.merge(v->mesh().transformed_bounding_box(trafo * v->get_matrix())); auto hr = BeltTransformPipeline::compute_belt_height_and_floor(pcfg, bb, object_height); object_height = hr.object_height; belt_floor = hr.floor_params; @@ -4202,6 +4212,13 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full BoundingBoxf3 bb = model_object.raw_bounding_box(); object_max_z = (float)bb.size().z(); if (print_config.belt_printer.value) { + // Z as placed on the bed, XY around the instance origin: the belt floor + // depends on where the box sits in Z (see update_slicing_parameters()). + if (! model_object.instances.empty()) { + bb = model_object.instance_bounding_box(0, false); + const Vec3d off = model_object.instances.front()->get_offset(); + bb.translate(-off.x(), -off.y(), 0.); + } auto hr = BeltTransformPipeline::compute_belt_height_and_floor(print_config, bb, object_max_z); object_max_z = (float)hr.object_height; belt_floor = hr.floor_params; diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index 57232d5a11..be5311fd7b 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -1305,6 +1305,22 @@ TEST_CASE("Belt supports reach the belt under a leading overhang", "[Print][belt const PrintObject &object = *print.objects().front(); REQUIRE(! object.layers().empty()); + // The whole part is sliced: the layers lean at 45 deg, so the part spans + // (y + z) / sqrt(2) of slicing Z, and every layer in that span has geometry. + { + double lo = std::numeric_limits::max(), hi = std::numeric_limits::lowest(); + for (const stl_vertex &v : mesh.its.vertices) { + lo = std::min(lo, v.y() + v.z()); + hi = std::max(hi, v.y() + v.z()); + } + const double span = (hi - lo) / std::sqrt(2.); + size_t nonempty = 0; + for (const Layer *layer : object.layers()) + if (! layer->lslices.empty()) + ++ nonempty; + INFO("non-empty object layers " << nonempty << ", slicing span " << span << " mm"); + CHECK(double(nonempty) * 0.2 > span - 0.6); + } BeltFloorContext floor; REQUIRE(floor.init(object.slicing_parameters(), print.config()));