diff --git a/deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 62d231e375..ba015b0faa 100644 --- a/deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -88,18 +88,6 @@ struct NfpPConfig { */ bool explore_holes = false; - /** - * @brief Keep the final pile on the bin. - * - * The final alignment centres the pile on the alignment target. A target - * near an edge (a belt printer starts its parts at the leading end of the - * belt) would push part of a pile that is larger than the room around that - * point off the bed; with this set the pile stops at the edge instead, and a - * pile that does not fit along an axis is centred on it. Off by default, so - * the alignment of every other printer is unchanged. - */ - bool clamp_to_bin = false; - /** * @brief If true, use all CPUs available. Run on a single core otherwise. */ @@ -1125,22 +1113,22 @@ private: auto d = cb - ci; - // Keep the pile on the bin (see Config::clamp_to_bin). The items' boxes carry - // their inflation, which is the margin left at the edge. - if (config_.clamp_to_bin) { - auto on_bin = [](Coord lo, Coord hi, Coord bin_lo, Coord bin_hi, Coord shift) { - if (hi - lo >= bin_hi - bin_lo) - return (bin_lo + bin_hi) / 2 - (lo + hi) / 2; - if (lo + shift < bin_lo) - shift = bin_lo - lo; - if (hi + shift > bin_hi) - shift = bin_hi - hi; - return shift; - }; - setX(d, on_bin(getX(bb.minCorner()), getX(bb.maxCorner()), getX(bbin.minCorner()), getX(bbin.maxCorner()), getX(d))); - setY(d, on_bin(getY(bb.minCorner()), getY(bb.maxCorner()), getY(bbin.minCorner()), getY(bbin.maxCorner()), getY(d))); - cb = ci + d; - } + // Keep the pile on the bin. Centring it on a target near an edge (a belt + // printer starts its parts at the leading end of the belt) would push a pile + // larger than the room around that point off the bed, so it stops at the + // edge instead; a pile that does not fit along an axis is centred on it. + // The items' boxes carry their inflation, which is the margin left at the edge. + auto on_bin = [](Coord lo, Coord hi, Coord bin_lo, Coord bin_hi, Coord shift) { + if (hi - lo >= bin_hi - bin_lo) + return (bin_lo + bin_hi) / 2 - (lo + hi) / 2; + if (lo + shift < bin_lo) + shift = bin_lo - lo; + if (hi + shift > bin_hi) + shift = bin_hi - hi; + return shift; + }; + setX(d, on_bin(getX(bb.minCorner()), getX(bb.maxCorner()), getX(bbin.minCorner()), getX(bbin.maxCorner()), getX(d))); + setY(d, on_bin(getY(bb.minCorner()), getY(bb.maxCorner()), getY(bbin.minCorner()), getY(bbin.maxCorner()), getY(d))); // BBS make sure the item won't clash with excluded regions // do we have wipe tower after arranging? @@ -1169,9 +1157,7 @@ private: return; } Item objs_convex_hull_item(objs_convex_hull); - Vertex objs_convex_hull_ref = objs_convex_hull_item.referenceVertex(); - Vertex diff = objs_convex_hull_ref - sl::boundingBox(objs_convex_hull).center(); - Vertex ref_aligned = cb + diff; // reference point when pile center aligned with bed center + Vertex ref_aligned = objs_convex_hull_item.referenceVertex() + d; bool ref_aligned_is_ok = std::any_of(nfps.begin(), nfps.end(), [&ref_aligned](auto& nfp) {return sl::isInside(ref_aligned, nfp); }); if (!ref_aligned_is_ok) { // ref_aligned is not good, then find a nearest point on nfp boundary diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index eacb122d05..50b20bacd1 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -300,12 +300,10 @@ template void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { if (params.is_belt) { - // Pack from the end of the belt that prints first, and keep the pile on the - // bed when it is larger than the room around that end. + // Pack from the end of the belt that prints first. pcfg.starting_point = !params.belt_reversed ? PConf::Alignment::BOTTOM_LEFT : params.belt_axis == 1 ? PConf::Alignment::TOP_LEFT : PConf::Alignment::BOTTOM_RIGHT; - pcfg.clamp_to_bin = true; } else if (params.is_seq_print) { // Start placing the items from the center of the print bed diff --git a/tests/libslic3r/test_arrange.cpp b/tests/libslic3r/test_arrange.cpp index f4dad02808..030dd9e4b6 100644 --- a/tests/libslic3r/test_arrange.cpp +++ b/tests/libslic3r/test_arrange.cpp @@ -261,17 +261,67 @@ TEST_CASE("Arrange without final alignment keeps items disjoint", "[Arrange]") require_no_overlap(items); } -TEST_CASE("Arrange aligns the pile to a custom center", "[Arrange]") +TEST_CASE("Arrange centers a pile that fits on the custom alignment point", "[Arrange]") { - // align_center != (0.5, 0.5) selects Alignment::USER_DEFINED. - ArrangePolygons items = squares(5, 30.); + ArrangePolygons items = squares(4, 30.); ArrangeParams params = quiet_params(scaled(2.)); params.align_center = Vec2d(0.3, 0.7); arrange(items, bed(250, 250), params); - for (const ArrangePolygon &ap : items) + BoundingBox pile; + for (const ArrangePolygon &ap : items) { REQUIRE(ap.bed_idx == 0); + pile.merge(ap.transformed_poly().contour.bounding_box()); + } + // best_object_pos is align_center scaled to the bed. + const Point expected(scaled(250. * 0.3), scaled(250. * 0.7)); + REQUIRE(std::abs(pile.center().x() - expected.x()) <= scaled(0.5)); + REQUIRE(std::abs(pile.center().y() - expected.y()) <= scaled(0.5)); + require_no_overlap(items); +} + +// A row of four 40 mm parts centred on the 30% mark of a 200 x 50 mm bed would start 24 mm +// left of it (#15727). The pile stops at the left edge instead. +TEST_CASE("Arrange keeps a custom-aligned pile within the bed", "[Arrange]") +{ + const BoundingBox bed_ = bed(200, 50); + ArrangePolygons items = squares(4, 40.); + ArrangeParams params = quiet_params(scaled(2.)); + params.align_center = Vec2d(0.3, 0.5); + + arrange(items, bed_, params); + + coord_t leftmost = std::numeric_limits::max(); + for (const ArrangePolygon &ap : items) { + REQUIRE(ap.bed_idx == 0); + const BoundingBox bb = ap.transformed_poly().contour.bounding_box(); + CHECK(bed_.contains(bb)); + leftmost = std::min(leftmost, bb.min.x()); + } + // Stopped at the edge it was aimed at, not re-centred. + CHECK(leftmost < scaled(10.)); + require_no_overlap(items); +} + +// Arranging a selection leaves the unselected parts where they are, but the final alignment +// centres the box around both, so the selection does not move by its own centring. The check +// against the bed and the fixed part must test where the selection actually lands. +TEST_CASE("Arrange keeps a selection on the bed and clear of a fixed part", "[Arrange]") +{ + ArrangePolygons fixed = squares(1, 40.); + fixed.front().translation = {scaled(80.), scaled(80.)}; + ArrangePolygons items = squares(3, 40.); + for (ArrangePolygon &ap : items) + ap.inflation = scaled(1.); + + arrange(items, fixed, bed(200, 200), quiet_params()); + + for (const ArrangePolygon &ap : items) { + REQUIRE(ap.bed_idx == 0); + REQUIRE(bed(200, 200).contains(ap.transformed_poly().contour.bounding_box())); + } + items.insert(items.end(), fixed.begin(), fixed.end()); require_no_overlap(items); } @@ -303,10 +353,9 @@ TEST_CASE("Arrange keeps a pile aligned near an edge on the bed", "[Arrange][bel require_no_overlap(items); } -// The clamp is a belt feature. Printers whose best_object_pos is off-centre (the A1 mini -// and the H2 family) keep their final alignment: the pile is centred on that point, even -// when that puts part of it outside the bed. -TEST_CASE("Arrange leaves the final alignment of a flat bed unclamped", "[Arrange]") +// Flat-bed printers with an off-centre best_object_pos (the A1 mini and the H2 family) get +// the same clamp: centring the pile on that point used to push it off the bed (#15727). +TEST_CASE("Arrange keeps a flat-bed pile aligned near an edge on the bed", "[Arrange]") { const BoundingBox bed_ = bed(95, 500); ArrangePolygons items = squares(4, 90.); @@ -315,14 +364,15 @@ TEST_CASE("Arrange leaves the final alignment of a flat bed unclamped", "[Arrang arrange(items, bed_, params); - BoundingBox pile; + coord_t lowest = std::numeric_limits::max(); for (const ArrangePolygon &ap : items) { REQUIRE(ap.bed_idx == 0); - pile.merge(ap.transformed_poly().contour.bounding_box()); + const BoundingBox bb = ap.transformed_poly().contour.bounding_box(); + CHECK(bed_.contains(bb)); + lowest = std::min(lowest, bb.min.y()); } - // Centred on the 5% mark of the bed's length, not pushed inside it. - CHECK_THAT(unscaled(pile.center().y()), Catch::Matchers::WithinAbs(0.05 * 500., 15.)); - CHECK(pile.min.y() < 0); + // Stopped at the edge it was aimed at, not re-centred. + CHECK(lowest < scaled(10.)); require_no_overlap(items); }