diff --git a/src/libslic3r/GCode/SeamPerimeters.hpp b/src/libslic3r/GCode/SeamPerimeters.hpp index 21c50e0a98..99553e7780 100644 --- a/src/libslic3r/GCode/SeamPerimeters.hpp +++ b/src/libslic3r/GCode/SeamPerimeters.hpp @@ -107,6 +107,8 @@ struct Perimeter { struct IndexToCoord { + IndexToCoord(const tcb::span positions): positions(positions) {} + IndexToCoord() = default; double operator()(const size_t index, size_t dim) const; tcb::span positions; @@ -162,6 +164,11 @@ struct Perimeter PointTrees blocked_points{}; }; +struct BoundedPerimeter { + Perimeters::Perimeter perimeter; + BoundingBox bounding_box; +}; + /** * @brief Create a Perimeter for each polygon in each of the shells. */ diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 0e8adff308..63e5eb6d9c 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -48,6 +48,29 @@ ObjectShells partition_to_shells( return result; } +LayerPerimeters sort_to_layers(Shells::Shells<> &&shells) { + const std::size_t layer_count{Perimeters::get_layer_count(shells)}; + LayerPerimeters result(layer_count); + + for (Shells::Shell<> &shell : shells) { + for (Shells::Slice<> &slice : shell) { + const BoundingBox bounding_box{Geometry::scaled(slice.boundary.positions)}; + result[slice.layer_index].push_back( + Perimeters::BoundedPerimeter{std::move(slice.boundary), bounding_box} + ); + } + } + 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, @@ -74,7 +97,7 @@ ObjectSeams precalculate_seams( break; } case spRear: { - result[print_object] = Rear::get_object_seams(std::move(shells), params.rear_project_threshold); + result[print_object] = Rear::get_object_seams(sort_to_layers(std::move(shells)), params.rear_tolerance, params.rear_y_offset); break; } case spRandom: { @@ -111,7 +134,8 @@ Params Placer::get_params(const DynamicPrintConfig &config) { params.staggered_inner_seams = config.opt_bool("staggered_inner_seams"); params.max_nearest_detour = 1.0; - params.rear_project_threshold = 0.05; // % + params.rear_tolerance = 0.2; + params.rear_y_offset = 20; params.aligned.jump_visibility_threshold = 0.6; params.max_distance = 5.0; params.perimeter.oversampling_max_distance = 0.2; @@ -128,24 +152,6 @@ Params Placer::get_params(const DynamicPrintConfig &config) { return params; } -ObjectLayerPerimeters sort_to_layers(ObjectShells &&object_shells) { - ObjectLayerPerimeters result; - for (auto &[print_object, shells] : object_shells) { - const std::size_t layer_count{print_object->layer_count()}; - result[print_object] = LayerPerimeters(layer_count); - - for (Shells::Shell<> &shell : shells) { - for (Shells::Slice<> &slice : shell) { - const BoundingBox bounding_box{Geometry::scaled(slice.boundary.positions)}; - result[print_object][slice.layer_index].push_back( - BoundedPerimeter{std::move(slice.boundary), bounding_box} - ); - } - } - } - return result; -} - void Placer::init( SpanOfConstPtrs objects, const Params ¶ms, @@ -306,14 +312,14 @@ struct NearestCorner { }; std::pair place_seam_near( - const std::vector &layer_perimeters, + const std::vector &layer_perimeters, const ExtrusionLoop &loop, const Point &position, const double max_detour ) { BoundingBoxes choose_from; choose_from.reserve(layer_perimeters.size()); - for (const BoundedPerimeter &perimeter : layer_perimeters) { + for (const Perimeters::BoundedPerimeter &perimeter : layer_perimeters) { choose_from.push_back(perimeter.bounding_box); } @@ -367,7 +373,7 @@ Point Placer::place_seam(const Layer *layer, const ExtrusionLoop &loop, const Po if (this->params.seam_preference == spNearest) { - const std::vector &perimeters{this->perimeters_per_layer.at(po)[layer_index]}; + const std::vector &perimeters{this->perimeters_per_layer.at(po)[layer_index]}; const auto [seam_choice, perimeter_index] = place_seam_near(perimeters, loop, last_pos, this->params.max_nearest_detour); return finalize_seam_position(loop_polygon, seam_choice, perimeters[perimeter_index].perimeter, loop_width, do_staggering); } else { diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 625170944a..51ea244926 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -21,20 +21,16 @@ namespace Slic3r::Seams { -struct BoundedPerimeter { - Perimeters::Perimeter perimeter; - BoundingBox bounding_box; -}; - using ObjectSeams = std::unordered_map>>; -using LayerPerimeters = std::vector>; +using LayerPerimeters = std::vector>; using ObjectLayerPerimeters = std::unordered_map; struct Params { double max_nearest_detour; - double rear_project_threshold; + double rear_tolerance; + double rear_y_offset; Aligned::Params aligned; double max_distance{}; unsigned random_seed{}; diff --git a/src/libslic3r/GCode/SeamRear.cpp b/src/libslic3r/GCode/SeamRear.cpp index d189b5ad08..cf60637c25 100644 --- a/src/libslic3r/GCode/SeamRear.cpp +++ b/src/libslic3r/GCode/SeamRear.cpp @@ -15,111 +15,72 @@ BoundingBoxf get_bounding_box(const Shells::Shell<> &shell) { return result; } -std::optional get_rearest_point( - const Perimeters::Perimeter &perimeter, - const PointType point_type, - const PointClassification point_classification -) { - double max_y{-std::numeric_limits::infinity()}; - std::optional choosen_index; - for (std::size_t i{0}; i < perimeter.positions.size(); ++i) { - const Perimeters::PointType _point_type{perimeter.point_types[i]}; - const Perimeters::PointClassification _point_classification{perimeter.point_classifications[i]}; +struct RearestPointCalculator { + double rear_tolerance; + double rear_y_offset; + BoundingBoxf bounding_box; - if (point_type == _point_type && point_classification == _point_classification) { - const Vec2d &position{perimeter.positions[i]}; - if (position.y() > max_y) { - max_y = position.y(); - choosen_index = i; + std::optional operator()( + const Perimeters::Perimeter &perimeter, + const PointType point_type, + const PointClassification point_classification + ) { + std::vector possible_lines; + for (std::size_t i{0}; i < perimeter.positions.size() - 1; ++i) { + if (perimeter.point_types[i] != point_type) { + continue; } + if (perimeter.point_classifications[i] != point_classification) { + continue; + } + if (perimeter.point_types[i + 1] != point_type) { + continue; + } + if (perimeter.point_classifications[i + 1] != point_classification) { + continue; + } + possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[i+1], i, i + 1}); } - } - if (choosen_index) { - return SeamChoice{*choosen_index, *choosen_index, perimeter.positions[*choosen_index]}; - } - return std::nullopt; -} - -std::optional StraightLine::operator()( - const Perimeters::Perimeter &perimeter, - const PointType point_type, - const PointClassification point_classification -) const { - std::vector possible_lines; - for (std::size_t i{0}; i < perimeter.positions.size() - 1; ++i) { - if (perimeter.point_types[i] != point_type) { - continue; - } - if (perimeter.point_classifications[i] != point_classification) { - continue; - } - if (perimeter.point_types[i + 1] != point_type) { - continue; - } - if (perimeter.point_classifications[i + 1] != point_classification) { - continue; - } - possible_lines.push_back(PerimeterLine{perimeter.positions[i], perimeter.positions[i+1], i, i + 1}); - } - if (possible_lines.empty()) { - return std::nullopt; - } - - const AABBTreeLines::LinesDistancer possible_distancer{possible_lines}; - const BoundingBoxf bounding_box{perimeter.positions}; - - const std::vector> intersections{ - possible_distancer.intersections_with_line(PerimeterLine{ - this->prefered_position, Vec2d{this->prefered_position.x(), bounding_box.min.y()}, - 0, 0})}; - if (!intersections.empty()) { - const auto[position, line_index]{intersections.front()}; - if (position.y() < bounding_box.max.y() - - this->rear_project_threshold * (bounding_box.max.y() - bounding_box.min.y())) { + if (possible_lines.empty()) { return std::nullopt; } - const PerimeterLine &intersected_line{possible_lines[line_index]}; - const SeamChoice intersected_choice{intersected_line.previous_index, intersected_line.next_index, position}; - return intersected_choice; + const BoundingBoxf bounding_box{perimeter.positions}; + const AABBTreeLines::LinesDistancer possible_distancer{possible_lines}; + const double center_x{(bounding_box.max.x() + bounding_box.min.x()) / 2.0}; + const Vec2d prefered_position{center_x, bounding_box.max.y() + rear_y_offset}; + auto [_, line_index, point] = possible_distancer.distance_from_lines_extra(prefered_position); + const Vec2d location_at_bb{center_x, bounding_box.max.y()}; + auto [_d, line_index_at_bb, point_at_bb] = possible_distancer.distance_from_lines_extra(location_at_bb); + const double y_distance{point.y() - point_at_bb.y()}; + + Vec2d result{point}; + if (y_distance < 0) { + result = point_at_bb; + } else if (y_distance <= rear_tolerance) { + const double factor{y_distance / rear_tolerance}; + result = factor * point + (1 - factor) * point_at_bb; + } + return SeamChoice{possible_lines[line_index].previous_index, possible_lines[line_index].next_index, result}; } - return std::nullopt; -} +}; } // namespace Impl std::vector> get_object_seams( - Shells::Shells<> &&shells, - const double rear_project_threshold + std::vector> &&perimeters, + const double rear_tolerance, + const double rear_y_offset ) { - double average_x_center{0.0}; - std::size_t count{0}; - for (const Shells::Shell<> &shell : shells) { - for (const Shells::Slice<> &slice : shell) { - if (slice.boundary.positions.empty()) { - continue; - } - const BoundingBoxf slice_bounding_box{slice.boundary.positions}; - average_x_center += (slice_bounding_box.min.x() + slice_bounding_box.max.x()) / 2.0; - count++; + std::vector> result; + + 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)}); } } - average_x_center /= count; - return Seams::get_object_seams(std::move(shells), [&](const Shells::Shell<> &shell) { - BoundingBoxf bounding_box{Impl::get_bounding_box(shell)}; - const Vec2d back_center{average_x_center, bounding_box.max.y()}; - std::optional> straight_seam { - Seams::maybe_get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) { - return Seams::maybe_choose_seam_point( - perimeter, - Impl::StraightLine{back_center, rear_project_threshold} - ); - }) - }; - if (!straight_seam) { - return Seams::get_shell_seam(shell, [&](const Perimeters::Perimeter &perimeter, std::size_t) { - return Seams::choose_seam_point(perimeter, Impl::get_rearest_point); - }); - } - return *straight_seam; - }); + + return result; } } // namespace Slic3r::Seams::Rear diff --git a/src/libslic3r/GCode/SeamRear.hpp b/src/libslic3r/GCode/SeamRear.hpp index a313432780..5ab2fe4269 100644 --- a/src/libslic3r/GCode/SeamRear.hpp +++ b/src/libslic3r/GCode/SeamRear.hpp @@ -16,24 +16,12 @@ struct PerimeterLine using Scalar = Vec2d::Scalar; static const constexpr int Dim = 2; }; - -struct StraightLine -{ - Vec2d prefered_position; - double rear_project_threshold; - - std::optional operator()( - const Perimeters::Perimeter &perimeter, - const Perimeters::PointType point_type, - const Perimeters::PointClassification point_classification - ) const; -}; - } // namespace Impl std::vector> get_object_seams( - Shells::Shells<> &&shells, - const double rear_project_threshold + std::vector> &&perimeters, + const double rear_tolerance, + const double rear_y_offet ); } // namespace Slic3r::Seams::Rear diff --git a/tests/fff_print/benchmark_seams.cpp b/tests/fff_print/benchmark_seams.cpp index 337c730a6d..211407a9c5 100644 --- a/tests/fff_print/benchmark_seams.cpp +++ b/tests/fff_print/benchmark_seams.cpp @@ -54,6 +54,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm }); }; + /** BENCHMARK_ADVANCED("Generate rear seam benchy")(Catch::Benchmark::Chronometer meter) { std::vector> inputs; inputs.reserve(meter.runs()); @@ -66,6 +67,7 @@ TEST_CASE_METHOD(Slic3r::Test::SeamsFixture, "Seam benchmarks", "[Seams][.Benchm return Rear::get_object_seams(std::move(inputs[i]), params.rear_project_threshold); }); }; + */ BENCHMARK_ADVANCED("Generate random seam benchy")(Catch::Benchmark::Chronometer meter) { std::vector> inputs; diff --git a/tests/fff_print/test_seam_rear.cpp b/tests/fff_print/test_seam_rear.cpp index c6ee01c5b3..356fa093af 100644 --- a/tests/fff_print/test_seam_rear.cpp +++ b/tests/fff_print/test_seam_rear.cpp @@ -34,16 +34,7 @@ Perimeters::Perimeter get_perimeter() { } } // namespace RearTest -TEST_CASE("StraightLine operator places seam point near the prefered position", "[Seams][SeamRear]") { - const Rear::Impl::StraightLine rearest{Vec2d{0.7, 2.0}}; - std::optional choice{rearest(RearTest::get_perimeter(), Perimeters::PointType::common, Perimeters::PointClassification::common)}; - - REQUIRE(choice); - CHECK(scaled(choice->position) == scaled(Vec2d{0.7, 1.0})); - CHECK(choice->previous_index == 2); - CHECK(choice->next_index == 3); -} - +/** TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][Integration]") { Shells::Shells<> perimeters{ Perimeters::create_perimeters(shell_polygons, layer_infos, painting, params.perimeter)}; @@ -58,3 +49,4 @@ TEST_CASE_METHOD(Test::SeamsFixture, "Generate rear seam", "[Seams][SeamRear][In Test::serialize_seam(csv, seam); } } +*/