mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-07 01:57:38 +00:00
Keep the prime tower and its approach travel on non-rectangular beds
The placement clamps and the tower-approach router both stood in the bed's bounding box for the bed itself, so on a delta or hexagonal bed the prime tower could be parked in a corner that does not exist and the nozzle could be routed across it. Both now test the real printable outline, slicing reports a tower that does not fit instead of printing it off the bed, and a tower parked near an edge is routed along the clamped side rather than falling back to a straight line across the tower. Also fixes the placement validation rotating the tower hull by degrees read as radians about the plate origin, and never rotating the generated tower footprint at all.
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "libslic3r/GCode/WipeTower.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
@@ -53,6 +55,65 @@ TEST_CASE("Other flavors wait in the wipe tower with a seconds dwell", "[WipeTow
|
||||
CHECK(wait_command(flavor, 1.5f) == "G4 S1.500\n");
|
||||
}
|
||||
|
||||
// The prime tower is validated against the real printable outline, so the placement clamps have to
|
||||
// agree with it wherever that outline is not a rectangle. A regular hexagon inscribed in a 200mm
|
||||
// circle stands in for the shipped delta beds.
|
||||
TEST_CASE("The wipe tower placement clamp follows a non-rectangular bed outline", "[WipeTower]")
|
||||
{
|
||||
const coord_t margin = scaled<coord_t>(1.);
|
||||
auto square_at = [](double x, double y, double side) {
|
||||
return BoundingBox(Point::new_scale(x, y), Point::new_scale(x + side, y + side));
|
||||
};
|
||||
// Does the footprint, padded by pad, sit inside the outline once the returned move is applied?
|
||||
auto lands_inside = [](BoundingBox box, const Polygons &bed, const Vec2f &move, coord_t pad) {
|
||||
box.translate(Point::new_scale(move.x(), move.y()));
|
||||
return diff(Polygons{box.inflated(pad).polygon()}, bed).empty();
|
||||
};
|
||||
|
||||
const Polygons hex_bed{make_circle_num_segments(scaled<double>(100.), 6)};
|
||||
const Polygons square_bed{Polygon::new_scale(Pointfs{{0., 0.}, {200., 0.}, {200., 200.}, {0., 200.}})};
|
||||
|
||||
SECTION("a rectangular bed is left to the bounding box clamp") {
|
||||
const Vec2f move = WipeTower::move_box_inside_polygon(square_at(50., 50., 30.), square_bed, margin);
|
||||
CHECK_THAT(move.x(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
CHECK_THAT(move.y(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
}
|
||||
|
||||
// Dragging the tower off one edge may not pull it away from the other, or it would jump out from
|
||||
// under the cursor instead of sliding along the edge.
|
||||
SECTION("only the violated axis is clamped") {
|
||||
const Vec2f move = WipeTower::move_box_inside_polygon(square_at(185., 50., 30.), square_bed, margin);
|
||||
CHECK_THAT(move.x(), Catch::Matchers::WithinAbs(-16., 1e-6));
|
||||
CHECK_THAT(move.y(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
}
|
||||
|
||||
SECTION("a footprint already inside the outline is left alone") {
|
||||
const Vec2f move = WipeTower::move_box_inside_polygon(square_at(-15., -15., 30.), hex_bed, margin);
|
||||
CHECK_THAT(move.x(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
CHECK_THAT(move.y(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
}
|
||||
|
||||
SECTION("a footprint in the bounding box corner is pulled onto the bed") {
|
||||
const BoundingBox box = square_at(55., 50., 30.);
|
||||
REQUIRE_FALSE(lands_inside(box, hex_bed, Vec2f::Zero(), margin)); // in the bbox, off the hexagon
|
||||
CHECK(lands_inside(box, hex_bed, WipeTower::move_box_inside_polygon(box, hex_bed, margin), margin));
|
||||
}
|
||||
|
||||
// An unresolved auto brim width reaches the drag clamp as a negative margin. Padding by it would
|
||||
// shrink the footprint and hand back a position the slice validation still rejects.
|
||||
SECTION("a negative margin still lands the footprint inside the outline") {
|
||||
const BoundingBox box = square_at(55., 50., 30.);
|
||||
const coord_t brim = scaled<coord_t>(-0.5);
|
||||
CHECK(lands_inside(box, hex_bed, WipeTower::move_box_inside_polygon(box, hex_bed, brim), 0));
|
||||
}
|
||||
|
||||
SECTION("a footprint too large for the bed is left alone") {
|
||||
const Vec2f move = WipeTower::move_box_inside_polygon(square_at(-200., -200., 400.), hex_bed, margin);
|
||||
CHECK_THAT(move.x(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
CHECK_THAT(move.y(), Catch::Matchers::WithinAbs(0., 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
// The cases above only exercise the helpers in isolation. The one below slices a real
|
||||
// two-filament print, so it also covers the binding constraint of both changes: that the
|
||||
// configured `gcode_flavor` reaches the wipe tower writer and lands in the exported G-code.
|
||||
|
||||
Reference in New Issue
Block a user