diff --git a/src/libslic3r/GCode/SeamGeometry.cpp b/src/libslic3r/GCode/SeamGeometry.cpp index 2a90668215..a0f5403f7d 100644 --- a/src/libslic3r/GCode/SeamGeometry.cpp +++ b/src/libslic3r/GCode/SeamGeometry.cpp @@ -127,9 +127,16 @@ std::pair get_mapping( return {result, new_bucket_id}; } -Extrusion::Extrusion(BoundingBox bounding_box, const double width, const ExPolygon &island_boundary) - : bounding_box(std::move(bounding_box)), width(width), island_boundary(island_boundary) { - +Extrusion::Extrusion( + Polygon &&polygon, + BoundingBox bounding_box, + const double width, + const ExPolygon &island_boundary +) + : polygon(polygon) + , bounding_box(std::move(bounding_box)) + , width(width) + , island_boundary(island_boundary) { this->island_boundary_bounding_boxes.push_back(island_boundary.contour.bounding_box()); std::transform( @@ -149,9 +156,10 @@ Geometry::Extrusions get_external_perimeters(const Slic3r::Layer &layer, const L )}; for (const ExtrusionEntity *entity : *collection) { if (entity->role().is_external_perimeter()) { - const BoundingBox bounding_box{entity->as_polyline().points}; + Polygon polygon{entity->as_polyline().points}; + const BoundingBox bounding_box{polygon.bounding_box()}; const double width{layer_region.flow(FlowRole::frExternalPerimeter).width()}; - result.emplace_back(bounding_box, width, island.boundary); + result.emplace_back(std::move(polygon), bounding_box, width, island.boundary); } } } @@ -325,6 +333,12 @@ std::vector get_vertex_angles(const std::vector &points, const do return result; } +double bounding_box_distance(const BoundingBox &a, const BoundingBox &b) { + const double bb_max_distance{unscaled(Point{a.max - b.max}).norm()}; + const double bb_min_distance{unscaled(Point{a.min - b.min}).norm()}; + return std::max(bb_max_distance, bb_min_distance); +} + std::pair pick_closest_bounding_box( const BoundingBox &to, const BoundingBoxes &choose_from ) { @@ -333,9 +347,7 @@ std::pair pick_closest_bounding_box( for (std::size_t i{0}; i < choose_from.size(); ++i) { const BoundingBox &candidate{choose_from[i]}; - const double bb_max_distance{unscaled(Point{candidate.max - to.max}).norm()}; - const double bb_min_distance{unscaled(Point{candidate.min - to.min}).norm()}; - const double distance{std::max(bb_max_distance, bb_min_distance)}; + const double distance{bounding_box_distance(candidate, to)}; if (distance < min_distance) { choosen_index = i; diff --git a/src/libslic3r/GCode/SeamGeometry.hpp b/src/libslic3r/GCode/SeamGeometry.hpp index 219fd1a4fe..fd43854502 100644 --- a/src/libslic3r/GCode/SeamGeometry.hpp +++ b/src/libslic3r/GCode/SeamGeometry.hpp @@ -18,13 +18,19 @@ namespace Slic3r::Seams::Geometry { struct Extrusion { - Extrusion(BoundingBox bounding_box, const double width, const ExPolygon &island_boundary); + Extrusion( + Polygon &&polygon, + BoundingBox bounding_box, + const double width, + const ExPolygon &island_boundary + ); Extrusion(const Extrusion &) = delete; Extrusion(Extrusion &&) = default; Extrusion &operator=(const Extrusion &) = delete; Extrusion &operator=(Extrusion &&) = delete; + Polygon polygon; BoundingBox bounding_box; double width; const ExPolygon &island_boundary; @@ -151,6 +157,8 @@ std::vector get_overhangs( // Measured from outside, convex is positive std::vector get_vertex_angles(const std::vector &points, const double min_arm_length); +double bounding_box_distance(const BoundingBox &a, const BoundingBox &b); + std::pair pick_closest_bounding_box( const BoundingBox &to, const BoundingBoxes &choose_from ); diff --git a/src/libslic3r/GCode/SeamShells.cpp b/src/libslic3r/GCode/SeamShells.cpp index 6cf5f328d4..65e9149c82 100644 --- a/src/libslic3r/GCode/SeamShells.cpp +++ b/src/libslic3r/GCode/SeamShells.cpp @@ -5,7 +5,7 @@ namespace Slic3r::Seams::Shells::Impl { -BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters) { +BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) { BoundedPolygons result; result.reserve(external_perimeters.size()); @@ -13,12 +13,26 @@ BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimet transform( external_perimeters.begin(), external_perimeters.end(), back_inserter(result), - [](const Geometry::Extrusion &external_perimeter) { + [&](const Geometry::Extrusion &external_perimeter) { const auto [choosen_index, _]{Geometry::pick_closest_bounding_box( external_perimeter.bounding_box, external_perimeter.island_boundary_bounding_boxes )}; + const double distance{Geometry::bounding_box_distance( + external_perimeter.island_boundary_bounding_boxes[choosen_index], + external_perimeter.bounding_box + )}; + + if (distance > max_bb_distance) { + Polygons expanded_extrusion{expand(external_perimeter.polygon, external_perimeter.width / 2.0)}; + if (!expanded_extrusion.empty()) { + return BoundedPolygon{ + expanded_extrusion.front(), expanded_extrusion.front().bounding_box(), external_perimeter.polygon.is_clockwise() + }; + } + } + const bool is_hole{choosen_index != 0}; const Polygon &adjacent_boundary{ !is_hole ? external_perimeter.island_boundary.contour : @@ -29,11 +43,11 @@ BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimet return result; } -std::vector project_to_geometry(const std::vector &extrusions) { +std::vector project_to_geometry(const std::vector &extrusions, const double max_bb_distance) { std::vector result(extrusions.size()); for (std::size_t layer_index{0}; layer_index < extrusions.size(); ++layer_index) { - result[layer_index] = project_to_geometry(extrusions[layer_index]); + result[layer_index] = project_to_geometry(extrusions[layer_index], max_bb_distance); } return result; @@ -61,7 +75,7 @@ namespace Slic3r::Seams::Shells { Shells create_shells( const std::vector &extrusions, const double max_distance ) { - std::vector projected{Impl::project_to_geometry(extrusions)}; + std::vector projected{Impl::project_to_geometry(extrusions, max_distance)}; std::vector layer_sizes; layer_sizes.reserve(projected.size()); diff --git a/src/libslic3r/GCode/SeamShells.hpp b/src/libslic3r/GCode/SeamShells.hpp index 4d775865a4..ea81053506 100644 --- a/src/libslic3r/GCode/SeamShells.hpp +++ b/src/libslic3r/GCode/SeamShells.hpp @@ -25,21 +25,7 @@ struct BoundedPolygon { using BoundedPolygons = std::vector; -/** - * Project extrusion path to the original mesh. - * - * Takes the extrusion path and finds the closest polygon to it in - * the extruison island boundary. - * - * Then it expands the extrusion path so it roughly tracks the island boundary - * and check that all points in expanded extrusion path are within a reasonable - * distance (extrusion width) from the closes polygon. - * - * If the expanded extrusion path matches the boundary it returns the - * closeset polygon from the island boundary. Otherwise it returns - * the expanded extrusion. - */ -BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions); +BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions, const double max_bb_distance); } namespace Slic3r::Seams::Shells { diff --git a/tests/fff_print/test_seam_shells.cpp b/tests/fff_print/test_seam_shells.cpp index abbd4f3c45..b514304fbc 100644 --- a/tests/fff_print/test_seam_shells.cpp +++ b/tests/fff_print/test_seam_shells.cpp @@ -23,7 +23,7 @@ struct ProjectionFixture double extrusion_width{0.2}; ProjectionFixture() { - extrusions.emplace_back(extrusion_path.bounding_box(), extrusion_width, island_boundary); + extrusions.emplace_back(Polygon{extrusion_path}, extrusion_path.bounding_box(), extrusion_width, island_boundary); } }; @@ -33,13 +33,31 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry matches", "[Seams][Seam boundary_polygon.scale(1.0 + extrusion_width / 2.0 + 0.1); island_boundary.contour = boundary_polygon; - Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions)}; + Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 5.0)}; REQUIRE(result.size() == 1); REQUIRE(result[0].polygon.size() == 4); // Boundary polygon is picked. CHECK(result[0].polygon[0].x() == Approx(scaled(-(1.0 + extrusion_width / 2.0 + 0.1)))); } +TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seams][SeamShells]") { + Polygon boundary_polygon{extrusion_path}; + + // Island boundary is far from the extrusion. + boundary_polygon.scale(5.0); + + island_boundary.contour = boundary_polygon; + + Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 1.0)}; + REQUIRE(result.size() == 1); + REQUIRE(result[0].polygon.size() == 4); + + const Polygon expanded{expand(extrusions.front().polygon, extrusion_width / 2.0).front()}; + + // The extrusion is expanded and returned. + CHECK(result[0].polygon == expanded); +} + void serialize_shells( std::ostream &out, const Shells::Shells &shells, const double layer_height ) { @@ -69,5 +87,5 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Create shells", "[Seams][SeamShells][Integ serialize_shells(csv, shell_polygons, print->full_print_config().opt_float("layer_height")); } - CHECK(shell_polygons.size() == 39); + CHECK(shell_polygons.size() == 36); }