From f6396f59e91a787e53c6214ae685289009cdb1b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C5=A0ach?= Date: Fri, 31 May 2024 17:20:09 +0200 Subject: [PATCH] Refactor seams - due to new rear implementation shells are now only required for aligned seams. --- src/libslic3r/GCode/SeamAligned.cpp | 25 ++++++- src/libslic3r/GCode/SeamChoice.cpp | 61 ----------------- src/libslic3r/GCode/SeamChoice.hpp | 17 +---- src/libslic3r/GCode/SeamGeometry.cpp | 49 ++++++++++++++ src/libslic3r/GCode/SeamGeometry.hpp | 11 ++++ src/libslic3r/GCode/SeamPerimeters.cpp | 32 +++++---- src/libslic3r/GCode/SeamPerimeters.hpp | 34 ++++------ src/libslic3r/GCode/SeamPlacer.cpp | 44 +++++-------- src/libslic3r/GCode/SeamPlacer.hpp | 3 +- src/libslic3r/GCode/SeamRandom.cpp | 30 +++++++-- src/libslic3r/GCode/SeamRandom.hpp | 2 +- src/libslic3r/GCode/SeamRear.cpp | 23 ++++++- src/libslic3r/GCode/SeamShells.cpp | 83 +++++------------------- src/libslic3r/GCode/SeamShells.hpp | 36 +++++----- tests/fff_print/benchmark_seams.cpp | 38 +++++++---- tests/fff_print/test_data.hpp | 6 +- tests/fff_print/test_seam_aligned.cpp | 22 ++++--- tests/fff_print/test_seam_perimeters.cpp | 11 ++-- tests/fff_print/test_seam_random.cpp | 13 ++-- tests/fff_print/test_seam_rear.cpp | 15 ++--- tests/fff_print/test_seam_shells.cpp | 36 +--------- 21 files changed, 271 insertions(+), 320 deletions(-) diff --git a/src/libslic3r/GCode/SeamAligned.cpp b/src/libslic3r/GCode/SeamAligned.cpp index 11148c4006..895146898c 100644 --- a/src/libslic3r/GCode/SeamAligned.cpp +++ b/src/libslic3r/GCode/SeamAligned.cpp @@ -261,6 +261,29 @@ struct SeamCandidate { std::vector visibilities; }; +std::vector get_shell_seam( + const Shells::Shell<> &shell, + const std::function &chooser +) { + std::vector result; + result.reserve(shell.size()); + for (std::size_t i{0}; i < shell.size(); ++i) { + const Shells::Slice<> &slice{shell[i]}; + if (slice.boundary.is_degenerate) { + if (std::optional seam_choice{ + choose_degenerate_seam_point(slice.boundary)}) { + result.push_back(*seam_choice); + } else { + result.emplace_back(); + } + } else { + const SeamChoice choice{chooser(slice.boundary, i)}; + result.push_back(choice); + } + } + return result; +} + SeamCandidate get_seam_candidate( const Shells::Shell<> &shell, const Vec2d &starting_position, @@ -273,7 +296,7 @@ SeamCandidate get_seam_candidate( std::vector choice_visibilities(shell.size(), 1.0); std::vector choices{ - Seams::get_shell_seam(shell, [&, previous_position{starting_position}](const Perimeter &perimeter, std::size_t slice_index) mutable { + get_shell_seam(shell, [&, previous_position{starting_position}](const Perimeter &perimeter, std::size_t slice_index) mutable { SeamChoice candidate{Seams::choose_seam_point( perimeter, Impl::Nearest{previous_position, params.max_detour} )}; diff --git a/src/libslic3r/GCode/SeamChoice.cpp b/src/libslic3r/GCode/SeamChoice.cpp index dfcb4b4b08..2a577413d8 100644 --- a/src/libslic3r/GCode/SeamChoice.cpp +++ b/src/libslic3r/GCode/SeamChoice.cpp @@ -47,65 +47,4 @@ std::optional choose_degenerate_seam_point(const Perimeters::Perimet return std::nullopt; } -std::optional> maybe_get_shell_seam( - const Shells::Shell<> &shell, - const std::function(const Perimeters::Perimeter &, std::size_t)> &chooser -) { - std::vector result; - result.reserve(shell.size()); - for (std::size_t i{0}; i < shell.size(); ++i) { - const Shells::Slice<> &slice{shell[i]}; - if (slice.boundary.is_degenerate) { - if (std::optional seam_choice{ - choose_degenerate_seam_point(slice.boundary)}) { - result.push_back(*seam_choice); - } else { - result.emplace_back(); - } - } else { - const std::optional choice{chooser(slice.boundary, i)}; - if (!choice) { - return std::nullopt; - } - result.push_back(*choice); - } - } - return result; -} - -std::vector get_shell_seam( - const Shells::Shell<> &shell, - const std::function &chooser -) { - std::optional> seam{maybe_get_shell_seam( - shell, - [&](const Perimeters::Perimeter &perimeter, std::size_t slice_index) { - return chooser(perimeter, slice_index); - } - )}; - if (!seam) { - // Should be unreachable as chooser always returns a SeamChoice! - return std::vector(shell.size()); - } - return *seam; -} - -std::vector> get_object_seams( - Shells::Shells<> &&shells, - const std::function(const Shells::Shell<>&)> &get_shell_seam -) { - std::vector> layer_seams(get_layer_count(shells)); - - for (Shells::Shell<> &shell : shells) { - std::vector seam{get_shell_seam(shell)}; - - for (std::size_t perimeter_id{}; perimeter_id < shell.size(); ++perimeter_id) { - const SeamChoice &choice{seam[perimeter_id]}; - Perimeters::Perimeter &perimeter{shell[perimeter_id].boundary}; - layer_seams[shell[perimeter_id].layer_index].emplace_back(choice, std::move(perimeter)); - } - } - - return layer_seams; -} } // namespace Slic3r::Seams diff --git a/src/libslic3r/GCode/SeamChoice.hpp b/src/libslic3r/GCode/SeamChoice.hpp index d33933b71f..484c1d53da 100644 --- a/src/libslic3r/GCode/SeamChoice.hpp +++ b/src/libslic3r/GCode/SeamChoice.hpp @@ -2,7 +2,7 @@ #define libslic3r_SeamChoice_hpp_ #include "libslic3r/Polygon.hpp" -#include "libslic3r/GCode/SeamPerimeters.hpp" +#include "libslic3r/GCode/SeamShells.hpp" namespace Slic3r::Seams { @@ -52,21 +52,6 @@ SeamChoice choose_seam_point( std::optional choose_degenerate_seam_point(const Perimeters::Perimeter &perimeter); -std::optional> maybe_get_shell_seam( - const Shells::Shell<> &shell, - const std::function(const Perimeters::Perimeter &, std::size_t)> &chooser -); - -std::vector get_shell_seam( - const Shells::Shell<> &shell, - const std::function &chooser -); - -std::vector> get_object_seams( - Shells::Shells<> &&shells, - const std::function(const Shells::Shell<> &)> &get_shell_seam -); - } // namespace Slic3r::Seams #endif // libslic3r_SeamChoice_hpp_ diff --git a/src/libslic3r/GCode/SeamGeometry.cpp b/src/libslic3r/GCode/SeamGeometry.cpp index 563836e2e3..8688fb0e45 100644 --- a/src/libslic3r/GCode/SeamGeometry.cpp +++ b/src/libslic3r/GCode/SeamGeometry.cpp @@ -1,4 +1,5 @@ #include "libslic3r/GCode/SeamGeometry.hpp" +#include "ClipperUtils.hpp" #include "KDTreeIndirect.hpp" #include "Layer.hpp" #include @@ -188,6 +189,54 @@ std::vector get_extrusions(tcb::span obj return result; } +BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) { + BoundedPolygons result; + result.reserve(external_perimeters.size()); + + using std::transform, std::back_inserter; + + transform( + external_perimeters.begin(), external_perimeters.end(), back_inserter(result), + [&](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 : + external_perimeter.island_boundary.holes[choosen_index - 1]}; + return BoundedPolygon{adjacent_boundary, external_perimeter.island_boundary_bounding_boxes[choosen_index], is_hole}; + } + ); + return result; +} + +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], max_bb_distance); + } + + return result; +} + std::vector oversample_edge(const Vec2d &from, const Vec2d &to, const double max_distance) { const double total_distance{(from - to).norm()}; const auto points_count{static_cast(std::ceil(total_distance / max_distance)) + 1}; diff --git a/src/libslic3r/GCode/SeamGeometry.hpp b/src/libslic3r/GCode/SeamGeometry.hpp index ff3c11d83f..80faa53440 100644 --- a/src/libslic3r/GCode/SeamGeometry.hpp +++ b/src/libslic3r/GCode/SeamGeometry.hpp @@ -43,6 +43,17 @@ using Extrusions = std::vector; std::vector get_extrusions(tcb::span object_layers); +struct BoundedPolygon { + Polygon polygon; + BoundingBox bounding_box; + bool is_hole{false}; +}; + +using BoundedPolygons = std::vector; + +BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance); +std::vector project_to_geometry(const std::vector &extrusions, const double max_bb_distance); + Vec2d get_polygon_normal( const std::vector &points, const std::size_t index, const double min_arm_length ); diff --git a/src/libslic3r/GCode/SeamPerimeters.cpp b/src/libslic3r/GCode/SeamPerimeters.cpp index 32c44b7b84..6664494711 100644 --- a/src/libslic3r/GCode/SeamPerimeters.cpp +++ b/src/libslic3r/GCode/SeamPerimeters.cpp @@ -384,26 +384,32 @@ Perimeter Perimeter::create( std::move(angle_types)}; } -Shells::Shells<> create_perimeters( - const std::vector> &shells, +LayerPerimeters create_perimeters( + const std::vector &polygons, const std::vector &layer_infos, const ModelInfo::Painting &painting, const PerimeterParams ¶ms ) { - std::vector> result; - result.reserve(shells.size()); + LayerPerimeters result; + result.reserve(polygons.size()); + std::transform( - shells.begin(), shells.end(), std::back_inserter(result), - [](const Shells::Shell &shell) { return Shells::Shell<>(shell.size()); } + polygons.begin(), polygons.end(), std::back_inserter(result), + [](const Geometry::BoundedPolygons &layer) { return BoundedPerimeters(layer.size()); } ); - Geometry::iterate_nested(shells, [&](const std::size_t shell_index, const std::size_t polygon_index){ - const Shells::Shell &shell{shells[shell_index]}; - const Shells::Slice& slice{shell[polygon_index]}; - const Polygon &polygon{slice.boundary}; - const LayerInfo &layer_info{layer_infos[slice.layer_index]}; - result[shell_index][polygon_index] = {Perimeter::create(polygon, painting, layer_info, params), slice.layer_index}; - }); + Geometry::iterate_nested( + polygons, + [&](const std::size_t layer_index, const std::size_t polygon_index) { + const Geometry::BoundedPolygons &layer{polygons[layer_index]}; + const Geometry::BoundedPolygon &bounded_polygon{layer[polygon_index]}; + const LayerInfo &layer_info{layer_infos[layer_index]}; + result[layer_index][polygon_index] = BoundedPerimeter{ + Perimeter::create(bounded_polygon.polygon, painting, layer_info, params), + bounded_polygon.bounding_box}; + } + ); return result; } + } // namespace Slic3r::Seams::Perimeter diff --git a/src/libslic3r/GCode/SeamPerimeters.hpp b/src/libslic3r/GCode/SeamPerimeters.hpp index 99553e7780..47be8806eb 100644 --- a/src/libslic3r/GCode/SeamPerimeters.hpp +++ b/src/libslic3r/GCode/SeamPerimeters.hpp @@ -5,8 +5,8 @@ #include "libslic3r/GCode/SeamPainting.hpp" #include "libslic3r/KDTreeIndirect.hpp" - -#include "libslic3r/GCode/SeamShells.hpp" +#include "libslic3r/AABBTreeLines.hpp" +#include "libslic3r/GCode/SeamGeometry.hpp" namespace Slic3r { class Layer; @@ -164,37 +164,25 @@ struct Perimeter PointTrees blocked_points{}; }; +using Perimeters = std::vector; + struct BoundedPerimeter { - Perimeters::Perimeter perimeter; + Perimeter perimeter; BoundingBox bounding_box; }; -/** - * @brief Create a Perimeter for each polygon in each of the shells. - */ -Shells::Shells create_perimeters( - const std::vector> &shells, +using BoundedPerimeters = std::vector; +using LayerPerimeters = std::vector; + +LayerPerimeters create_perimeters( + const std::vector &polygons, const std::vector &layer_infos, const ModelInfo::Painting &painting, const PerimeterParams ¶ms ); -inline std::size_t get_layer_count( - const Shells::Shells<> &shells -) { - std::size_t layer_count{0}; - for (const Shells::Shell<> &shell : shells) { - for (const Shells::Slice<>& slice : shell) { - if (slice.layer_index >= layer_count) { - layer_count = slice.layer_index + 1; - } - } - } - return layer_count; -} - inline std::vector extract_points( - const Perimeters::Perimeter &perimeter, const Perimeters::PointType point_type + const Perimeter &perimeter, const PointType point_type ) { std::vector result; for (std::size_t i{0}; i < perimeter.positions.size(); ++i) { diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 63e5eb6d9c..2a764b2663 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -7,8 +7,10 @@ #include #include + #include "SeamPlacer.hpp" +#include "libslic3r/GCode/SeamShells.hpp" #include "libslic3r/GCode/SeamAligned.hpp" #include "libslic3r/GCode/SeamRear.hpp" #include "libslic3r/GCode/SeamRandom.hpp" @@ -17,16 +19,15 @@ namespace Slic3r::Seams { -using ObjectShells = std::vector>>; using ObjectPainting = std::map; -ObjectShells partition_to_shells( +ObjectLayerPerimeters get_perimeters( SpanOfConstPtrs objects, const Params ¶ms, const ObjectPainting& object_painting, const std::function &throw_if_canceled ) { - ObjectShells result; + ObjectLayerPerimeters result; for (const PrintObject *print_object : objects) { const ModelInfo::Painting &painting{object_painting.at(print_object)}; @@ -37,20 +38,18 @@ ObjectShells partition_to_shells( const Perimeters::LayerInfos layer_infos{Perimeters::get_layer_infos( print_object->layers(), params.perimeter.elephant_foot_compensation )}; - Shells::Shells shell_polygons{ - Shells::create_shells(extrusions, params.max_distance)}; + const std::vector projected{Geometry::project_to_geometry(extrusions, params.max_distance)}; + Perimeters::LayerPerimeters perimeters{Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; - Shells::Shells<> perimeters{ - Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; throw_if_canceled(); - result.emplace_back(print_object, std::move(perimeters)); + result.emplace(print_object, std::move(perimeters)); } return result; } -LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) { - const std::size_t layer_count{Perimeters::get_layer_count(shells)}; - LayerPerimeters result(layer_count); +Perimeters::LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) { + const std::size_t layer_count{Shells::get_layer_count(shells)}; + Perimeters::LayerPerimeters result(layer_count); for (Shells::Shell<> &shell : shells) { for (Shells::Slice<> &slice : shell) { @@ -63,22 +62,14 @@ LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) { return result; } -ObjectLayerPerimeters sort_to_layers(ObjectShells &&object_shells) { - ObjectLayerPerimeters result; - for (auto &[print_object, shells] : object_shells) { - result[print_object] = sort_to_layers(std::move(shells)); - } - return result; -} - ObjectSeams precalculate_seams( const Params ¶ms, - ObjectShells &&seam_data, + ObjectLayerPerimeters &&seam_data, const std::function &throw_if_canceled ) { ObjectSeams result; - for (auto &[print_object, shells] : seam_data) { + for (auto &[print_object, layer_perimeters] : seam_data) { switch (params.seam_preference) { case spAligned: { const Transform3d transformation{print_object->trafo_centered()}; @@ -91,17 +82,18 @@ ObjectSeams precalculate_seams( points_visibility, params.convex_visibility_modifier, params.concave_visibility_modifier}; + Shells::Shells<> shells{Shells::create_shells(std::move(layer_perimeters), params.max_distance)}; result[print_object] = Aligned::get_object_seams( std::move(shells), visibility_calculator, params.aligned ); break; } case spRear: { - result[print_object] = Rear::get_object_seams(sort_to_layers(std::move(shells)), params.rear_tolerance, params.rear_y_offset); + result[print_object] = Rear::get_object_seams(std::move(layer_perimeters), params.rear_tolerance, params.rear_y_offset); break; } case spRandom: { - result[print_object] = Random::get_object_seams(std::move(shells), params.random_seed); + result[print_object] = Random::get_object_seams(std::move(layer_perimeters), params.random_seed); break; } case spNearest: { @@ -166,14 +158,14 @@ void Placer::init( object_painting.emplace(print_object, ModelInfo::Painting{transformation, volumes}); } - ObjectShells seam_data{partition_to_shells(objects, params, object_painting, throw_if_canceled)}; + ObjectLayerPerimeters perimeters{get_perimeters(objects, params, object_painting, throw_if_canceled)}; this->params = params; if (this->params.seam_preference != spNearest) { this->seams_per_object = - precalculate_seams(params, std::move(seam_data), throw_if_canceled); + precalculate_seams(params, std::move(perimeters), throw_if_canceled); } else { - this->perimeters_per_layer = sort_to_layers(std::move(seam_data)); + this->perimeters_per_layer = std::move(perimeters); } BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: init: end"; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 51ea244926..843a5ddeac 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -23,8 +23,7 @@ namespace Slic3r::Seams { using ObjectSeams = std::unordered_map>>; -using LayerPerimeters = std::vector>; -using ObjectLayerPerimeters = std::unordered_map; +using ObjectLayerPerimeters = std::unordered_map; struct Params { diff --git a/src/libslic3r/GCode/SeamRandom.cpp b/src/libslic3r/GCode/SeamRandom.cpp index 27f01ca6be..03fa33e6ed 100644 --- a/src/libslic3r/GCode/SeamRandom.cpp +++ b/src/libslic3r/GCode/SeamRandom.cpp @@ -122,15 +122,33 @@ std::optional Random::operator()( } // namespace Impl std::vector> get_object_seams( - Shells::Shells<> &&shells, const unsigned fixed_seed + Perimeters::LayerPerimeters &&perimeters, const unsigned fixed_seed ) { std::mt19937 random_engine{fixed_seed}; const Impl::Random random{random_engine}; - return Seams::get_object_seams(std::move(shells), [&](const Shells::Shell<> &shell) { - return Seams::get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) { - return Seams::choose_seam_point(perimeter, random); - }); - }); + std::vector> result; + + for (std::vector &layer : perimeters) { + result.emplace_back(); + for (Perimeters::BoundedPerimeter &perimeter : layer) { + if (perimeter.perimeter.is_degenerate) { + std::optional seam_choice{ + Seams::choose_degenerate_seam_point(perimeter.perimeter)}; + if (seam_choice) { + result.back().push_back( + SeamPerimeterChoice{*seam_choice, std::move(perimeter.perimeter)} + ); + } else { + result.back().push_back(SeamPerimeterChoice{SeamChoice{}, std::move(perimeter.perimeter)}); + } + } else { + result.back().push_back(SeamPerimeterChoice{ + Seams::choose_seam_point(perimeter.perimeter, random), + std::move(perimeter.perimeter)}); + } + } + } + return result; } } // namespace Slic3r::Seams::Random diff --git a/src/libslic3r/GCode/SeamRandom.hpp b/src/libslic3r/GCode/SeamRandom.hpp index 81c8dc4342..c7eb0f3644 100644 --- a/src/libslic3r/GCode/SeamRandom.hpp +++ b/src/libslic3r/GCode/SeamRandom.hpp @@ -24,6 +24,6 @@ struct Random }; } std::vector> get_object_seams( - Shells::Shells<> &&shells, const unsigned fixed_seed + Perimeters::LayerPerimeters &&perimeters, const unsigned fixed_seed ); } diff --git a/src/libslic3r/GCode/SeamRear.cpp b/src/libslic3r/GCode/SeamRear.cpp index cf60637c25..bb5ba03fca 100644 --- a/src/libslic3r/GCode/SeamRear.cpp +++ b/src/libslic3r/GCode/SeamRear.cpp @@ -75,9 +75,26 @@ std::vector> get_object_seams( for (std::vector &layer : perimeters) { result.emplace_back(); for (Perimeters::BoundedPerimeter &perimeter : layer) { - BoundingBoxf bounding_box{unscaled(perimeter.bounding_box)}; - const SeamChoice seam_choice{Seams::choose_seam_point(perimeter.perimeter, Impl::RearestPointCalculator{rear_tolerance, rear_y_offset, bounding_box})}; - result.back().push_back(SeamPerimeterChoice{seam_choice, std::move(perimeter.perimeter)}); + if (perimeter.perimeter.is_degenerate) { + std::optional seam_choice{ + Seams::choose_degenerate_seam_point(perimeter.perimeter)}; + if (seam_choice) { + result.back().push_back( + SeamPerimeterChoice{*seam_choice, std::move(perimeter.perimeter)} + ); + } else { + result.back().push_back(SeamPerimeterChoice{SeamChoice{}, std::move(perimeter.perimeter)}); + } + } else { + BoundingBoxf bounding_box{unscaled(perimeter.bounding_box)}; + const SeamChoice seam_choice{Seams::choose_seam_point( + perimeter.perimeter, + Impl::RearestPointCalculator{rear_tolerance, rear_y_offset, bounding_box} + )}; + result.back().push_back( + SeamPerimeterChoice{seam_choice, std::move(perimeter.perimeter)} + ); + } } } diff --git a/src/libslic3r/GCode/SeamShells.cpp b/src/libslic3r/GCode/SeamShells.cpp index 65e9149c82..b3ee128424 100644 --- a/src/libslic3r/GCode/SeamShells.cpp +++ b/src/libslic3r/GCode/SeamShells.cpp @@ -5,65 +5,17 @@ namespace Slic3r::Seams::Shells::Impl { -BoundedPolygons project_to_geometry(const Geometry::Extrusions &external_perimeters, const double max_bb_distance) { - BoundedPolygons result; - result.reserve(external_perimeters.size()); - - using std::transform, std::back_inserter; - - transform( - external_perimeters.begin(), external_perimeters.end(), back_inserter(result), - [&](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 : - external_perimeter.island_boundary.holes[choosen_index - 1]}; - return BoundedPolygon{adjacent_boundary, external_perimeter.island_boundary_bounding_boxes[choosen_index], is_hole}; - } - ); - return result; -} - -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], max_bb_distance); - } - - return result; -} - -Shells map_to_shells( - std::vector &&layers, const Geometry::Mapping &mapping, const std::size_t shell_count +Shells<> map_to_shells( + Perimeters::LayerPerimeters &&layers, const Geometry::Mapping &mapping, const std::size_t shell_count ) { - Shells result(shell_count); + Shells<> result(shell_count); for (std::size_t layer_index{0}; layer_index < layers.size(); ++layer_index) { - BoundedPolygons &perimeters{layers[layer_index]}; + Perimeters::BoundedPerimeters &perimeters{layers[layer_index]}; for (std::size_t perimeter_index{0}; perimeter_index < perimeters.size(); perimeter_index++) { - Polygon &perimeter{perimeters[perimeter_index].polygon}; + Perimeters::Perimeter &perimeter{perimeters[perimeter_index].perimeter}; result[mapping[layer_index][perimeter_index]].push_back( - Slice{std::move(perimeter), layer_index} + Slice<>{std::move(perimeter), layer_index} ); } } @@ -72,30 +24,31 @@ Shells map_to_shells( } // namespace Slic3r::Seams::Shells::Impl namespace Slic3r::Seams::Shells { -Shells create_shells( - const std::vector &extrusions, const double max_distance +Shells<> create_shells( + Perimeters::LayerPerimeters &&perimeters, const double max_distance ) { - std::vector projected{Impl::project_to_geometry(extrusions, max_distance)}; + using Perimeters::BoundedPerimeters; + using Perimeters::BoundedPerimeter; std::vector layer_sizes; - layer_sizes.reserve(projected.size()); - for (const Impl::BoundedPolygons &perimeters : projected) { - layer_sizes.push_back(perimeters.size()); + layer_sizes.reserve(perimeters.size()); + for (const BoundedPerimeters &layer : perimeters) { + layer_sizes.push_back(layer.size()); } const auto &[shell_mapping, shell_count]{Geometry::get_mapping( layer_sizes, [&](const std::size_t layer_index, const std::size_t item_index) -> Geometry::MappingOperatorResult { - const Impl::BoundedPolygons &layer{projected[layer_index]}; - const Impl::BoundedPolygons &next_layer{projected[layer_index + 1]}; + const BoundedPerimeters &layer{perimeters[layer_index]}; + const BoundedPerimeters &next_layer{perimeters[layer_index + 1]}; if (next_layer.empty()) { return std::nullopt; } BoundingBoxes next_layer_bounding_boxes; - for (const Impl::BoundedPolygon &bounded_polygon : next_layer) { - next_layer_bounding_boxes.emplace_back(bounded_polygon.bounding_box); + for (const BoundedPerimeter &bounded_perimeter : next_layer) { + next_layer_bounding_boxes.emplace_back(bounded_perimeter.bounding_box); } const auto [perimeter_index, distance] = Geometry::pick_closest_bounding_box( @@ -109,6 +62,6 @@ Shells create_shells( } )}; - return Impl::map_to_shells(std::move(projected), shell_mapping, shell_count); + return Impl::map_to_shells(std::move(perimeters), shell_mapping, shell_count); } } // namespace Slic3r::Seams::Shells diff --git a/src/libslic3r/GCode/SeamShells.hpp b/src/libslic3r/GCode/SeamShells.hpp index ea81053506..2a5d0ce19b 100644 --- a/src/libslic3r/GCode/SeamShells.hpp +++ b/src/libslic3r/GCode/SeamShells.hpp @@ -4,6 +4,7 @@ #include #include +#include "libslic3r/GCode/SeamPerimeters.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/GCode/SeamGeometry.hpp" @@ -11,23 +12,6 @@ namespace Slic3r { class Layer; } -namespace Slic3r::Seams::Perimeters { -struct Perimeter; -} - -namespace Slic3r::Seams::Shells::Impl { - -struct BoundedPolygon { - Polygon polygon; - BoundingBox bounding_box; - bool is_hole{false}; -}; - -using BoundedPolygons = std::vector; - -BoundedPolygons project_to_geometry(const Geometry::Extrusions &extrusions, const double max_bb_distance); -} - namespace Slic3r::Seams::Shells { template struct Slice { @@ -39,8 +23,22 @@ template using Shell = std::vector> template using Shells = std::vector>; -Shells create_shells( - const std::vector &extrusions, const double max_distance +inline std::size_t get_layer_count( + const Shells<> &shells +) { + std::size_t layer_count{0}; + for (const Shell<> &shell : shells) { + for (const Slice<>& slice : shell) { + if (slice.layer_index >= layer_count) { + layer_count = slice.layer_index + 1; + } + } + } + return layer_count; +} + +Shells<> create_shells( + Perimeters::LayerPerimeters &&perimeters, const double max_distance ); } // namespace Slic3r::Seams::Shells diff --git a/tests/fff_print/benchmark_seams.cpp b/tests/fff_print/benchmark_seams.cpp index 211407a9c5..41f5242067 100644 --- a/tests/fff_print/benchmark_seams.cpp +++ b/tests/fff_print/benchmark_seams.cpp @@ -12,8 +12,18 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm }; using namespace Slic3r::Seams; + BENCHMARK_ADVANCED("Create shells benchy")(Catch::Benchmark::Chronometer meter) { - meter.measure([&] { return Shells::create_shells(extrusions, params.max_distance); }); + std::vector inputs; + inputs.reserve(meter.runs()); + std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() { + return Slic3r::Seams::Perimeters::create_perimeters( + projected, layer_infos, painting, params.perimeter + ); + }); + meter.measure([&](const int i) { + return Shells::create_shells(std::move(inputs[i]), params.max_distance); + }); }; @@ -27,7 +37,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm BENCHMARK_ADVANCED("Create perimeters benchy")(Catch::Benchmark::Chronometer meter) { meter.measure([&] { - return Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter); + return Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter); }); }; @@ -35,8 +45,12 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm std::vector> inputs; inputs.reserve(meter.runs()); std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() { - return Perimeters::create_perimeters( - shell_polygons, layer_infos, painting, params.perimeter + Slic3r::Seams::Perimeters::LayerPerimeters perimeters{ + Slic3r::Seams::Perimeters::create_perimeters( + projected, layer_infos, painting, params.perimeter + )}; + return Shells::create_shells( + std::move(perimeters), params.max_distance ); }); meter.measure([&](const int i) { @@ -54,27 +68,25 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm }); }; - /** BENCHMARK_ADVANCED("Generate rear seam benchy")(Catch::Benchmark::Chronometer meter) { - std::vector> inputs; + std::vector inputs; inputs.reserve(meter.runs()); std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() { - return create_perimeters( - shell_polygons, layer_infos, painting, params.perimeter + return Slic3r::Seams::Perimeters::create_perimeters( + projected, layer_infos, painting, params.perimeter ); }); meter.measure([&](const int i) { - return Rear::get_object_seams(std::move(inputs[i]), params.rear_project_threshold); + return Rear::get_object_seams(std::move(inputs[i]), params.rear_tolerance, params.rear_y_offset); }); }; - */ BENCHMARK_ADVANCED("Generate random seam benchy")(Catch::Benchmark::Chronometer meter) { - std::vector> inputs; + std::vector inputs; inputs.reserve(meter.runs()); std::generate_n(std::back_inserter(inputs), meter.runs(), [&]() { - return Perimeters::create_perimeters( - shell_polygons, layer_infos, painting, params.perimeter + return Slic3r::Seams::Perimeters::create_perimeters( + projected, layer_infos, painting, params.perimeter ); }); meter.measure([&](const int i) { diff --git a/tests/fff_print/test_data.hpp b/tests/fff_print/test_data.hpp index 61e1b92d42..939d1a7631 100644 --- a/tests/fff_print/test_data.hpp +++ b/tests/fff_print/test_data.hpp @@ -219,10 +219,8 @@ struct SeamsFixture const Seams::Perimeters::LayerInfos layer_infos{Seams::Perimeters::get_layer_infos( print_object->layers(), params.perimeter.elephant_foot_compensation )}; - Seams::Shells::Shells shell_polygons{ - Seams::Shells::create_shells(extrusions, params.max_distance)}; - - const std::size_t shell_index{15}; + const std::vector projected{ + Seams::Geometry::project_to_geometry(extrusions, params.max_distance)}; const ModelInfo::Visibility visibility{transformation, volumes, params.visibility, [](){}}; Seams::Aligned::VisibilityCalculator diff --git a/tests/fff_print/test_seam_aligned.cpp b/tests/fff_print/test_seam_aligned.cpp index 1ab755cfaf..33080e749e 100644 --- a/tests/fff_print/test_seam_aligned.cpp +++ b/tests/fff_print/test_seam_aligned.cpp @@ -115,13 +115,13 @@ TEST_CASE_METHOD(PickSeamOptionFixture, "Least visible point", "[Seams][SeamAlig } TEST_CASE_METHOD(Test::SeamsFixture, "Generate aligned seam", "[Seams][SeamAligned][Integration]") { - Shells::Shells<> perimeters{ - Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; - Shells::Shells<> shell_perimeters; - shell_perimeters.push_back(std::move(perimeters[shell_index])); + Seams::Perimeters::LayerPerimeters perimeters{ + Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; + Seams::Shells::Shells<> shells{ + Seams::Shells::create_shells(std::move(perimeters), params.max_distance)}; + const std::vector> seam{ - Aligned::get_object_seams(std::move(shell_perimeters), visibility_calculator, params.aligned)}; - REQUIRE(seam.size() == 125); + Aligned::get_object_seams(std::move(shells), visibility_calculator, params.aligned)}; if constexpr (debug_files) { std::ofstream csv{"aligned_seam.csv"}; @@ -133,9 +133,13 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Calculate visibility", "[Seams][SeamAligne if constexpr (debug_files) { std::ofstream csv{"visibility.csv"}; csv << "x,y,z,visibility,total_visibility" << std::endl; - Shells::Shells<> perimeters{ - Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; - for (const Shells::Shell<> &shell : perimeters) { + + Seams::Perimeters::LayerPerimeters perimeters{ + Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; + + Seams::Shells::Shells<> shells{ + Seams::Shells::create_shells(std::move(perimeters), params.max_distance)}; + for (const Shells::Shell<> &shell : shells) { for (const Shells::Slice<> &slice : shell) { for (std::size_t index{0}; index < slice.boundary.positions.size(); ++index) { const Vec2d &position{slice.boundary.positions[index]}; diff --git a/tests/fff_print/test_seam_perimeters.cpp b/tests/fff_print/test_seam_perimeters.cpp index 405eb3731d..36e4f67b66 100644 --- a/tests/fff_print/test_seam_perimeters.cpp +++ b/tests/fff_print/test_seam_perimeters.cpp @@ -167,15 +167,14 @@ void serialize_shell(std::ostream &output, const Shells::Shell perimeters{ - create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; + Seams::Perimeters::LayerPerimeters perimeters{ + Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; - const Shells::Shell<> &shell{perimeters[shell_index]}; + Seams::Shells::Shells<> shells{ + Seams::Shells::create_shells(std::move(perimeters), params.max_distance)}; if constexpr (debug_files) { std::ofstream csv{"perimeters.csv"}; - serialize_shell(csv, shell); + serialize_shell(csv, shells[0]); } - - REQUIRE(shell.size() == 54); } diff --git a/tests/fff_print/test_seam_random.cpp b/tests/fff_print/test_seam_random.cpp index 8a69c6b66a..cd9514619e 100644 --- a/tests/fff_print/test_seam_random.cpp +++ b/tests/fff_print/test_seam_random.cpp @@ -84,16 +84,13 @@ TEST_CASE("Random respects point type", "[Seams][SeamRandom]") { } TEST_CASE_METHOD(Test::SeamsFixture, "Generate random seam", "[Seams][SeamRandom][Integration]") { - Shells::Shells<> perimeters{ - Seams::Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; - Shells::Shells<> shell_perimeters; - shell_perimeters.push_back(std::move(perimeters[shell_index])); - const std::vector> seam{ - Random::get_object_seams(std::move(shell_perimeters), params.random_seed)}; - REQUIRE(seam.size() == 125); + Seams::Perimeters::LayerPerimeters perimeters{ + Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; + const std::vector> seams{ + Random::get_object_seams(std::move(perimeters), params.random_seed)}; if constexpr (debug_files) { std::ofstream csv{"random_seam.csv"}; - Test::serialize_seam(csv, seam); + Test::serialize_seam(csv, seams); } } diff --git a/tests/fff_print/test_seam_rear.cpp b/tests/fff_print/test_seam_rear.cpp index 356fa093af..fc183a3ce7 100644 --- a/tests/fff_print/test_seam_rear.cpp +++ b/tests/fff_print/test_seam_rear.cpp @@ -34,19 +34,14 @@ Perimeters::Perimeter get_perimeter() { } } // namespace RearTest -/** TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][Integration]") { - Shells::Shells<> perimeters{ - Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; - Shells::Shells<> shell_perimeters; - shell_perimeters.push_back(std::move(perimeters[shell_index])); - const std::vector> seam{ - Rear::get_object_seams(std::move(shell_perimeters), params.rear_project_threshold)}; - REQUIRE(seam.size() == 125); + Seams::Perimeters::LayerPerimeters perimeters{ + Seams::Perimeters::create_perimeters(projected, layer_infos, painting, params.perimeter)}; + const std::vector> seams{ + Rear::get_object_seams(std::move(perimeters), params.rear_tolerance, params.rear_y_offset)}; if constexpr (debug_files) { std::ofstream csv{"rear_seam.csv"}; - Test::serialize_seam(csv, seam); + Test::serialize_seam(csv, seams); } } -*/ diff --git a/tests/fff_print/test_seam_shells.cpp b/tests/fff_print/test_seam_shells.cpp index b514304fbc..56bd273780 100644 --- a/tests/fff_print/test_seam_shells.cpp +++ b/tests/fff_print/test_seam_shells.cpp @@ -33,7 +33,7 @@ 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, 5.0)}; + Seams::Geometry::BoundedPolygons result{Seams::Geometry::project_to_geometry(extrusions, 5.0)}; REQUIRE(result.size() == 1); REQUIRE(result[0].polygon.size() == 4); // Boundary polygon is picked. @@ -48,7 +48,7 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seam island_boundary.contour = boundary_polygon; - Shells::Impl::BoundedPolygons result{Shells::Impl::project_to_geometry(extrusions, 1.0)}; + Seams::Geometry::BoundedPolygons result{Seams::Geometry::project_to_geometry(extrusions, 1.0)}; REQUIRE(result.size() == 1); REQUIRE(result[0].polygon.size() == 4); @@ -57,35 +57,3 @@ TEST_CASE_METHOD(ProjectionFixture, "Project to geometry does not match", "[Seam // 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 -) { - out << "x,y,z,layer_index,slice_id,shell_id" << std::endl; - for (std::size_t shell_id{}; shell_id < shells.size(); ++shell_id) { - const Shells::Shell &shell{shells[shell_id]}; - for (std::size_t slice_id{}; slice_id < shell.size(); ++slice_id) { - const Shells::Slice &slice{shell[slice_id]}; - for (const Point &point : slice.boundary) { - // clang-format off - out - << point.x() << "," - << point.y() << "," - << slice.layer_index * 1e6 * layer_height << "," - << slice.layer_index << "," - << slice_id << "," - << shell_id << std::endl; - // clang-format on - } - } - } -} - -TEST_CASE_METHOD(Test::SeamsFixture, "Create shells", "[Seams][SeamShells][Integration]") { - if constexpr (debug_files) { - std::ofstream csv{"shells.csv"}; - serialize_shells(csv, shell_polygons, print->full_print_config().opt_float("layer_height")); - } - - CHECK(shell_polygons.size() == 36); -}