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");