From a8141ef360092aae9f2d93399ef6a3d359e8a11e Mon Sep 17 00:00:00 2001 From: Rodrigo <162915171+RF47@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:07:33 -0300 Subject: [PATCH] Infill Line Multiplier (#9432) * Infill Line Multiplier * Modular Offset Function * Lightning multiline * Crosshatch Multiline ipCrosshatch * cleaning Cleaning clean2 * 3d Honeycomb cut poliline ends * Fill Tpmsd Multiline Fill Tpmsd Multiline * Update Multiline function multiline funcion simplify * Update FillTpmsD * FillHoneycomb * Update src/libslic3r/PrintConfig.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix Honeycomb Multiline Simplify polylines in honeycomb infill generation * Improve multiline infill support and pattern simplification Moved multiline infill application after pattern translation and simplification in Fill3DHoneycomb, and added multiline support to FillAdaptive. Updated honeycomb and 3D honeycomb infill to simplify polylines to 5x line width. Extended GUI and config to support multiline for Adaptive Cubic infill pattern and clarified max value comment. minimum changes Co-Authored-By: Ian Bassi <12130714+ianalexis@users.noreply.github.com> * Increase multiline fill spacing in honeycomb infill Adjusts the spacing parameter in the multiline_fill function to 1.1 times the original spacing, potentially improving infill distribution or print quality. * Refine fill_multiline tooltip and pattern support logic Updated the tooltip for the 'fill_multiline' parameter to improve clarity and punctuation. Refactored the logic in ConfigManipulation.cpp to clarify which infill patterns support multiline infill. * better management of non supported infill patterns --------- Co-authored-by: SoftFever Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Ian Bassi Co-authored-by: Ian Bassi <12130714+ianalexis@users.noreply.github.com> --- src/libslic3r/Fill/Fill.cpp | 7 +++ src/libslic3r/Fill/Fill3DHoneycomb.cpp | 21 ++++++-- src/libslic3r/Fill/FillAdaptive.cpp | 4 ++ src/libslic3r/Fill/FillBase.cpp | 67 +++++++++++++++++++++++--- src/libslic3r/Fill/FillBase.hpp | 4 +- src/libslic3r/Fill/FillCrossHatch.cpp | 8 ++- src/libslic3r/Fill/FillGyroid.cpp | 13 +++-- src/libslic3r/Fill/FillHoneycomb.cpp | 15 +++--- src/libslic3r/Fill/FillLightning.cpp | 6 ++- src/libslic3r/Fill/FillRectilinear.cpp | 24 ++++++--- src/libslic3r/Fill/FillTpmsD.cpp | 4 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 8 +++ src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 15 ++++++ src/slic3r/GUI/Tab.cpp | 1 + 17 files changed, 164 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index e56d4b2ec7..e001690ce4 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -41,6 +41,8 @@ struct SurfaceFillParams // FillParams float density = 0.f; + // Infill line multiplier count. + int multiline = 1; // Don't adjust spacing to fill the space evenly. // bool dont_adjust = false; // Length of the infill anchor along the perimeter line. @@ -88,6 +90,7 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(overlap); RETURN_COMPARE_NON_EQUAL(angle); RETURN_COMPARE_NON_EQUAL(density); + RETURN_COMPARE_NON_EQUAL(multiline); // RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); RETURN_COMPARE_NON_EQUAL(anchor_length); RETURN_COMPARE_NON_EQUAL(anchor_length_max); @@ -117,6 +120,7 @@ struct SurfaceFillParams this->bridge == rhs.bridge && this->bridge_angle == rhs.bridge_angle && this->density == rhs.density && + this->multiline == rhs.multiline && // this->dont_adjust == rhs.dont_adjust && this->anchor_length == rhs.anchor_length && this->anchor_length_max == rhs.anchor_length_max && @@ -647,6 +651,7 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p params.extruder = layerm.region().extruder(extrusion_role); params.pattern = region_config.sparse_infill_pattern.value; params.density = float(region_config.sparse_infill_density); + params.multiline = int(region_config.fill_multiline); params.lattice_angle_1 = region_config.lattice_angle_1; params.lattice_angle_2 = region_config.lattice_angle_2; params.infill_overhang_angle = region_config.infill_overhang_angle; @@ -1023,6 +1028,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // apply half spacing using this flow's own spacing and generate infill FillParams params; params.density = float(0.01 * surface_fill.params.density); + params.multiline = surface_fill.params.multiline; params.dont_adjust = false; // surface_fill.params.dont_adjust; params.anchor_length = surface_fill.params.anchor_length; params.anchor_length_max = surface_fill.params.anchor_length_max; @@ -1199,6 +1205,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc params.lattice_angle_1 = surface_fill.params.lattice_angle_1; params.lattice_angle_2 = surface_fill.params.lattice_angle_2; params.infill_overhang_angle = surface_fill.params.infill_overhang_angle; + params.multiline = surface_fill.params.multiline; for (ExPolygon &expoly : surface_fill.expolygons) { // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index eadcfbf537..34a1ff1b50 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -1,7 +1,7 @@ #include "../ClipperUtils.hpp" #include "../ShortestPath.hpp" #include "../Surface.hpp" - +#include "FillBase.hpp" #include "Fill3DHoneycomb.hpp" namespace Slic3r { @@ -212,7 +212,7 @@ void Fill3DHoneycomb::_fill_surface_single( // = 4 * integrate(func=4*x(sqrt(2) - 1) + 1, from=0, to=0.25) // = (sqrt(2) + 1) / 2 [... I think] // make a first guess at the preferred grid Size - coordf_t gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) / params.density); + coordf_t gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density); // This density calculation is incorrect for many values > 25%, possibly // due to quantisation error, so this value is used as a first guess, then the @@ -228,7 +228,7 @@ void Fill3DHoneycomb::_fill_surface_single( layersPerModule = 2; // re-adjust the grid size for a partial octahedral path // (scale of 1.1 guessed based on modeling) - gridSize = (scale_(this->spacing) * 1.1 / params.density); + gridSize = (scale_(this->spacing) * 1.1 * params.multiline / params.density); // re-adjust zScale to make layering consistent zScale = (gridSize * 2) / (layersPerModule * layerHeight); } else { @@ -238,7 +238,7 @@ void Fill3DHoneycomb::_fill_surface_single( // re-adjust zScale to make layering consistent zScale = (gridSize * 2) / (layersPerModule * layerHeight); // re-adjust the grid size to account for the new zScale - gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) / params.density); + gridSize = (scale_(this->spacing) * ((zScale + 1.) / 2.) * params.multiline / params.density); // re-calculate layersPerModule and zScale layersPerModule = floor((gridSize * 2) / (zScale * layerHeight) + 0.05); if(layersPerModule < 2){ @@ -264,11 +264,24 @@ void Fill3DHoneycomb::_fill_surface_single( // move pattern in place for (Polyline &pl : polylines){ pl.translate(bb.min); + pl.simplify(5 * spacing); // simplify to 5x line width } + // Apply multiline offset if needed + multiline_fill(polylines, params, spacing); + // clip pattern to boundaries, chain the clipped polylines polylines = intersection_pl(polylines, to_polygons(expolygon)); + if (! polylines.empty()) { + // Remove very small bits, but be careful to not remove infill lines connecting thin walls! + // The infill perimeter lines should be separated by around a single infill line width. + const double minlength = scale_(0.8 * this->spacing); + polylines.erase( + std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }), + polylines.end()); + } + // copy from fliplines if (!polylines.empty()) { int infill_start_idx = polylines_out.size(); // only rotate what belongs to us. diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index d4b117b99d..3ec9ec3685 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -1369,6 +1369,10 @@ void Filler::_fill_surface_single( // Convert lines to polylines. all_polylines.reserve(lines.size()); std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; }); + + // Apply multiline offset if needed + multiline_fill(all_polylines, params, spacing); + // Crop all polylines all_polylines = intersection_pl(std::move(all_polylines), expolygon); #endif diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 9f5a54a0d9..8936f5f55f 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -1,6 +1,7 @@ #include #include +#include #include "../ClipperUtils.hpp" #include "../EdgeGrid.hpp" #include "../Geometry.hpp" @@ -25,7 +26,6 @@ // BBS: new infill pattern header #include "FillConcentricInternal.hpp" #include "FillCrossHatch.hpp" - // #define INFILL_DEBUG_OUTPUT namespace Slic3r { @@ -190,22 +190,22 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para // Orca: Dedicated function to calculate gap fill lines for the provided surface, according to the print object parameters // and append them to the out ExtrusionEntityCollection. void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, ExtrusionEntityCollection* out){ - + //Orca: just to be safe, check against null pointer for the print object config and if NULL return. if (this->print_object_config == nullptr) return; - + // Orca: Enable gap fill as per the user preference. Return early if gap fill is to not be applied. if ((this->print_object_config->gap_fill_target.value == gftNowhere) || (surface->surface_type == stInternalSolid && this->print_object_config->gap_fill_target.value != gftEverywhere)) return; - + Flow new_flow = params.flow; ExPolygons unextruded_areas; unextruded_areas = diff_ex(this->no_overlap_expolygons, union_ex(out->polygons_covered_by_spacing(10))); ExPolygons gapfill_areas = union_ex(unextruded_areas); if (!this->no_overlap_expolygons.empty()) gapfill_areas = intersection_ex(gapfill_areas, this->no_overlap_expolygons); - + if (gapfill_areas.size() > 0 && params.density >= 1) { double min = 0.2 * new_flow.scaled_spacing() * (1 - INSET_OVERLAP_TOLERANCE); double max = 2. * new_flow.scaled_spacing(); @@ -222,20 +222,20 @@ void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, Ex std::vector order2 = chain_points(ordering_points); for (size_t i : order2) 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(min, max, &polylines); } - + if (!polylines.empty() && !is_bridge(params.extrusion_role)) { polylines.erase(std::remove_if(polylines.begin(), polylines.end(), [&](const ThickPolyline& p) { return p.length() < scale_(params.config->filter_out_gap_fill.value); }), polylines.end()); - + ExtrusionEntityCollection gap_fill; variable_width(polylines, erGapFill, params.flow, gap_fill.entities); auto gap = std::move(gap_fill.entities); @@ -2696,4 +2696,55 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const Polygons &boun connect_base_support(std::move(infill_ordered), polygons_src, bbox, polylines_out, spacing, params); } +//Fill Multiline +void multiline_fill(Polylines& polylines, const FillParams& params, float spacing) +{ + if (params.multiline > 1) { + const int n_lines = params.multiline; + const int n_polylines = static_cast(polylines.size()); + Polylines all_polylines; + all_polylines.reserve(n_lines * n_polylines); + + const float center = (n_lines - 1) / 2.0f; + + for (int line = 0; line < n_lines; ++line) { + float offset = (static_cast(line) - center) * spacing; + + for (const Polyline& pl : polylines) { + const size_t n = pl.points.size(); + if (n < 2) { + all_polylines.emplace_back(pl); + continue; + } + + Points new_points; + new_points.reserve(n); + for (size_t i = 0; i < n; ++i) { + Vec2f tangent; + if (i == 0) + tangent = Vec2f(pl.points[1].x() - pl.points[0].x(), pl.points[1].y() - pl.points[0].y()); + else if (i == n - 1) + tangent = Vec2f(pl.points[n - 1].x() - pl.points[n - 2].x(), pl.points[n - 1].y() - pl.points[n - 2].y()); + else + tangent = Vec2f(pl.points[i + 1].x() - pl.points[i - 1].x(), pl.points[i + 1].y() - pl.points[i - 1].y()); + + float len = std::hypot(tangent.x(), tangent.y()); + if (len == 0) + len = 1.0f; + tangent /= len; + Vec2f normal(-tangent.y(), tangent.x()); + + Point p = pl.points[i]; + p.x() += scale_(normal.x() * offset); + p.y() += scale_(normal.y() * offset); + new_points.push_back(p); + } + + all_polylines.emplace_back(std::move(new_points)); + } + } + polylines = std::move(all_polylines); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 4348ba5127..89974c3fde 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -53,6 +53,7 @@ struct FillParams // Fill density, fraction in <0, 1> float density { 0.f }; + int multiline{1}; // Length of an infill anchor along the perimeter. // 1000mm is roughly the maximum length line that fits into a 32bit coord_t. @@ -223,7 +224,8 @@ public: static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance); }; - + //Fill Multiline + void multiline_fill(Polylines& polylines, const FillParams& params, float spacing); } // namespace Slic3r #endif // slic3r_FillBase_hpp_ diff --git a/src/libslic3r/Fill/FillCrossHatch.cpp b/src/libslic3r/Fill/FillCrossHatch.cpp index 3772a1fec2..359c4252c4 100644 --- a/src/libslic3r/Fill/FillCrossHatch.cpp +++ b/src/libslic3r/Fill/FillCrossHatch.cpp @@ -2,7 +2,7 @@ #include "../ShortestPath.hpp" #include "../Surface.hpp" #include - +#include "FillBase.hpp" #include "FillCrossHatch.hpp" namespace Slic3r { @@ -186,7 +186,8 @@ void FillCrossHatch ::_fill_surface_single( BoundingBox bb = expolygon.contour.bounding_box(); // linespace modifier - coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); + double density_adjusted = params.density / params.multiline; + coord_t line_spacing = coord_t(scale_(this->spacing) / density_adjusted); // reduce density if (params.density < 0.999) line_spacing *= 1.08; @@ -204,6 +205,9 @@ void FillCrossHatch ::_fill_surface_single( // shift the pattern to the actual space for (Polyline &pl : polylines) { pl.translate(bb.min); } + // Apply multiline offset if needed + multiline_fill(polylines, params, spacing); + polylines = intersection_pl(polylines, to_polygons(expolygon)); // --- remove small remains from gyroid infill diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index 12c9a8cfec..caa8459bec 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -4,7 +4,7 @@ #include #include #include - +#include "FillBase.hpp" #include "FillGyroid.hpp" namespace Slic3r { @@ -149,10 +149,10 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double constexpr double FillGyroid::PatternTolerance; void FillGyroid::_fill_surface_single( - const FillParams ¶ms, + const FillParams ¶ms, unsigned int thickness_layers, - const std::pair &direction, - ExPolygon expolygon, + const std::pair &direction, + ExPolygon expolygon, Polylines &polylines_out) { auto infill_angle = float(this->angle + (CorrectionAngle * 2*M_PI) / 360.); @@ -161,7 +161,7 @@ void FillGyroid::_fill_surface_single( BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * DensityAdjust); + double density_adjusted = std::max(0., params.density * DensityAdjust / params.multiline); // Distance between the gyroid waves in scaled coordinates. coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); @@ -184,6 +184,9 @@ void FillGyroid::_fill_surface_single( for (Polyline &pl : polylines) pl.translate(bb.min); + // Apply multiline offset if needed + multiline_fill(polylines, params, spacing); + polylines = intersection_pl(polylines, expolygon); if (! polylines.empty()) { diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp index 3e37ddbc84..e750425a82 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -7,9 +7,9 @@ namespace Slic3r { void FillHoneycomb::_fill_surface_single( - const FillParams ¶ms, + const FillParams ¶ms, unsigned int thickness_layers, - const std::pair &direction, + const std::pair &direction, ExPolygon expolygon, Polylines &polylines_out) { @@ -19,7 +19,7 @@ void FillHoneycomb::_fill_surface_single( if (it_m == this->cache.end()) { it_m = this->cache.insert(it_m, std::pair(cache_id, CacheData())); CacheData &m = it_m->second; - coord_t min_spacing = coord_t(scale_(this->spacing)); + coord_t min_spacing = coord_t(scale_(this->spacing)) * params.multiline; m.distance = coord_t(min_spacing / params.density); m.hex_side = coord_t(m.distance / (sqrt(3)/2)); m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); @@ -36,14 +36,14 @@ void FillHoneycomb::_fill_surface_single( { // adjust actual bounding box to the nearest multiple of our hex pattern // and align it so that it matches across layers - + BoundingBox bounding_box = expolygon.contour.bounding_box(); { // rotate bounding box according to infill direction Polygon bb_polygon = bounding_box.polygon(); bb_polygon.rotate(direction.first, m.hex_center); bounding_box = bb_polygon.bounding_box(); - + // extend bounding box so that our pattern will be aligned with other layers // $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one // The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough. @@ -69,10 +69,13 @@ void FillHoneycomb::_fill_surface_single( x += m.distance; } p.rotate(-direction.first, m.hex_center); + p.simplify(5 * spacing); // simplify to 5x line width all_polylines.push_back(p); } } - + // Apply multiline offset if needed + multiline_fill(all_polylines, params, 1.1 * spacing); + all_polylines = intersection_pl(std::move(all_polylines), expolygon); chain_or_connect_infill(std::move(all_polylines), expolygon, polylines_out, this->spacing, params); } diff --git a/src/libslic3r/Fill/FillLightning.cpp b/src/libslic3r/Fill/FillLightning.cpp index 5cd7ae5797..502de9d51c 100644 --- a/src/libslic3r/Fill/FillLightning.cpp +++ b/src/libslic3r/Fill/FillLightning.cpp @@ -1,6 +1,6 @@ #include "../Print.hpp" #include "../ShortestPath.hpp" - +#include "FillBase.hpp" #include "FillLightning.hpp" #include "Lightning/Generator.hpp" @@ -16,6 +16,10 @@ void Filler::_fill_surface_single( const Layer &layer = generator->getTreesForLayer(this->layer_id); Polylines fill_lines = layer.convertToLines(to_polygons(expolygon), scaled(0.5 * this->spacing - this->overlap)); + // Apply multiline offset if needed + multiline_fill(fill_lines, params, spacing); + + chain_or_connect_infill(std::move(fill_lines), expolygon, polylines_out, this->spacing, params); } diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 261ae3c046..102db7795b 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -2956,11 +2956,12 @@ void make_fill_lines(const ExPolygonWithOffset &poly_with_offset, Point refpt, d } } -bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list &sweep_params, Polylines &polylines_out) +bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list &sweep_params, Polylines &polylines_out) // fill multiline { assert(sweep_params.size() >= 1); - assert(! params.full_infill()); + assert(!params.full_infill()); params.density /= double(sweep_params.size()); + int n_multilines = params.multiline; assert(params.density > 0.0001f && params.density <= 1.f); ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, float(scale_(this->overlap - 0.5 * this->spacing))); @@ -2970,12 +2971,21 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar Polylines fill_lines; coord_t line_width = coord_t(scale_(this->spacing)); - coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); + coord_t line_spacing = coord_t(scale_(this->spacing) * params.multiline / params.density); std::pair rotate_vector = this->_infill_direction(surface); for (const SweepParams &sweep : sweep_params) { // Rotate polygons so that we can work with vertical lines here float angle = rotate_vector.first + sweep.angle_base; - make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, - angle), rotate_vector.second.rotated(-angle), angle, line_width + coord_t(SCALED_EPSILON), line_spacing, coord_t(scale_(sweep.pattern_shift)), fill_lines); + //Fill Multiline + for (int i = 0; i < n_multilines; ++i) { + coord_t group_offset = i * line_spacing; + coord_t internal_offset = (i - (n_multilines - 1) / 2.0f) * line_width; + coord_t total_offset = group_offset + internal_offset; + coord_t pattern_shift = scale_(sweep.pattern_shift + unscale_(total_offset)); + + make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, -angle), rotate_vector.second.rotated(-angle), angle, + line_width + coord_t(SCALED_EPSILON), line_spacing, pattern_shift, fill_lines); + } } if (!fill_lines.empty()) { @@ -3057,8 +3067,7 @@ Polylines Fill2DLattice::fill_surface(const Surface *surface, const FillParams & return polylines_out; } -Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams ¶ms) -{ +Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams ¶ms){ Polylines polylines_out; if (! this->fill_surface_by_multilines( surface, params, @@ -3073,7 +3082,7 @@ Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ Polylines polylines_out; if (! this->fill_surface_by_multilines( surface, params, - { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), float((3./2.) * this->spacing / params.density) } }, + { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), float((3./2.) * this->spacing * params.multiline / params.density) } }, polylines_out)) BOOST_LOG_TRIVIAL(error) << "FillStars::fill_surface() failed to fill a region."; return polylines_out; @@ -3094,7 +3103,6 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ Polylines FillQuarterCubic::fill_surface(const Surface* surface, const FillParams& params) { using namespace boost::math::float_constants; - Polylines polylines_out; coord_t line_width = coord_t(scale_(this->spacing)); diff --git a/src/libslic3r/Fill/FillTpmsD.cpp b/src/libslic3r/Fill/FillTpmsD.cpp index 99692b4b68..1f2f3fb76c 100644 --- a/src/libslic3r/Fill/FillTpmsD.cpp +++ b/src/libslic3r/Fill/FillTpmsD.cpp @@ -110,7 +110,7 @@ void FillTpmsD::_fill_surface_single( BoundingBox bb = expolygon.contour.bounding_box(); // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * DensityAdjust); + double density_adjusted = std::max(0., params.density * DensityAdjust / params.multiline); // Distance between the gyroid waves in scaled coordinates. coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); @@ -129,6 +129,8 @@ void FillTpmsD::_fill_surface_single( for (Polyline &pl : polylines) pl.translate(bb.min); + // Apply multiline offset if needed + multiline_fill(polylines, params, spacing); polylines = intersection_pl(polylines, expolygon); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 255270421a..35ee46b27d 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -785,7 +785,7 @@ static std::vector s_Preset_print_options { "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", "top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "bottom_shell_layers", "bottom_shell_thickness", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction", - "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", + "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density", "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 590dbb785b..723c1eb3ef 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2361,6 +2361,14 @@ void PrintConfigDef::init_fff_params() def->max = 100; def->set_default_value(new ConfigOptionPercent(20)); + // Infill multiline + def = this->add("fill_multiline", coInt); + def->label = L("Fill Multiline"); + def->tooltip = L("Using multiple lines for the infill pattern, if supported by infill pattern."); + def->min = 1; + def->max = 5; // Maximum number of lines for infill pattern + def->set_default_value(new ConfigOptionInt(1)); + def = this->add("sparse_infill_pattern", coEnum); def->label = L("Sparse infill pattern"); def->category = L("Strength"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a71adcab88..0f0f034db9 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -980,6 +980,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, infill_combination)) // Orca: ((ConfigOptionFloatOrPercent, infill_combination_max_layer_height)) + ((ConfigOptionInt, fill_multiline)) // Ironing options ((ConfigOptionEnum, ironing_type)) ((ConfigOptionEnum, ironing_pattern)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 8c13c43cc2..b07c11163f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1065,6 +1065,7 @@ bool PrintObject::invalidate_state_by_config_options( #endif } else if ( opt_key == "interface_shells" + || opt_key == "infill_multiline" || opt_key == "infill_combination" || opt_key == "infill_combination_max_layer_height" || opt_key == "bottom_shell_thickness" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index c36b10356e..6c17eb9c3b 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -294,6 +294,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } double sparse_infill_density = config->option("sparse_infill_density")->value; + int fill_multiline = config->option("fill_multiline")->value; auto timelapse_type = config->opt_enum("timelapse_type"); if (!is_plate_config && @@ -546,6 +547,20 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co bool have_combined_infill = config->opt_bool("infill_combination") && have_infill; toggle_line("infill_combination_max_layer_height", have_combined_infill); + // Infill patterns that support multiline infill. + InfillPattern pattern = config->opt_enum("sparse_infill_pattern"); + bool have_multiline_infill_pattern = pattern == ipGyroid || pattern == ipGrid || pattern == ipRectilinear || pattern == ipTpmsD || pattern == ipCrossHatch || pattern == ipHoneycomb || + pattern == ipCubic || pattern == ipStars || pattern == ipAlignedRectilinear || pattern == ipLightning || pattern == ip3DHoneycomb || pattern == ipAdaptiveCubic || pattern == ipSupportCubic; + toggle_line("fill_multiline", have_multiline_infill_pattern); + + // If the infill pattern does not support multiline infill, set fill_multiline to 1. + if (have_multiline_infill_pattern==false) { + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("fill_multiline", new ConfigOptionInt(1)); + apply(config, &new_conf); + } + + // Hide infill anchor max if sparse_infill_pattern is not line or if sparse_infill_pattern is line but infill_anchor_max is 0. bool infill_anchor = config->opt_enum("sparse_infill_pattern") != ipLine; toggle_field("infill_anchor_max",infill_anchor); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3dbd25cbb1..64816c5ab7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2207,6 +2207,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Infill"), L"param_infill"); optgroup->append_single_option_line("sparse_infill_density", "strength_settings_infill#sparse-infill-density"); + optgroup->append_single_option_line("fill_multiline"); // fill multiline optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern"); optgroup->append_single_option_line("infill_direction", "strength_settings_infill#direction"); optgroup->append_single_option_line("sparse_infill_rotate_template", "strength_settings_infill#rotation");