diff --git a/src/libslic3r/GCode/SeamGeometry.cpp b/src/libslic3r/GCode/SeamGeometry.cpp index d87fb49130..47a9d602f2 100644 --- a/src/libslic3r/GCode/SeamGeometry.cpp +++ b/src/libslic3r/GCode/SeamGeometry.cpp @@ -441,7 +441,7 @@ Polygon to_polygon(const ExtrusionLoop &loop) { return Polygon{loop_points}; } -std::optional offset_along_loop_lines( +std::optional offset_along_lines( const Vec2d &point, const std::size_t loop_line_index, const Linesf &loop_lines, diff --git a/src/libslic3r/GCode/SeamGeometry.hpp b/src/libslic3r/GCode/SeamGeometry.hpp index 2eff2dae89..9b5c1c47ce 100644 --- a/src/libslic3r/GCode/SeamGeometry.hpp +++ b/src/libslic3r/GCode/SeamGeometry.hpp @@ -202,7 +202,7 @@ struct PointOnLine{ std::size_t line_index; }; -std::optional offset_along_loop_lines( +std::optional offset_along_lines( const Vec2d &point, const std::size_t loop_line_index, const Linesf &loop_lines, diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 341093b3b7..91f778849a 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -196,10 +196,10 @@ const SeamPerimeterChoice &choose_closest_seam( } std::pair 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 &distancer ) { - const AABBTreeLines::LinesDistancer 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 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 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 finalize_seam_position( const double staggering_offset{depth}; - std::optional staggered_point{Geometry::offset_along_loop_lines( + std::optional 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 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 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 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 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); diff --git a/src/libslic3r/GCode/SeamScarf.cpp b/src/libslic3r/GCode/SeamScarf.cpp index babbd369b8..5e606f2090 100644 --- a/src/libslic3r/GCode/SeamScarf.cpp +++ b/src/libslic3r/GCode/SeamScarf.cpp @@ -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 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(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(path.polyline.size() - 2)}; point_index >= 0; + --point_index) { + const Point &previous_point{path.polyline[point_index + 1]}; + const Point ¤t_point{path.polyline[point_index]}; + const Line line{previous_point, current_point}; + if (is_on_line(point, line, tolerance)) { + return PathPoint{ + point, + static_cast(path_index), static_cast(point_index) + }; + } + } + } + return std::nullopt; +} + std::optional get_point_offset_from_end(const ExtrusionPaths &paths, const double length) { double distance{0.0}; @@ -307,7 +342,8 @@ std::pair add_scarf_seam( std::optional 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{ diff --git a/src/libslic3r/GCode/SeamScarf.hpp b/src/libslic3r/GCode/SeamScarf.hpp index 6cf03d6ee8..c014e929fb 100644 --- a/src/libslic3r/GCode/SeamScarf.hpp +++ b/src/libslic3r/GCode/SeamScarf.hpp @@ -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)>; @@ -72,6 +72,12 @@ GCode::SmoothPath elevate_scarf( ); std::optional get_point_offset_from_end(const ExtrusionPaths &paths, const double length); + +std::optional find_path_point_from_end( + const ExtrusionPaths &paths, + const Point &point, + const double tolerance +); } // namespace Impl std::pair add_scarf_seam( diff --git a/tests/fff_print/test_seam_geometry.cpp b/tests/fff_print/test_seam_geometry.cpp index 76ff39d536..67dcc3e3c3 100644 --- a/tests/fff_print/test_seam_geometry.cpp +++ b/tests/fff_print/test_seam_geometry.cpp @@ -152,7 +152,7 @@ const Linesf lines{to_unscaled_linesf({ExPolygon{ }})}; TEST_CASE("Offset along loop lines forward", "[Seams][SeamGeometry]") { - const std::optional result{Seams::Geometry::offset_along_loop_lines( + const std::optional 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 result{Seams::Geometry::offset_along_loop_lines( + const std::optional result{Seams::Geometry::offset_along_lines( {1.0, 0.5}, 1, lines, 1.8, Seams::Geometry::Direction1D::backward )}; REQUIRE(result); diff --git a/tests/fff_print/test_seam_scarf.cpp b/tests/fff_print/test_seam_scarf.cpp index d473ae7df9..2f9b727cc8 100644 --- a/tests/fff_print/test_seam_scarf.cpp +++ b/tests/fff_print/test_seam_scarf.cpp @@ -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 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)); diff --git a/tests/fff_print/test_seam_shells.cpp b/tests/fff_print/test_seam_shells.cpp index 852b17cbe4..cbc0cfaf83 100644 --- a/tests/fff_print/test_seam_shells.cpp +++ b/tests/fff_print/test_seam_shells.cpp @@ -10,8 +10,6 @@ using namespace Slic3r; using namespace Slic3r::Seams; -constexpr bool debug_files{false}; - struct ProjectionFixture { Polygon extrusion_path{