diff --git a/include/SkeletalTrapezoidationJoint.h b/include/SkeletalTrapezoidationJoint.h index 059f6d50d1..3aa2ff88e3 100644 --- a/include/SkeletalTrapezoidationJoint.h +++ b/include/SkeletalTrapezoidationJoint.h @@ -51,7 +51,7 @@ class SkeletalTrapezoidationJoint { beading_ = storage; } - std::shared_ptr getBeading() + std::shared_ptr getBeading() const { return beading_.lock(); } diff --git a/include/WallToolPaths.h b/include/WallToolPaths.h index ff1df0a63a..1ef9db161b 100644 --- a/include/WallToolPaths.h +++ b/include/WallToolPaths.h @@ -110,9 +110,9 @@ class WallToolPaths static void stitchToolPaths(std::vector& toolpaths, const Settings& settings); /*! - * Remove polylines shorter than half the smallest line width along that polyline. + * Remove polylines shorter than half the smallest line width along that polyline, if that polyline isn't part of an outer wall. */ - static void removeSmallLines(std::vector& toolpaths); + static void removeSmallFillLines(std::vector& toolpaths); /*! * Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided diff --git a/include/utils/polygonUtils.h b/include/utils/polygonUtils.h index 825bc62e79..7ed9d92787 100644 --- a/include/utils/polygonUtils.h +++ b/include/utils/polygonUtils.h @@ -674,7 +674,16 @@ class PolygonUtils * \param a_step The angle between segments of the circle. * \return A new Polygon containing the circle. */ - static Polygon makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians a_step = std::numbers::pi / 8); + template + static T makeCircle(const Point2LL& mid, const coord_t radius, const AngleRadians a_step = std::numbers::pi / 8, VA... args) + { + T circle; + for (double a = 0; a < 2 * std::numbers::pi; a += a_step) + { + circle.emplace_back(mid + Point2LL(radius * cos(a), radius * sin(a)), args...); + } + return circle; + } /*! * Create a "wheel" shape. diff --git a/src/SkeletalTrapezoidation.cpp b/src/SkeletalTrapezoidation.cpp index 46454c3eb6..2bb4f2ccca 100644 --- a/src/SkeletalTrapezoidation.cpp +++ b/src/SkeletalTrapezoidation.cpp @@ -17,6 +17,7 @@ #include "utils/VoronoiUtils.h" #include "utils/linearAlg2D.h" #include "utils/macros.h" +#include "utils/polygonUtils.h" #define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX \ 1000 // A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing @@ -2184,39 +2185,83 @@ void SkeletalTrapezoidation::generateLocalMaximaSingleBeads() { std::vector& generated_toolpaths = *p_generated_toolpaths; - for (auto& node : graph_.nodes) + const auto addCircleToToolpath = [&](const Point2LL& center, coord_t width, size_t inset_index) + { + if (inset_index >= generated_toolpaths.size()) + { + generated_toolpaths.resize(inset_index + 1); + } + constexpr bool is_odd = true; + generated_toolpaths[inset_index].emplace_back(inset_index, is_odd); + ExtrusionLine& line = generated_toolpaths[inset_index].back(); + // total area to be extruded is pi*(w/2)^2 = pi*w*w/4 + // Width a constant extrusion width w, that would be a length of pi*w/4 + // If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r + // So our circle needs to be such that r=w/8 + const coord_t r = width / 8; + constexpr coord_t n_segments = 6; + const auto circle = PolygonUtils::makeCircle>(center, r, 2 * std::numbers::pi / n_segments, width, inset_index); + line.junctions_.insert(line.junctions_.end(), circle.begin(), circle.end()); + }; + + Point2LL local_maxima_accumulator; + coord_t width_accumulator = 0; + size_t accumulator_count = 0; + + for (const auto& node : graph_.nodes) { if (! node.data_.hasBeading()) { continue; } - Beading& beading = node.data_.getBeading()->beading_; - if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true) && ! node.isCentral()) + const Beading& beading = node.data_.getBeading()->beading_; + if (beading.bead_widths.size() % 2 == 1 && node.isLocalMaximum(true)) { const size_t inset_index = beading.bead_widths.size() / 2; - constexpr bool is_odd = true; - if (inset_index >= generated_toolpaths.size()) + const coord_t width = beading.bead_widths[inset_index]; + local_maxima_accumulator += node.p_; + width_accumulator += width; + ++accumulator_count; + if (! node.isCentral()) { - generated_toolpaths.resize(inset_index + 1); + addCircleToToolpath(node.p_, width, inset_index); } - generated_toolpaths[inset_index].emplace_back(inset_index, is_odd); - ExtrusionLine& line = generated_toolpaths[inset_index].back(); - const coord_t width = beading.bead_widths[inset_index]; - // total area to be extruded is pi*(w/2)^2 = pi*w*w/4 - // Width a constant extrusion width w, that would be a length of pi*w/4 - // If we make a small circle to fill up the hole, then that circle would have a circumference of 2*pi*r - // So our circle needs to be such that r=w/8 - const coord_t r = width / 8; - constexpr coord_t n_segments = 6; - for (coord_t segment = 0; segment < n_segments; segment++) + } + } + + if (accumulator_count > 0) + { + bool replace_with_local_maxima = generated_toolpaths.empty() || generated_toolpaths[0].empty(); + coord_t total_path_length = 0; + if (! replace_with_local_maxima) + { + coord_t min_width = std::numeric_limits::max(); + for (const auto& line : generated_toolpaths[0]) { - double a = 2.0 * std::numbers::pi / n_segments * segment; - line.junctions_.emplace_back(node.p_ + Point2LL(r * cos(a), r * sin(a)), width, inset_index); + total_path_length += line.length(); + for (const ExtrusionJunction& j : line) + { + min_width = std::min(min_width, j.w_); + } } + replace_with_local_maxima |= total_path_length <= min_width / 2; + } + if (replace_with_local_maxima) + { + const coord_t width = width_accumulator / accumulator_count; + local_maxima_accumulator = local_maxima_accumulator / accumulator_count; + if (generated_toolpaths.empty()) + { + generated_toolpaths.emplace_back(); + } + else + { + generated_toolpaths[0].clear(); + } + addCircleToToolpath(local_maxima_accumulator, width, 0); } } } - // // ^^^^^^^^^^^^^^^^^^^^^ // TOOLPATH GENERATION diff --git a/src/WallToolPaths.cpp b/src/WallToolPaths.cpp index b144d0ebfb..c34aaf8d69 100644 --- a/src/WallToolPaths.cpp +++ b/src/WallToolPaths.cpp @@ -181,7 +181,7 @@ const std::vector& WallToolPaths::generate() scripta::PointVDI{ "width", &ExtrusionJunction::w_ }, scripta::PointVDI{ "perimeter_index", &ExtrusionJunction::perimeter_index_ }); - removeSmallLines(toolpaths_); + removeSmallFillLines(toolpaths_); scripta::log( "toolpaths_2", toolpaths_, @@ -274,13 +274,17 @@ void WallToolPaths::stitchToolPaths(std::vector& toolpaths, } } -void WallToolPaths::removeSmallLines(std::vector& toolpaths) +void WallToolPaths::removeSmallFillLines(std::vector& toolpaths) { for (VariableWidthLines& inset : toolpaths) { for (size_t line_idx = 0; line_idx < inset.size(); line_idx++) { ExtrusionLine& line = inset[line_idx]; + if (line.is_outer_wall()) + { + continue; + } coord_t min_width = std::numeric_limits::max(); for (const ExtrusionJunction& j : line) { diff --git a/src/utils/polygonUtils.cpp b/src/utils/polygonUtils.cpp index 84f367c586..3890055558 100644 --- a/src/utils/polygonUtils.cpp +++ b/src/utils/polygonUtils.cpp @@ -1386,16 +1386,6 @@ double PolygonUtils::relativeHammingDistance(const Shape& poly_a, const Shape& p return hamming_distance / total_area; } -Polygon PolygonUtils::makeCircle(const Point2LL mid, const coord_t radius, const AngleRadians a_step) -{ - Polygon circle; - for (double a = 0; a < 2 * std::numbers::pi; a += a_step) - { - circle.emplace_back(mid + Point2LL(radius * cos(a), radius * sin(a))); - } - return circle; -} - Polygon PolygonUtils::makeWheel(const Point2LL& mid, const coord_t inner_radius, const coord_t outer_radius, const size_t semi_nb_spokes, const size_t arc_angle_resolution) { Polygon wheel;