Fix implementation of inner seam offsetting.

This commit is contained in:
Martin Šach 2024-09-19 15:36:52 +02:00 committed by Lukas Matena
parent 0d9b079e4b
commit 5033d18824
8 changed files with 133 additions and 51 deletions

View File

@ -441,7 +441,7 @@ Polygon to_polygon(const ExtrusionLoop &loop) {
return Polygon{loop_points};
}
std::optional<PointOnLine> offset_along_loop_lines(
std::optional<PointOnLine> offset_along_lines(
const Vec2d &point,
const std::size_t loop_line_index,
const Linesf &loop_lines,

View File

@ -202,7 +202,7 @@ struct PointOnLine{
std::size_t line_index;
};
std::optional<PointOnLine> offset_along_loop_lines(
std::optional<PointOnLine> offset_along_lines(
const Vec2d &point,
const std::size_t loop_line_index,
const Linesf &loop_lines,

View File

@ -196,10 +196,10 @@ const SeamPerimeterChoice &choose_closest_seam(
}
std::pair<std::size_t, Vec2d> project_to_extrusion_loop(
const SeamChoice &seam_choice, const Perimeters::Perimeter &perimeter, const Linesf &loop_lines
const SeamChoice &seam_choice,
const Perimeters::Perimeter &perimeter,
const AABBTreeLines::LinesDistancer<Linef> &distancer
) {
const AABBTreeLines::LinesDistancer<Linef> distancer{loop_lines};
const bool is_at_vertex{seam_choice.previous_index == seam_choice.next_index};
const Vec2d edge{
perimeter.positions[seam_choice.next_index] -
@ -250,9 +250,10 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
const ExPolygon perimeter_polygon{Geometry::scaled(perimeter.positions)};
const Linesf perimeter_lines{to_unscaled_linesf({perimeter_polygon})};
const Linesf loop_lines{to_unscaled_linesf({ExPolygon{loop_polygon}})};
const AABBTreeLines::LinesDistancer<Linef> distancer{loop_lines};
auto [loop_line_index, loop_point]{
project_to_extrusion_loop(seam_choice, perimeter, loop_lines)};
project_to_extrusion_loop(seam_choice, perimeter, distancer)};
const Geometry::Direction1D offset_direction{
flipped ? Geometry::Direction1D::forward : Geometry::Direction1D::backward};
@ -264,14 +265,14 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
const double staggering_offset{depth};
std::optional<Geometry::PointOnLine> staggered_point{Geometry::offset_along_loop_lines(
std::optional<Geometry::PointOnLine> staggered_point{Geometry::offset_along_lines(
loop_point, seam_choice.previous_index, perimeter_lines, staggering_offset,
offset_direction
)};
if (staggered_point) {
seam_choice = to_seam_choice(*staggered_point, perimeter);
std::tie(loop_line_index, loop_point) = project_to_extrusion_loop(seam_choice, perimeter, loop_lines);
std::tie(loop_line_index, loop_point) = project_to_extrusion_loop(seam_choice, perimeter, distancer);
}
}
@ -290,41 +291,57 @@ boost::variant<Point, Scarf::Scarf> finalize_seam_position(
if (place_scarf_seam) {
Scarf::Scarf scarf{};
scarf.length = region->config().scarf_seam_length;
scarf.entire_loop = region->config().scarf_seam_entire_loop;
scarf.max_segment_length = region->config().scarf_seam_max_segment_length;
scarf.start_height = region->config().scarf_seam_start_height.get_abs_value(1.0);
if (loop.role() == ExtrusionRole::Perimeter) { // Inner perimeter
const double offset{scarf.entire_loop ? 0 : scarf.length};
const ExPolygons shrank_polygons{shrink_ex({perimeter_polygon}, scaled(loop_width / 2.0))};
if (shrank_polygons.empty()) {
return scaled(loop_point);
}
const std::optional<Geometry::PointOnLine> outter_scarf_start_point{Geometry::offset_along_loop_lines(
seam_choice.position, seam_choice.previous_index, to_unscaled_linesf({shrank_polygons.front()}), offset, offset_direction
)};
if (!outter_scarf_start_point) {
return scaled(loop_point);
}
const auto [end_point_previous_index, end_point]{project_to_extrusion_loop(
to_seam_choice(*outter_scarf_start_point, perimeter), perimeter, loop_lines
)};
if (!region->config().scarf_seam_on_inner_perimeters) {
return scaled(end_point);
}
scarf.end_point = scaled(end_point);
scarf.end_point_previous_index = end_point_previous_index;
} else { // Outter perimeter
scarf.end_point = scaled(loop_point);
scarf.end_point_previous_index = loop_line_index;
const double offset{scarf.entire_loop ? 0.0 : region->config().scarf_seam_length.value};
const std::optional<Geometry::PointOnLine> outter_scarf_start_point{Geometry::offset_along_lines(
seam_choice.position,
seam_choice.previous_index,
perimeter_lines,
offset,
offset_direction
)};
if (!outter_scarf_start_point) {
return scaled(loop_point);
}
return scarf;
if (loop.role() != ExtrusionRole::Perimeter) { // Outter perimeter
scarf.start_point = scaled(project_to_extrusion_loop(
to_seam_choice(*outter_scarf_start_point, perimeter),
perimeter,
distancer
).second);
scarf.end_point = scaled(loop_point);
scarf.end_point_previous_index = loop_line_index;
return scarf;
} else {
if (!region->config().scarf_seam_on_inner_perimeters) {
return scaled(outter_scarf_start_point->point);
}
const std::optional<Geometry::PointOnLine> inner_scarf_start_point{Geometry::offset_along_lines(
outter_scarf_start_point->point,
outter_scarf_start_point->line_index,
perimeter_lines,
offset,
offset_direction
)};
if (!inner_scarf_start_point) {
return scaled(outter_scarf_start_point->point);
}
scarf.start_point = scaled(project_to_extrusion_loop(
to_seam_choice(*inner_scarf_start_point, perimeter),
perimeter,
distancer
).second);
scarf.end_point = scaled(outter_scarf_start_point->point);
scarf.end_point_previous_index = outter_scarf_start_point->line_index;
return scarf;
}
}
return scaled(loop_point);

View File

@ -229,6 +229,41 @@ GCode::SmoothPath elevate_scarf(
return first_segment;
}
bool is_on_line(const Point &point, const Line &line, const double tolerance) {
return line.distance_to_squared(point) < tolerance * tolerance;
}
std::optional<PathPoint> find_path_point_from_end(
const ExtrusionPaths &paths,
const Point &point,
const double tolerance
) {
if (paths.empty()) {
return std::nullopt;
}
for (int path_index{static_cast<int>(paths.size() - 1)}; path_index >= 0; --path_index) {
const ExtrusionPath &path{paths[path_index]};
if (path.polyline.size() < 2) {
throw std::runtime_error(
"Invalid path: less than two points: " + std::to_string(path.size()) + "!"
);
}
for (int point_index{static_cast<int>(path.polyline.size() - 2)}; point_index >= 0;
--point_index) {
const Point &previous_point{path.polyline[point_index + 1]};
const Point &current_point{path.polyline[point_index]};
const Line line{previous_point, current_point};
if (is_on_line(point, line, tolerance)) {
return PathPoint{
point,
static_cast<size_t>(path_index), static_cast<size_t>(point_index)
};
}
}
}
return std::nullopt;
}
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length) {
double distance{0.0};
@ -307,7 +342,8 @@ std::pair<GCode::SmoothPath, std::size_t> add_scarf_seam(
std::optional<Impl::PathPoint> start_point;
if (!scarf.entire_loop) {
start_point = Impl::get_point_offset_from_end(paths, scaled(scarf.length));
const double tolerance{scaled(1e-2 /* mm */)};
start_point = Impl::find_path_point_from_end(paths, scarf.start_point, tolerance);
}
if (!start_point) {
start_point = Impl::PathPoint{

View File

@ -13,12 +13,12 @@ namespace Slic3r::Seams::Scarf {
struct Scarf
{
Point start_point;
Point end_point;
std::size_t end_point_previous_index{};
double length{};
double max_segment_length{};
bool entire_loop{};
double start_height;
double start_height{};
};
using SmoothingFunction = std::function<GCode::SmoothPath(tcb::span<const ExtrusionPath>)>;
@ -72,6 +72,12 @@ GCode::SmoothPath elevate_scarf(
);
std::optional<PathPoint> get_point_offset_from_end(const ExtrusionPaths &paths, const double length);
std::optional<PathPoint> find_path_point_from_end(
const ExtrusionPaths &paths,
const Point &point,
const double tolerance
);
} // namespace Impl
std::pair<GCode::SmoothPath, std::size_t> add_scarf_seam(

View File

@ -152,7 +152,7 @@ const Linesf lines{to_unscaled_linesf({ExPolygon{
}})};
TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_loop_lines(
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
{0.5, 0.0}, 0, lines, 3.9, Seams::Geometry::Direction1D::forward
)};
REQUIRE(result);
@ -162,7 +162,7 @@ TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") {
}
TEST_CASE("Offset along loop lines backward", "[Seams][SeamGeometry]") {
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_loop_lines(
const std::optional<Seams::Geometry::PointOnLine> result{Seams::Geometry::offset_along_lines(
{1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward
)};
REQUIRE(result);

View File

@ -231,6 +231,32 @@ TEST_CASE("Get point offset from the path end", "[Seams][Scarf]") {
CHECK(result->path_index == 1);
}
TEST_CASE("Find point on path from the path end", "[Seams][Scarf]") {
using Seams::Scarf::Impl::get_point_offset_from_end;
const Points points{
Point::new_scale(0, 0),
Point::new_scale(1, 0),
Point::new_scale(2, 0),
Point::new_scale(3, 0),
Point::new_scale(4, 0),
};
const ExtrusionPaths paths{
{{points[0], points[1]}, {}},
{{points[1], points[2]}, {}},
{{points[2], points[3], points[4]}, {}},
};
const auto point{Point::new_scale(3.4, 0)};
std::optional<PathPoint> result{Seams::Scarf::Impl::find_path_point_from_end(paths, point, scaled(1e-2))};
REQUIRE(result);
CHECK(result->point == point);
CHECK(result->previous_point_on_path_index == 1);
CHECK(result->path_index == 2);
}
TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
using Seams::Scarf::add_scarf_seam;
using Seams::Scarf::Impl::convert_to_smooth;
@ -247,29 +273,28 @@ TEST_CASE("Add scarf seam", "[Seams][Scarf]") {
const ExtrusionPaths paths{
{Polyline{points}, {}},
};
Scarf scarf{};
scarf.start_point = Point::new_scale(0.5, 0);
scarf.end_point = Point::new_scale(1, 0.5);
scarf.start_height = 0.2;
scarf.length = 1.0;
scarf.max_segment_length = 0.1;
scarf.end_point_previous_index = 1;
scarf.entire_loop = false;
const auto start_point{Point::new_scale(0.5, 0)};
const auto [path, wipe_offset]{add_scarf_seam(ExtrusionPaths{paths}, scarf, convert_to_smooth, false)};
REQUIRE(path.size() == 4);
CHECK(wipe_offset == 1);
REQUIRE(path.back().path.size() >= scarf.length / scarf.max_segment_length);
REQUIRE(path.back().path.size() >= 1.0 / scarf.max_segment_length);
CHECK(path.back().path.back().point == scarf.end_point);
CHECK(path.back().path.front().point == start_point);
CHECK(path.back().path.front().point == scarf.start_point);
CHECK(path.back().path.back().e_fraction == Approx(0));
REQUIRE(path.front().path.size() >= scarf.length / scarf.max_segment_length);
REQUIRE(path.front().path.size() >= 1.0 / scarf.max_segment_length);
CHECK(path.front().path.back().point == scarf.end_point);
CHECK(path.front().path.front().point == start_point);
CHECK(path.front().path.front().point == scarf.start_point);
CHECK(path.front().path.front().e_fraction == Approx(0));
CHECK(path.front().path.front().height_fraction == Approx(scarf.start_height));

View File

@ -10,8 +10,6 @@
using namespace Slic3r;
using namespace Slic3r::Seams;
constexpr bool debug_files{false};
struct ProjectionFixture
{
Polygon extrusion_path{