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 <noreply@anthropic.com>
This commit is contained in:
harrierpigeon
2026-10-07 00:10:29 -05:00
co-authored by Claude Fable 5.1
parent dda58b07cd
commit a2f5a8ce2b
2 changed files with 34 additions and 1 deletions
+18 -1
View File
@@ -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;
+16
View File
@@ -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<double>::max(), hi = std::numeric_limits<double>::lowest();
for (const stl_vertex &v : mesh.its.vertices) {
lo = std::min<double>(lo, v.y() + v.z());
hi = std::max<double>(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()));