diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index dbb79a1d86..6203a0725b 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -67,8 +67,6 @@ set(lisbslic3r_sources Fill/FillBase.hpp Fill/FillConcentric.cpp Fill/FillConcentric.hpp - Fill/FillConcentricWGapFill.cpp - Fill/FillConcentricWGapFill.hpp Fill/FillConcentricInternal.cpp Fill/FillConcentricInternal.hpp Fill/FillHoneycomb.cpp diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 7aae8da152..3d2be9cf39 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -12,6 +12,7 @@ #include "FillBase.hpp" #include "FillRectilinear.hpp" #include "FillConcentricInternal.hpp" +#include "FillConcentric.hpp" #define NARROW_INFILL_AREA_THRESHOLD 3 @@ -407,6 +408,11 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: assert(fill_concentric != nullptr); fill_concentric->print_config = &this->object()->print()->config(); fill_concentric->print_object_config = &this->object()->config(); + } else if (surface_fill.params.pattern == ipConcentric) { + FillConcentric *fill_concentric = dynamic_cast(f.get()); + assert(fill_concentric != nullptr); + fill_concentric->print_config = &this->object()->print()->config(); + fill_concentric->print_object_config = &this->object()->config(); } // calculate flow spacing for infill pattern generation @@ -434,6 +440,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.anchor_length = surface_fill.params.anchor_length; params.anchor_length_max = surface_fill.params.anchor_length_max; params.resolution = resolution; + params.use_arachne = surface_fill.params.pattern == ipConcentric; // BBS params.flow = surface_fill.params.flow; diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 74ffdc372b..ae4811ca87 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -9,6 +9,7 @@ #include "../PrintConfig.hpp" #include "../Surface.hpp" #include "../libslic3r.h" +#include "../VariableWidth.hpp" #include "FillBase.hpp" #include "FillConcentric.hpp" @@ -21,7 +22,6 @@ #include "FillAdaptive.hpp" #include "FillLightning.hpp" // BBS: new infill pattern header -#include "FillConcentricWGapFill.hpp" #include "FillConcentricInternal.hpp" // #define INFILL_DEBUG_OUTPUT @@ -58,7 +58,6 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipLightning: return new FillLightning::Filler(); #endif // HAS_LIGHTNING_INFILL // BBS: for internal solid infill only - case ipConcentricGapFill: return new FillConcentricWGapFill(); case ipConcentricInternal: return new FillConcentricInternal(); // BBS: for bottom and top surface only case ipMonotonicLine: return new FillMonotonicLineWGapFill(); @@ -107,16 +106,31 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) return polylines_out; } +ThickPolylines Fill::fill_surface_arachne(const Surface* surface, const FillParams& params) +{ + // Perform offset. + Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing))); + // Create the infills for each of the regions. + ThickPolylines thick_polylines_out; + for (ExPolygon& expoly : expp) + _fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), thick_polylines_out); + return thick_polylines_out; +} + // BBS: this method is used to fill the ExtrusionEntityCollection. It call fill_surface by default void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out) { Polylines polylines; + ThickPolylines thick_polylines; try { - polylines = this->fill_surface(surface, params); + if (params.use_arachne) + thick_polylines = this->fill_surface_arachne(surface, params); + else + polylines = this->fill_surface(surface, params); } catch (InfillFailedException&) {} - if (!polylines.empty()) { + if (!polylines.empty() || !thick_polylines.empty()) { // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) double flow_mm3_per_mm = params.flow.mm3_per_mm(); @@ -136,10 +150,17 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para out.push_back(eec = new ExtrusionEntityCollection()); // Only concentric fills are not sorted. eec->no_sort = this->no_sort(); - extrusion_entities_append_paths( - eec->entities, std::move(polylines), - params.extrusion_role, - flow_mm3_per_mm, float(flow_width), params.flow.height()); + if (params.use_arachne) { + Flow new_flow = params.flow.with_spacing(float(this->spacing)); + variable_width(thick_polylines, params.extrusion_role, new_flow, eec->entities); + thick_polylines.clear(); + } + else { + extrusion_entities_append_paths( + eec->entities, std::move(polylines), + params.extrusion_role, + flow_mm3_per_mm, float(flow_width), params.flow.height()); + } } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index fb444494a8..74a3f630e1 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -63,6 +63,9 @@ struct FillParams // in this case we don't try to make more continuous paths bool complete { false }; + // For Concentric infill, to switch between Classic and Arachne. + bool use_arachne{ false }; + // BBS Flow flow; ExtrusionRole extrusion_role{ ExtrusionRole(0) }; @@ -121,6 +124,7 @@ public: // Perform the fill. virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); + virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params); // BBS: this method is used to fill the ExtrusionEntityCollection. // It call fill_surface by default @@ -149,6 +153,13 @@ protected: ExPolygon /* expolygon */, Polylines & /* polylines_out */) {}; + // Used for concentric infill to generate ThickPolylines using Arachne. + virtual void _fill_surface_single(const FillParams& params, + unsigned int thickness_layers, + const std::pair& direction, + ExPolygon expolygon, + ThickPolylines& thick_polylines_out) {} + virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; } virtual std::pair _infill_direction(const Surface *surface) const; diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index d5997552b9..fe9b79e09b 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -1,6 +1,8 @@ #include "../ClipperUtils.hpp" #include "../ExPolygon.hpp" #include "../Surface.hpp" +#include "../VariableWidth.hpp" +#include "Arachne/WallToolPaths.hpp" #include "FillConcentric.hpp" @@ -61,4 +63,84 @@ void FillConcentric::_fill_surface_single( // We want the loops to be split inside the G-code generator to get optimum path planning. } +void FillConcentric::_fill_surface_single(const FillParams& params, + unsigned int thickness_layers, + const std::pair& direction, + ExPolygon expolygon, + ThickPolylines& thick_polylines_out) +{ + assert(params.use_arachne); + assert(this->print_config != nullptr && this->print_object_config != nullptr); + + // no rotation is supported for this infill pattern + Point bbox_size = expolygon.contour.bounding_box().size(); + coord_t min_spacing = scaled(this->spacing); + + if (params.density > 0.9999f && !params.dont_adjust) { + coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1; + Polygons polygons = offset(expolygon, float(min_spacing) / 2.f); + + double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end()); + Arachne::WallToolPathsParams input_params; + input_params.min_bead_width = 0.85 * min_nozzle_diameter; + input_params.min_feature_size = 0.1; + input_params.wall_transition_length = 1.0 * min_nozzle_diameter; + input_params.wall_transition_angle = 10; + input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter; + input_params.wall_distribution_count = 1; + input_params.wall_add_middle_threshold = 0.75; + input_params.wall_split_middle_threshold = 0.5; + + Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, input_params); + + std::vector loops = wallToolPaths.getToolPaths(); + std::vector all_extrusions; + for (Arachne::VariableWidthLines& loop : loops) { + if (loop.empty()) + continue; + for (const Arachne::ExtrusionLine& wall : loop) + all_extrusions.emplace_back(&wall); + } + + // Split paths using a nearest neighbor search. + size_t firts_poly_idx = thick_polylines_out.size(); + Point last_pos(0, 0); + for (const Arachne::ExtrusionLine* extrusion : all_extrusions) { + if (extrusion->empty()) + continue; + + ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion); + if (extrusion->is_closed && thick_polyline.points.front() == thick_polyline.points.back() && thick_polyline.width.front() == thick_polyline.width.back()) { + thick_polyline.points.pop_back(); + assert(thick_polyline.points.size() * 2 == thick_polyline.width.size()); + int nearest_idx = last_pos.nearest_point_index(thick_polyline.points); + std::rotate(thick_polyline.points.begin(), thick_polyline.points.begin() + nearest_idx, thick_polyline.points.end()); + std::rotate(thick_polyline.width.begin(), thick_polyline.width.begin() + 2 * nearest_idx, thick_polyline.width.end()); + thick_polyline.points.emplace_back(thick_polyline.points.front()); + } + thick_polylines_out.emplace_back(std::move(thick_polyline)); + last_pos = thick_polylines_out.back().last_point(); + } + + // clip the paths to prevent the extruder from getting exactly on the first point of the loop + // Keep valid paths only. + size_t j = firts_poly_idx; + for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) { + thick_polylines_out[i].clip_end(this->loop_clipping); + if (thick_polylines_out[i].is_valid()) { + if (j < i) + thick_polylines_out[j] = std::move(thick_polylines_out[i]); + ++j; + } + } + if (j < thick_polylines_out.size()) + thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end()); + } + else { + Polylines polylines; + this->_fill_surface_single(params, thickness_layers, direction, expolygon, polylines); + append(thick_polylines_out, to_thick_polylines(std::move(polylines), min_spacing)); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillConcentric.hpp b/src/libslic3r/Fill/FillConcentric.hpp index 8bf01d11d2..2e63dcdc88 100644 --- a/src/libslic3r/Fill/FillConcentric.hpp +++ b/src/libslic3r/Fill/FillConcentric.hpp @@ -19,7 +19,18 @@ protected: ExPolygon expolygon, Polylines &polylines_out) override; + void _fill_surface_single(const FillParams& params, + unsigned int thickness_layers, + const std::pair& direction, + ExPolygon expolygon, + ThickPolylines& thick_polylines_out) override; + bool no_sort() const override { return true; } + + const PrintConfig* print_config = nullptr; + const PrintObjectConfig* print_object_config = nullptr; + + friend class Layer; }; } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillConcentricWGapFill.cpp b/src/libslic3r/Fill/FillConcentricWGapFill.cpp deleted file mode 100644 index 8f037fc9a5..0000000000 --- a/src/libslic3r/Fill/FillConcentricWGapFill.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include "../ClipperUtils.hpp" -#include "../ExPolygon.hpp" -#include "../Surface.hpp" -#include "../VariableWidth.hpp" -#include "../ShortestPath.hpp" - -#include "FillConcentricWGapFill.hpp" - -namespace Slic3r { - -const float concentric_overlap_threshold = 0.02; - -void FillConcentricWGapFill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out) -{ - //BBS: FillConcentricWGapFill.cpp is absolutely newly add by BBL for narrow internal solid infill area to reduce vibration - // Because the area is narrow, we should not use the surface->expolygon which has overlap with perimeter, but - // use no_overlap_expolygons instead to avoid overflow in narrow area. - //Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->spacing))); - float min_spacing = this->spacing * (1 - concentric_overlap_threshold); - Slic3r::ExPolygons expp = offset2_ex(this->no_overlap_expolygons, -double(scale_(0.5 * this->spacing + 0.5 * min_spacing) - 1), - +double(scale_(0.5 * min_spacing) - 1)); - // Create the infills for each of the regions. - Polylines polylines_out; - for (size_t i = 0; i < expp.size(); ++i) { - ExPolygon expolygon = expp[i]; - - coord_t distance = scale_(this->spacing / params.density); - if (params.density > 0.9999f && !params.dont_adjust) { - distance = scale_(this->spacing); - } - - ExPolygons gaps; - Polygons loops = (Polygons)expolygon; - ExPolygons last = { expolygon }; - bool first = true; - while (!last.empty()) { - ExPolygons next_onion = offset2_ex(last, -double(distance + scale_(this->spacing) / 2), +double(scale_(this->spacing) / 2)); - for (auto it = next_onion.begin(); it != next_onion.end(); it++) { - Polygons temp_loops = (Polygons)(*it); - loops.insert(loops.end(), temp_loops.begin(), temp_loops.end()); - } - append(gaps, diff_ex( - offset(last, -0.5f * distance), - offset(next_onion, 0.5f * distance + 10))); // 10 is safty offset - last = next_onion; - if (first && !this->no_overlap_expolygons.empty()) { - gaps = intersection_ex(gaps, this->no_overlap_expolygons); - } - first = false; - } - - ExtrusionRole good_role = params.extrusion_role; - ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); - coll_nosort->no_sort = this->no_sort(); //can be sorted inside the pass - extrusion_entities_append_loops( - coll_nosort->entities, std::move(loops), - good_role, - params.flow.mm3_per_mm(), - params.flow.width(), - params.flow.height()); - - //BBS: add internal gapfills between infill loops - if (!gaps.empty() && params.density >= 1) { - double min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE); - double max = 2. * distance; - ExPolygons gaps_ex = diff_ex( - offset2_ex(gaps, -float(min / 2), float(min / 2)), - offset2_ex(gaps, -float(max / 2), float(max / 2)), - ApplySafetyOffset::Yes); - //BBS: sort the gap_ex to avoid mess travel - Points ordering_points; - ordering_points.reserve(gaps_ex.size()); - ExPolygons gaps_ex_sorted; - gaps_ex_sorted.reserve(gaps_ex.size()); - for (const ExPolygon &ex : gaps_ex) - ordering_points.push_back(ex.contour.first_point()); - std::vector order = chain_points(ordering_points); - for (size_t i : order) - gaps_ex_sorted.emplace_back(std::move(gaps_ex[i])); - - ThickPolylines polylines; - for (ExPolygon& ex : gaps_ex_sorted) { - //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. - ex.douglas_peucker(SCALED_RESOLUTION * 0.1); - ex.medial_axis(max, min, &polylines); - } - - if (!polylines.empty() && !is_bridge(good_role)) { - ExtrusionEntityCollection gap_fill; - variable_width(polylines, erGapFill, params.flow, gap_fill.entities); - coll_nosort->append(std::move(gap_fill.entities)); - } - } - - if (!coll_nosort->entities.empty()) - out.push_back(coll_nosort); - else - delete coll_nosort; - } - - //BBS: add external gapfill between perimeter and infill - ExPolygons external_gaps = diff_ex(this->no_overlap_expolygons, offset_ex(expp, double(scale_(0.5 * this->spacing))), ApplySafetyOffset::Yes); - external_gaps = union_ex(external_gaps); - if (!this->no_overlap_expolygons.empty()) - external_gaps = intersection_ex(external_gaps, this->no_overlap_expolygons); - - if (!external_gaps.empty()) { - double min = 0.4 * scale_(params.flow.nozzle_diameter()) * (1 - INSET_OVERLAP_TOLERANCE); - double max = 2. * params.flow.scaled_width(); - //BBS: collapse, be sure we don't gapfill where the perimeters are already touching each other (negative spacing). - min = std::max(min, (double)Flow::rounded_rectangle_extrusion_width_from_spacing((float)EPSILON, (float)params.flow.height())); - ExPolygons external_gaps_collapsed = offset2_ex(external_gaps, double(-min / 2), double(+min / 2)); - - ThickPolylines polylines; - for (ExPolygon& ex : external_gaps_collapsed) { - //BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well. - ex.douglas_peucker(SCALED_RESOLUTION * 0.1); - ex.medial_axis(max, min, &polylines); - } - - ExtrusionEntityCollection* coll_external_gapfill = new ExtrusionEntityCollection(); - coll_external_gapfill->no_sort = this->no_sort(); - if (!polylines.empty() && !is_bridge(params.extrusion_role)) { - ExtrusionEntityCollection gap_fill; - variable_width(polylines, erGapFill, params.flow, gap_fill.entities); - coll_external_gapfill->append(std::move(gap_fill.entities)); - } - if (!coll_external_gapfill->entities.empty()) - out.push_back(coll_external_gapfill); - else - delete coll_external_gapfill; - } -} - -} \ No newline at end of file diff --git a/src/libslic3r/Fill/FillConcentricWGapFill.hpp b/src/libslic3r/Fill/FillConcentricWGapFill.hpp deleted file mode 100644 index 6bf5cdb5d6..0000000000 --- a/src/libslic3r/Fill/FillConcentricWGapFill.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef slic3r_FillConcentricWGapFil_hpp_ -#define slic3r_FillConcentricWGapFil_hpp_ - -#include "FillBase.hpp" - -namespace Slic3r { - -class FillConcentricWGapFill : public Fill -{ -public: - ~FillConcentricWGapFill() override = default; - void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; - -protected: - Fill* clone() const override { return new FillConcentricWGapFill(*this); }; - bool no_sort() const override { return true; } -}; - -} // namespace Slic3r - -#endif // slic3r_FillConcentricWGapFil_hpp_ diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 45361f68e2..ac00888d31 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -239,6 +239,18 @@ public: std::pair endpoints; }; +inline ThickPolylines to_thick_polylines(Polylines&& polylines, const coordf_t width) +{ + ThickPolylines out; + out.reserve(polylines.size()); + for (Polyline& polyline : polylines) { + out.emplace_back(); + out.back().width.assign((polyline.points.size() - 1) * 2, width); + out.back().points = std::move(polyline.points); + } + return out; +} + class Polyline3 : public MultiPoint3 { public: diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 64dbc4a62e..76e3ecc217 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -46,7 +46,7 @@ enum class FuzzySkinType { enum InfillPattern : int { ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb, - ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricGapFill, ipConcentricInternal, + ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal, #if HAS_LIGHTNING_INFILL ipLightning, #endif // HAS_LIGHTNING_INFILL