mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-14 03:11:19 +00:00
Fix custom arrange alignment outside the bed (#15733)
Auto-arrange centered the pile on `best_object_pos`, so on printers with an off-center position (A1 mini, H2C, H2D, H2D Pro, X2D) a large pile could end up partly off the bed. The final alignment now always clamps the pile to the bed, and the `clamp_to_bin` flag is removed. Belt printers already clamped, and centered alignment is unchanged. The check against fixed parts and excluded regions now tests the position the pile is actually moved to. Fixes #15727
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -300,12 +300,10 @@ template<class PConf>
|
||||
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
|
||||
|
||||
@@ -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<coord_t>::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<coord_t>::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<double>(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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user