From 6018d326f4c7cf0b30d7f43c1c507899a61bb1ab Mon Sep 17 00:00:00 2001 From: "xun.zhang" Date: Thu, 2 Jan 2025 16:13:36 +0800 Subject: [PATCH] ENH: generate outer wall contour paths in arachne 1. Add OuterWallContourStrategy class to generate outer wall contour paths. 2. Fix top one wall issue in arachne jira:NONE Signed-off-by: xun.zhang Change-Id: I44574df765cdd0d0d3fc4f6c3f7b846dfb4fa21f --- .../BeadingStrategy/BeadingStrategy.hpp | 2 + .../BeadingStrategyFactory.cpp | 10 +- .../LimitedBeadingStrategy.cpp | 6 +- .../OuterWallContourStrategy.cpp | 83 ++++++++++++ .../OuterWallContourStrategy.hpp | 29 +++++ src/libslic3r/Arachne/WallToolPaths.cpp | 58 +++++++-- src/libslic3r/Arachne/WallToolPaths.hpp | 3 + src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/PerimeterGenerator.cpp | 120 +++++++++++------- 9 files changed, 256 insertions(+), 57 deletions(-) create mode 100644 src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.cpp create mode 100644 src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.hpp diff --git a/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp b/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp index 99e38239f..9b9c89c45 100644 --- a/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp +++ b/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategy.hpp @@ -13,6 +13,8 @@ namespace Slic3r::Arachne template constexpr T pi_div(const T div) { return static_cast(M_PI) / div; } +constexpr int WallContourMarkedWidth = 0; +constexpr int FirstWallContourMarkedWidth = 1; /*! * Mostly virtual base class template. * diff --git a/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategyFactory.cpp b/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategyFactory.cpp index 4044c9013..d30566e81 100644 --- a/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategyFactory.cpp +++ b/src/libslic3r/Arachne/BeadingStrategy/BeadingStrategyFactory.cpp @@ -8,6 +8,7 @@ #include "DistributedBeadingStrategy.hpp" #include "RedistributeBeadingStrategy.hpp" #include "OuterWallInsetBeadingStrategy.hpp" +#include "OuterWallContourStrategy.hpp" #include #include @@ -44,9 +45,16 @@ BeadingStrategyPtr BeadingStrategyFactory::makeStrategy( ret = std::make_unique(outer_wall_offset, std::move(ret)); } + //Apply the OuterWallContourStrategy last, since that adds a 1-width marker wall to mark the boundary of first beading. + BOOST_LOG_TRIVIAL(debug) << "Applying the First Beading Contour Strategy."; + ret = std::make_unique(std::move(ret)); + //Apply the LimitedBeadingStrategy last, since that adds a 0-width marker wall which other beading strategies shouldn't touch. BOOST_LOG_TRIVIAL(debug) << "Applying the Limited Beading meta-strategy with maximum bead count = " << max_bead_count << "."; - ret = std::make_unique(max_bead_count, std::move(ret)); + ret = std::make_unique(max_bead_count + 2, std::move(ret)); + + + return ret; } } // namespace Slic3r::Arachne diff --git a/src/libslic3r/Arachne/BeadingStrategy/LimitedBeadingStrategy.cpp b/src/libslic3r/Arachne/BeadingStrategy/LimitedBeadingStrategy.cpp index 97d854b41..9a31b3b75 100644 --- a/src/libslic3r/Arachne/BeadingStrategy/LimitedBeadingStrategy.cpp +++ b/src/libslic3r/Arachne/BeadingStrategy/LimitedBeadingStrategy.cpp @@ -48,7 +48,7 @@ LimitedBeadingStrategy::Beading LimitedBeadingStrategy::compute(coord_t thicknes const coord_t innermost_toolpath_location = ret.toolpath_locations[max_bead_count / 2 - 1]; const coord_t innermost_toolpath_width = ret.bead_widths[max_bead_count / 2 - 1]; ret.toolpath_locations.insert(ret.toolpath_locations.begin() + max_bead_count / 2, innermost_toolpath_location + innermost_toolpath_width / 2); - ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, 0); + ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, WallContourMarkedWidth); } return ret; } @@ -77,14 +77,14 @@ LimitedBeadingStrategy::Beading LimitedBeadingStrategy::compute(coord_t thicknes coord_t innermost_toolpath_location = ret.toolpath_locations[max_bead_count / 2 - 1]; coord_t innermost_toolpath_width = ret.bead_widths[max_bead_count / 2 - 1]; ret.toolpath_locations.insert(ret.toolpath_locations.begin() + max_bead_count / 2, innermost_toolpath_location + innermost_toolpath_width / 2); - ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, 0); + ret.bead_widths.insert(ret.bead_widths.begin() + max_bead_count / 2, WallContourMarkedWidth); //Symmetry on both sides. Symmetry is guaranteed since this code is stopped early if the bead_count <= max_bead_count, and never reaches this point then. const size_t opposite_bead = bead_count - (max_bead_count / 2 - 1); innermost_toolpath_location = ret.toolpath_locations[opposite_bead]; innermost_toolpath_width = ret.bead_widths[opposite_bead]; ret.toolpath_locations.insert(ret.toolpath_locations.begin() + opposite_bead, innermost_toolpath_location - innermost_toolpath_width / 2); - ret.bead_widths.insert(ret.bead_widths.begin() + opposite_bead, 0); + ret.bead_widths.insert(ret.bead_widths.begin() + opposite_bead, WallContourMarkedWidth); return ret; } diff --git a/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.cpp b/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.cpp new file mode 100644 index 000000000..c082e0e3d --- /dev/null +++ b/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.cpp @@ -0,0 +1,83 @@ + +#include "OuterWallContourStrategy.hpp" +#include "Point.hpp" + +namespace Slic3r::Arachne +{ + + +OuterWallContourStrategy::OuterWallContourStrategy(BeadingStrategyPtr parent) + : BeadingStrategy(*parent) + , parent(std::move(parent)) +{ +} + +std::string OuterWallContourStrategy::toString() const +{ + return std::string("OuterWallContourStrategy+") + parent->toString(); +} + +coord_t OuterWallContourStrategy::getTransitioningLength(coord_t lower_bead_count) const +{ + return parent->getTransitioningLength(lower_bead_count); +} + +float OuterWallContourStrategy::getTransitionAnchorPos(coord_t lower_bead_count) const +{ + return parent->getTransitionAnchorPos(lower_bead_count); +} + +std::vector OuterWallContourStrategy::getNonlinearThicknesses(coord_t lower_bead_count) const +{ + return parent->getNonlinearThicknesses(lower_bead_count); +} + + +coord_t OuterWallContourStrategy::getTransitionThickness(coord_t lower_bead_count) const +{ + if(lower_bead_count <= 1) + return parent->getTransitionThickness(lower_bead_count); + else if(lower_bead_count == 2 || lower_bead_count ==3) + return parent->getTransitionThickness(1); + return parent->getTransitionThickness(lower_bead_count-2); +} + + +coord_t OuterWallContourStrategy::getOptimalBeadCount(coord_t thickness) const +{ + coord_t parent_bead_count = parent->getOptimalBeadCount(thickness); + if(parent_bead_count <= 1) + return parent_bead_count; + return parent_bead_count + 2; +} + + +coord_t OuterWallContourStrategy::getOptimalThickness(coord_t bead_count) const +{ + if (bead_count <= 1) + return parent->getOptimalThickness(bead_count); + return parent->getOptimalThickness(bead_count - 2) + 2; +} + +BeadingStrategy::Beading OuterWallContourStrategy::compute(coord_t thickness, coord_t bead_count) const +{ + if (bead_count <= 1) + return parent->compute(thickness, bead_count); + + assert(bead_count >= 3); + Beading ret = parent->compute(thickness, bead_count - 2); + if(ret.toolpath_locations.size() == 1){ + return ret; + } + if(ret.toolpath_locations.size() > 0 ){ + assert(ret.bead_widths.size()>0); + double location = ret.toolpath_locations.front() + ret.bead_widths.front() / 2; + double location_reverse = ret.toolpath_locations.back() - ret.bead_widths.back() / 2; + ret.toolpath_locations.insert(ret.toolpath_locations.begin()+1, location); + ret.bead_widths.insert(ret.bead_widths.begin()+1, FirstWallContourMarkedWidth); + ret.toolpath_locations.insert((ret.toolpath_locations.rbegin()+1).base(), location_reverse); + ret.bead_widths.insert((ret.bead_widths.rbegin()).base(), FirstWallContourMarkedWidth); + } + return ret; +} +} // namespace Slic3r::Arachne \ No newline at end of file diff --git a/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.hpp b/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.hpp new file mode 100644 index 000000000..1ecb4b40b --- /dev/null +++ b/src/libslic3r/Arachne/BeadingStrategy/OuterWallContourStrategy.hpp @@ -0,0 +1,29 @@ +#ifndef OUTER_WALL_CONTOUR_STRATEGY_H +#define OUTER_WALL_CONTOUR_STRATEGY_H + +#include "BeadingStrategy.hpp" +namespace Slic3r::Arachne +{ + +class OuterWallContourStrategy : public BeadingStrategy +{ +public: + OuterWallContourStrategy(BeadingStrategyPtr parent); + ~OuterWallContourStrategy() override = default; + + Beading compute(coord_t thickness, coord_t bead_count) const override; + coord_t getOptimalThickness(coord_t bead_count) const override; + coord_t getTransitionThickness(coord_t lower_bead_count) const override; + coord_t getOptimalBeadCount(coord_t thickness) const override; + std::string toString() const override; + + coord_t getTransitioningLength(coord_t lower_bead_count) const override; + float getTransitionAnchorPos(coord_t lower_bead_count) const override; + std::vector getNonlinearThicknesses(coord_t lower_bead_count) const override; + +protected: + const BeadingStrategyPtr parent; +}; + +} +#endif \ No newline at end of file diff --git a/src/libslic3r/Arachne/WallToolPaths.cpp b/src/libslic3r/Arachne/WallToolPaths.cpp index 5035d8d92..84ce20930 100644 --- a/src/libslic3r/Arachne/WallToolPaths.cpp +++ b/src/libslic3r/Arachne/WallToolPaths.cpp @@ -700,27 +700,37 @@ const std::vector &WallToolPaths::getToolPaths() void WallToolPaths::separateOutInnerContour() { + enum PathType{ + ActualPath, + WallContour, + FirstWallContour + }; + //We'll remove all 0-width paths from the original toolpaths and store them separately as polygons. std::vector actual_toolpaths; actual_toolpaths.reserve(toolpaths.size()); //A bit too much, but the correct order of magnitude. - std::vector contour_paths; - contour_paths.reserve(toolpaths.size() / inset_count); + std::vector wall_contour_paths; + wall_contour_paths.reserve(toolpaths.size() / inset_count); + std::vector first_wall_contour_paths; inner_contour.clear(); + first_wall_contour.clear(); for (const VariableWidthLines &inset : toolpaths) { if (inset.empty()) continue; - bool is_contour = false; + PathType type; for (const ExtrusionLine &line : inset) { for (const ExtrusionJunction &j : line) { - if (j.w == 0) - is_contour = true; + if (j.w == Arachne::WallContourMarkedWidth) + type = WallContour; + else if(j.w == Arachne::FirstWallContourMarkedWidth) + type = FirstWallContour; else - is_contour = false; + type = ActualPath; break; } } - if (is_contour) { + if (type==WallContour) { #ifdef DEBUG for (const ExtrusionLine &line : inset) for (const ExtrusionJunction &j : line) @@ -732,7 +742,16 @@ void WallToolPaths::separateOutInnerContour() else if (line.is_closed) // sometimes an very small even polygonal wall is not stitched into a polygon inner_contour.emplace_back(line.toPolygon()); } - } else { + } + else if (type == FirstWallContour){ + for (const ExtrusionLine &line : inset) { + if (line.is_odd) + continue; + else if (line.is_closed) + first_wall_contour.emplace_back(line.toPolygon()); + } + } + else { actual_toolpaths.emplace_back(inset); } } @@ -747,6 +766,15 @@ void WallToolPaths::separateOutInnerContour() //This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon. //The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation. inner_contour = union_(inner_contour, ClipperLib::PolyFillType::pftEvenOdd); + first_wall_contour = union_(first_wall_contour, ClipperLib::PolyFillType::pftEvenOdd); + + // restore the idx of non first wall paths + for (auto& paths : toolpaths) { + for (auto& path : paths) { + if (path.inset_idx > 1) + path.inset_idx -= 1; + } + } } const Polygons& WallToolPaths::getInnerContour() @@ -762,6 +790,20 @@ const Polygons& WallToolPaths::getInnerContour() return inner_contour; } +const Polygons& WallToolPaths::getFirstWallContour() +{ + if (!toolpaths_generated && inset_count > 0) + { + generate(); + } + else if(inset_count == 0) + { + return {}; + } + return first_wall_contour; +} + + bool WallToolPaths::removeEmptyToolPaths(std::vector &toolpaths) { toolpaths.erase(std::remove_if(toolpaths.begin(), toolpaths.end(), [](const VariableWidthLines& lines) diff --git a/src/libslic3r/Arachne/WallToolPaths.hpp b/src/libslic3r/Arachne/WallToolPaths.hpp index ed8e4b1f3..35e7f79a8 100644 --- a/src/libslic3r/Arachne/WallToolPaths.hpp +++ b/src/libslic3r/Arachne/WallToolPaths.hpp @@ -78,6 +78,8 @@ public: */ const Polygons& getInnerContour(); + const Polygons& getFirstWallContour(); + /*! * Removes empty paths from the toolpaths * \param toolpaths the VariableWidthPaths generated with \p generate() @@ -131,6 +133,7 @@ private: bool toolpaths_generated; // toolpaths; //slices->surfaces) { - bool generate_one_wall = false; bool generate_one_wall_by_first_layer = this->object_config->only_one_wall_first_layer && layer_id == 0; bool generate_one_wall_by_top_one_wall = this->object_config->top_one_wall_type == TopOneWallType::Topmost && this->upper_slices == nullptr || this->object_config->top_one_wall_type == TopOneWallType::Alltop; - - generate_one_wall = generate_one_wall_by_first_layer || generate_one_wall_by_top_one_wall; + bool generate_one_wall = generate_one_wall_by_first_layer || generate_one_wall_by_top_one_wall; // detect how many perimeters must be generated for this island int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops @@ -1815,35 +1812,6 @@ void PerimeterGenerator::process_arachne() if (last.size() != 1 || new_size != surface.expolygon.num_contours()) apply_circle_compensation = false; - std::vector circle_poly_indices; - Polygons last_p; - if (apply_circle_compensation) - last_p = to_polygons_with_flag(last.front(), surface.counter_circle_compensation, surface.holes_circle_compensation, circle_poly_indices); - else - last_p = to_polygons(last); - - // check whether to activate one wall mode - if (generate_one_wall && !generate_one_wall_by_first_layer) - { - ExPolygons top_expolys; - ExPolygons infill_contour_by_one_wall = offset_ex(last, -(ext_perimeter_width + perimeter_spacing) / 2.f); - - BoundingBox infill_bbox = get_extents(infill_contour_by_one_wall); - infill_bbox.offset(EPSILON); - - Polygons upper_polygons_clipped; - if (this->upper_slices) - upper_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_bbox); - top_expolys = diff_ex(infill_contour_by_one_wall, upper_polygons_clipped); - - Polygons lower_polygons_clipped; - if (this->lower_slices) - lower_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_bbox); - ExPolygons bottom_expolys = offset_ex(diff_ex(top_expolys, lower_polygons_clipped), std::max(ext_perimeter_spacing, perimeter_width)); - - top_expolys = diff_ex(top_expolys, bottom_expolys); - generate_one_wall = should_enable_top_one_wall(last, top_expolys); - } double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end()); Arachne::WallToolPathsParams input_params; @@ -1864,15 +1832,77 @@ void PerimeterGenerator::process_arachne() input_params.wall_distribution_count = this->object_config->wall_distribution_count.value; } - coord_t real_loop_number = generate_one_wall ? 1 : loop_number + 1; - - Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, real_loop_number, 0, layer_height, input_params); + std::vector circle_poly_indices; + Polygons last_p; + if (apply_circle_compensation) + last_p = to_polygons_with_flag(last.front(), surface.counter_circle_compensation, surface.holes_circle_compensation, circle_poly_indices); + else + last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, loop_number + 1, 0, layer_height, input_params); if (apply_circle_compensation) wallToolPaths.EnableHoleCompensation(true, circle_poly_indices); - std::vector perimeters = wallToolPaths.getToolPaths(); - ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour()); + ExPolygons top_expolys_by_one_wall; + ExPolygons infill_contour_by_one_wall = union_ex(wallToolPaths.getFirstWallContour()); + + // do detail check whether to enable one wall + if (generate_one_wall && !generate_one_wall_by_first_layer) + { + BoundingBox infill_bbox = get_extents(infill_contour_by_one_wall); + infill_bbox.offset(EPSILON); + + Polygons upper_polygons_clipped; + if (this->upper_slices) + upper_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_bbox); + top_expolys_by_one_wall = diff_ex(infill_contour_by_one_wall, upper_polygons_clipped); + + Polygons lower_polygons_clipped; + if (this->lower_slices) + lower_polygons_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_bbox); + ExPolygons bottom_expolys = diff_ex(top_expolys_by_one_wall, lower_polygons_clipped); + + top_expolys_by_one_wall = diff_ex(top_expolys_by_one_wall, bottom_expolys); + generate_one_wall = should_enable_top_one_wall(last, top_expolys_by_one_wall); + if (generate_one_wall) + top_expolys_by_one_wall = offset_ex(top_expolys_by_one_wall, perimeter_width); + } + + std::vector total_perimeters; + ExPolygons infill_contour; + if (!generate_one_wall) { + total_perimeters = wallToolPaths.getToolPaths(); + infill_contour = union_ex(wallToolPaths.getInnerContour()); + } + else if(loop_number >0) { + last = diff_ex(infill_contour_by_one_wall, top_expolys_by_one_wall); + last_p = to_polygons(last); + Arachne::WallToolPaths paths_new(last_p, perimeter_spacing, perimeter_spacing, loop_number, 0, layer_height, input_params); + + auto old_perimeters = wallToolPaths.getToolPaths(); + auto new_perimeters = paths_new.getToolPaths(); + for (auto& perimeters : old_perimeters) { + if (std::find_if(perimeters.begin(), perimeters.end(), [](auto& item) {return item.inset_idx == 0; }) != perimeters.end()) { + total_perimeters.emplace_back(); + for (auto& p : perimeters) { + if (p.inset_idx == 0) + total_perimeters.back().emplace_back(std::move(p)); + } + } + } + + for (auto& perimeters : new_perimeters) { + if (!perimeters.empty()) { + for (auto& p : perimeters){ + p.inset_idx += 1; + } + total_perimeters.emplace_back(std::move(perimeters)); + } + } + infill_contour = union_ex(union_ex(paths_new.getInnerContour()), top_expolys_by_one_wall); + infill_contour = intersection_ex(infill_contour, infill_contour_by_one_wall); + } + #ifdef ARACHNE_DEBUG { static int iRun = 0; @@ -1883,15 +1913,15 @@ void PerimeterGenerator::process_arachne() // All closed ExtrusionLine should have the same the first and the last point. // But in rare cases, Arachne produce ExtrusionLine marked as closed but without // equal the first and the last point. - assert([&perimeters = std::as_const(perimeters)]() -> bool { - for (const Arachne::VariableWidthLines& perimeter : perimeters) + assert([&total_perimeters = std::as_const(total_perimeters)]() -> bool { + for (const Arachne::VariableWidthLines& perimeter : total_perimeters) for (const Arachne::ExtrusionLine& el : perimeter) if (el.is_closed && el.junctions.front().p != el.junctions.back().p) return false; return true; }()); - int start_perimeter = int(perimeters.size()) - 1; + int start_perimeter = int(total_perimeters.size()) - 1; int end_perimeter = -1; int direction = -1; @@ -1899,15 +1929,15 @@ void PerimeterGenerator::process_arachne() this->object_config->wall_sequence == WallSequence::OuterInner || this->object_config->wall_sequence == WallSequence::InnerOuterInner; if (is_outer_wall_first) { start_perimeter = 0; - end_perimeter = int(perimeters.size()); + end_perimeter = int(total_perimeters.size()); direction = 1; } std::vector all_extrusions; for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) { - if (perimeters[perimeter_idx].empty()) + if (total_perimeters[perimeter_idx].empty()) continue; - for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx]) + for (Arachne::ExtrusionLine& wall : total_perimeters[perimeter_idx]) all_extrusions.emplace_back(&wall); } @@ -2069,7 +2099,7 @@ void PerimeterGenerator::process_arachne() if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty()) this->loops->append(extrusion_coll); - const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; + const coord_t spacing = (total_perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing; // collapse too narrow infill areas const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));