diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 52ef67475..962f74693 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -234,6 +234,23 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) if (density <= 0) continue; } + + //Set Params for fill + // apply half spacing using this flow's own spacing and generate infill + FillParams params; + params.density = float(0.01 * density); + params.dont_adjust = false; + params.fill_exactly = layerm.region()->config().enforce_full_fill_volume.getBool(); + params.dont_connect = layerm.region()->config().infill_not_connected.getBool(); + //adjust flow (to over-extrude when needed) + float flow_percent = 1; + if (surface.has_pos_top()) flow_percent *= layerm.region()->config().fill_top_flow_ratio.get_abs_value(1); + params.flow_mult = flow_percent; + //adjust spacing (to over-extrude when needed) + if (surface.has_mod_overBridge()) { + params.density = layerm.region()->config().over_bridge_flow_ratio.get_abs_value(1); + } + params.config = &layerm.region()->config(); // get filler object std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); @@ -249,32 +266,37 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) -1, // auto width *layerm.layer()->object() ); - + // calculate flow spacing for infill pattern generation bool using_internal_flow = false; - if (! surface.has_fill_solid() && ! is_bridge) { - // it's internal infill, so we can calculate a generic flow spacing - // for all layers, for avoiding the ugly effect of - // misaligned infill on first layer because of different extrusion width and - // layer height - Flow internal_flow = layerm.region()->flow( - frInfill, - layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layerm.layer()->object() - ); - f->spacing = internal_flow.spacing(); - using_internal_flow = true; - } else { - f->spacing = flow.spacing(); + { + coordf_t spacing; + if (!surface.has_fill_solid() && !is_bridge) { + // it's internal infill, so we can calculate a generic flow spacing + // for all layers, for avoiding the ugly effect of + // misaligned infill on first layer because of different extrusion width and + // layer height + Flow internal_flow = layerm.region()->flow( + frInfill, + layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers? + false, // no bridge + false, // no first layer + -1, // auto width + *layerm.layer()->object() + ); + spacing = internal_flow.spacing(); + using_internal_flow = true; + } + else { + spacing = flow.spacing(); + } + f->init_spacing(spacing, params); } double link_max_length = 0.; if (! is_bridge) { if (density > 80.) // 80% - link_max_length = 3 * f->spacing; // slic3r default : 3 + link_max_length = 3 * f->get_spacing(); // slic3r default : 3 } f->layer_id = layerm.layer()->id(); @@ -288,7 +310,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) //give the overlap size to let the infill do his overlap //add overlap if at least one perimeter if (layerm.region()->config().perimeters > 0) { - f->overlap = layerm.region()->config().get_abs_value("infill_overlap", (perimeter_spacing + (f->spacing)) / 2); + f->overlap = layerm.region()->config().get_abs_value("infill_overlap", (perimeter_spacing + (f->get_spacing())) / 2); if (f->overlap!=0) { f->no_overlap_expolygons = intersection_ex(layerm.fill_no_overlap_expolygons, ExPolygons() = { surface.expolygon }); } else { @@ -298,13 +320,6 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) f->overlap = 0; f->no_overlap_expolygons.push_back(surface.expolygon); } - - // apply half spacing using this flow's own spacing and generate infill - FillParams params; - params.density = float(0.01 * density); - params.dont_adjust = false; - params.fill_exactly = layerm.region()->config().enforce_full_fill_volume.getBool(); - params.dont_connect = layerm.region()->config().infill_not_connected.getBool(); // calculate actual flow from spacing (which might have been adjusted by the infill // pattern generator) @@ -313,21 +328,10 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // so we can safely ignore the slight variation that might have // been applied to $f->flow_spacing } else { - flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge); + flow = Flow::new_from_spacing(f->get_spacing(), flow.nozzle_diameter, (float)h, is_bridge); } - - //adjust flow (to over-extrude when needed) - float flow_percent = 1; - if (surface.has_pos_top()) flow_percent *= layerm.region()->config().fill_top_flow_ratio.get_abs_value(1); - params.flow_mult = flow_percent; - - //adjust spacing (to over-extrude when needed) - if (surface.has_mod_overBridge()){ - params.density = layerm.region()->config().over_bridge_flow_ratio.get_abs_value(1); - } - params.flow = &flow; - params.config = &layerm.region()->config(); + f->fill_surface_extrusion(&surface, params, out.entities); } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 379310bd9..ed413c509 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -15,6 +15,7 @@ #include "FillRectilinear2.hpp" #include "FillRectilinear3.hpp" #include "FillSmooth.hpp" +#include "../MedialAxis.hpp" namespace Slic3r { @@ -55,7 +56,7 @@ Fill* Fill::new_from_type(const std::string &type) return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second)); } -Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) const { // Perform offset. Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->spacing))); @@ -140,7 +141,7 @@ std::pair Fill::_infill_direction(const Surface *surface) const return std::pair(out_angle, out_shift); } -void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { +void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { //add overlap & call fill_surface Polylines polylines = this->fill_surface(surface, params); if (polylines.empty()) @@ -163,14 +164,6 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ double poylineVolume = 0; for (auto poly = this->no_overlap_expolygons.begin(); poly != this->no_overlap_expolygons.end(); ++poly) { poylineVolume += params.flow->height*unscaled(unscaled(poly->area())); - // add external "perimeter gap" - double perimeterRoundGap = unscaled(poly->contour.length()) * params.flow->height * (1 - 0.25*PI) * 0.5; - // add holes "perimeter gaps" - double holesGaps = 0; - for (auto hole = poly->holes.begin(); hole != poly->holes.end(); ++hole) { - holesGaps += unscaled(hole->length()) * params.flow->height * (1 - 0.25*PI) * 0.5; - } - poylineVolume += perimeterRoundGap + holesGaps; } //printf("process want %f mm3 extruded for a volume of %f space : we mult by %f %i\n", // extrudedVolume, @@ -203,6 +196,10 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ +coord_t Fill::_line_spacing_for_density(float density) const +{ + return coord_t(scale_(this->spacing) / density); +} /// cut poly between poly.point[idx_1] & poly.point[idx_1+1] /// add p1+-width to one part and p2+-width to the other one. @@ -461,7 +458,8 @@ Points getFrontier(Polylines &polylines, const Point& p1, const Point& p2, const /// return the connected polylines in polylines_out. Can output polygons (stored as polylines with first_point = last_point). /// complexity: worst: N(infill_ordered.points) x N(boundary.points) /// typical: N(infill_ordered) x ( N(boundary.points) + N(infill_ordered.points) ) -void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) { +void +Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) const { //TODO: fallback to the quick & dirty old algorithm when n(points) is too high. Polylines polylines_frontier = to_polylines(((Polygons)boundary)); @@ -573,4 +571,56 @@ void Fill::connect_infill(const Polylines &infill_ordered, const ExPolygon &boun } } + + +void +Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out) const { + + ThickPolylines polylines_gapfill; + double min = 0.4 * scale_(params.flow->nozzle_diameter) * (1 - INSET_OVERLAP_TOLERANCE); + double max = 2. * params.flow->scaled_width(); + // collapse + //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). + min = std::max(min, double(Flow::new_from_spacing(EPSILON, params.flow->nozzle_diameter, params.flow->height, false).scaled_width())); + //ExPolygons gapfill_areas_collapsed = diff_ex( + // offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)), + // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), + // true); + ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); + for (const ExPolygon &ex : gapfill_areas_collapsed) { + //remove too small gaps that are too hard to fill. + //ie one that are smaller than an extrusion with width of min and a length of max. + if (ex.area() > scale_(params.flow->nozzle_diameter)*scale_(params.flow->nozzle_diameter) * 2) { + MedialAxis{ ex, params.flow->scaled_width() * 2, params.flow->scaled_width() / 5, coord_t(params.flow->height) }.build(polylines_gapfill); + } + } + if (!polylines_gapfill.empty() && params.role != erBridgeInfill) { + //test +#ifdef _DEBUG + for (ThickPolyline poly : polylines_gapfill) { + for (coordf_t width : poly.width) { + if (width > params.flow->scaled_width() * 2.2) { + std::cerr << "ERRROR!!!! gapfill width = " << unscaled(width) << " > max_width = " << (params.flow->width * 2) << "\n"; + } + } + } +#endif + + ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, *params.flow); + //set role if needed + if (params.role != erSolidInfill) { + ExtrusionSetRole set_good_role(params.role); + gap_fill.visit(set_good_role); + } + //move them into the collection + if (!gap_fill.entities.empty()) { + ExtrusionEntityCollection *coll_gapfill = new ExtrusionEntityCollection(); + coll_gapfill->no_sort = this->no_sort(); + coll_gapfill->append(std::move(gap_fill.entities)); + coll_out.push_back(coll_gapfill); + } + } +} + + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index d1cce1838..a13aa1b8f 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -73,8 +73,6 @@ public: size_t layer_id; // Z coordinate of the top print surface, in unscaled coordinates coordf_t z; - // in unscaled coordinates - coordf_t spacing; // infill / perimeter overlap, in unscaled coordinates coordf_t overlap; ExPolygons no_overlap_expolygons; @@ -88,6 +86,9 @@ public: coord_t loop_clipping; // In scaled coordinates. Bounding box of the 2D projection of the object. BoundingBox bounding_box; +protected: + // in unscaled coordinates, please use init (after settings all others settings) as some algos want to modify the value + coordf_t spacing; public: virtual ~Fill() {} @@ -96,15 +97,17 @@ public: static Fill* new_from_type(const std::string &type); void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; } + virtual void init_spacing(coordf_t spacing, const FillParams ¶ms) { this->spacing = spacing; } + coordf_t get_spacing() const { return spacing; } // Do not sort the fill lines to optimize the print head path? virtual bool no_sort() const { return false; } // This method have to fill the ExtrusionEntityCollection. It call fill_surface by default - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out); + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const; // Perform the fill. - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const; protected: Fill() : @@ -127,17 +130,19 @@ protected: unsigned int /* thickness_layers */, const std::pair & /* direction */, ExPolygon & /* expolygon */, - Polylines & /* polylines_out */) {}; + Polylines & /* polylines_out */) const {}; virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; } + virtual coord_t _line_spacing_for_density(float density) const; + virtual std::pair _infill_direction(const Surface *surface) const; - void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms); + void connect_infill(const Polylines &infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const FillParams ¶ms) const; - void do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out); + void do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out) const; - ExtrusionRole getRoleFromSurfaceType(const FillParams ¶ms, const Surface *surface){ + ExtrusionRole getRoleFromSurfaceType(const FillParams ¶ms, const Surface *surface) const { if (params.role == erNone || params.role == erCustom) { return params.flow->bridge ? erBridgeInfill : @@ -165,7 +170,7 @@ public: } static Point _align_to_grid(Point coord, Point spacing) { return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); } - static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base) + static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base) { return base + _align_to_grid(coord - base, spacing); } static Point _align_to_grid(Point coord, Point spacing, Point base) { return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); } diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index a5fb06951..0c374b30e 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -8,30 +8,37 @@ #include "FillConcentric.hpp" namespace Slic3r { - -void FillConcentric::_fill_surface_single( +void +FillConcentric::init_spacing(coordf_t spacing, const FillParams ¶ms) +{ + Fill::init_spacing(spacing, params); + if (params.density > 0.9999f && !params.dont_adjust) { + this->spacing = unscale(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density))); + } +} + +void +FillConcentric::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, - Polylines &polylines_out) + Polylines &polylines_out) const { // no rotation is supported for this infill pattern BoundingBox bounding_box = expolygon.contour.bounding_box(); - coord_t min_spacing = scale_(this->spacing); - coord_t distance = coord_t(min_spacing / params.density); - + coord_t distance = _line_spacing_for_density(params.density); if (params.density > 0.9999f && !params.dont_adjust) { - distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance); - this->spacing = unscale(distance); + //it's == this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density)) because of the init_spacing() + distance = scale_(this->spacing); } Polygons loops = (Polygons)expolygon; Polygons last = loops; while (! last.empty()) { - last = offset2(last, -(distance + min_spacing/2), +min_spacing/2); + last = offset2(last, -(distance + scale_(this->spacing) /2), +scale_(this->spacing) /2); loops.insert(loops.end(), last.begin(), last.end()); } @@ -66,7 +73,7 @@ void FillConcentric::_fill_surface_single( } void FillConcentricWGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, - ExtrusionEntitiesPtr &out) { + ExtrusionEntitiesPtr &out) const { // Perform offset. Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->spacing))); @@ -86,19 +93,16 @@ void FillConcentricWGapFill::fill_surface_extrusion(const Surface *surface, cons // no rotation is supported for this infill pattern BoundingBox bounding_box = expolygon.contour.bounding_box(); - coord_t min_spacing = scale_(this->spacing); - coord_t distance = coord_t(min_spacing / params.density); - + coord_t distance = _line_spacing_for_density(params.density); if (params.density > 0.9999f && !params.dont_adjust) { - distance = this->_adjust_solid_spacing(bounding_box.size().x(), distance); - this->spacing = unscaled(distance); + distance = scale_(this->spacing); } ExPolygons gaps; Polygons loops = (Polygons)expolygon; Polygons last = loops; while (!last.empty()) { - Polygons next_onion = offset2(last, -(distance + min_spacing / 2), +min_spacing / 2); + Polygons next_onion = offset2(last, -(distance + scale_(this->spacing) / 2), +scale_(this->spacing) / 2); loops.insert(loops.end(), next_onion.begin(), next_onion.end()); append(gaps, diff_ex( offset(last, -0.5f * distance), diff --git a/src/libslic3r/Fill/FillConcentric.hpp b/src/libslic3r/Fill/FillConcentric.hpp index 593dfaff5..1c71e33b9 100644 --- a/src/libslic3r/Fill/FillConcentric.hpp +++ b/src/libslic3r/Fill/FillConcentric.hpp @@ -12,12 +12,13 @@ public: protected: virtual Fill* clone() const { return new FillConcentric(*this); }; + void init_spacing(coordf_t spacing, const FillParams ¶ms) override; virtual void _fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, - Polylines &polylines_out); + Polylines &polylines_out) const; virtual bool no_sort() const { return true; } }; @@ -29,7 +30,7 @@ public: protected: virtual Fill* clone() const { return new FillConcentricWGapFill(*this); }; - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override; virtual bool no_sort() const { return true; } }; diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 3b9266a0f..51e790613 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -11,7 +11,7 @@ void FillPlanePath::_fill_surface_single( unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, - Polylines &polylines_out) + Polylines &polylines_out) const { expolygon.rotate(- direction.first); @@ -75,7 +75,7 @@ void FillPlanePath::_fill_surface_single( } // Follow an Archimedean spiral, in polar coordinates: r=a+b\theta -Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const { // Radius to achieve. coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; @@ -145,7 +145,7 @@ static inline Point hilbert_n_to_xy(const size_t n) return Point(x, y); } -Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const { // Minimum power of two square to fit the domain. size_t sz = 2; @@ -168,7 +168,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, return line; } -Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const { // Radius to achieve. coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; diff --git a/src/libslic3r/Fill/FillPlanePath.hpp b/src/libslic3r/Fill/FillPlanePath.hpp index fef375b11..da2ee6fba 100644 --- a/src/libslic3r/Fill/FillPlanePath.hpp +++ b/src/libslic3r/Fill/FillPlanePath.hpp @@ -24,11 +24,11 @@ protected: unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, - Polylines &polylines_out) override; + Polylines &polylines_out) const override; virtual float _layer_angle(size_t idx) const { return 0.f; } virtual bool _centered() const = 0; - virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0; + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const = 0; }; class FillArchimedeanChords : public FillPlanePath @@ -39,7 +39,7 @@ public: protected: virtual bool _centered() const { return true; } - virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const; }; class FillHilbertCurve : public FillPlanePath @@ -50,7 +50,7 @@ public: protected: virtual bool _centered() const { return false; } - virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const; }; class FillOctagramSpiral : public FillPlanePath @@ -61,7 +61,7 @@ public: protected: virtual bool _centered() const { return true; } - virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) const; }; } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 1df02be8f..ed68d7c7b 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -865,12 +865,18 @@ std::vector FillRectilinear2::_vert_lines_for_polygon return segs; } -coord_t FillRectilinear2::_line_spacing_for_density(float density) const +void +FillRectilinear2::init_spacing(coordf_t spacing, const FillParams ¶ms) { - return coord_t(scale_(this->spacing) / density); + Fill::init_spacing(spacing, params); + //remove this code path becaus it's only really useful for squares at 45° and it override a setting + // define flow spacing according to requested density + //if (params.full_infill() && !params.dont_adjust) { + // this->spacing = unscale(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density))); + //} } -bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) +bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) const { // At the end, only the new polylines will be rotated back. size_t n_polylines_out_initial = polylines_out.size(); @@ -904,8 +910,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP // define flow spacing according to requested density if (params.full_infill() && !params.dont_adjust) { - line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing); - this->spacing = unscale(line_spacing); + //it's == this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing) because of the init_spacing + line_spacing = scale_(this->spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. @@ -1441,7 +1447,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP return true; } -Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams ¶ms) const { Polylines polylines_out; if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out)) { @@ -1450,7 +1456,7 @@ Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParam return polylines_out; } -Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms) const { // Each linear fill covers half of the target coverage. FillParams params2 = params; @@ -1463,7 +1469,7 @@ Polylines FillGrid2::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) const { // Each linear fill covers 1/3 of the target coverage. FillParams params2 = params; @@ -1479,7 +1485,7 @@ Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams & return polylines_out; } -Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ms) const { // Each linear fill covers 1/3 of the target coverage. FillParams params2 = params; @@ -1495,7 +1501,7 @@ Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ return polylines_out; } -Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ms) const { // Each linear fill covers 1/3 of the target coverage. FillParams params2 = params; @@ -1515,7 +1521,7 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ void -FillRectilinear2Peri::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) +FillRectilinear2Peri::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { ExtrusionEntityCollection *eecroot = new ExtrusionEntityCollection(); //you don't want to sort the extrusions: big infill first, small second @@ -1623,7 +1629,7 @@ coord_t FillScatteredRectilinear::_line_spacing_for_density(float density) const return coord_t(scale_(this->spacing) / 1.0); } -Polylines FillScatteredRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms) +Polylines FillScatteredRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms) const { Polylines polylines_out; @@ -1659,7 +1665,7 @@ std::vector FillScatteredRectilinear::_vert_lines_for void -FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { +FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { const coord_t scaled_nozzle_diam = scale_(params.flow->nozzle_diameter); const coord_t clearance = scaled_nozzle_diam * 2; const coord_t tooth_spacing_min = scaled_nozzle_diam; @@ -1770,7 +1776,8 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi } -void FillRectilinear2WGapFill::split_polygon_gap_fill(const Surface &surface, const FillParams ¶ms, ExPolygons &rectilinear, ExPolygons &gapfill) { +void +FillRectilinear2WGapFill::split_polygon_gap_fill(const Surface &surface, const FillParams ¶ms, ExPolygons &rectilinear, ExPolygons &gapfill) { // remove areas for gapfill // factor=0.5 : remove area smaller than a spacing. factor=1 : max spacing for the gapfill (but not the width) @@ -1786,7 +1793,7 @@ void FillRectilinear2WGapFill::split_polygon_gap_fill(const Surface &surface, co } void -FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { +FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); coll_nosort->no_sort = true; //can be sorted inside the pass ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); @@ -1889,55 +1896,5 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F } -void -Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, ExtrusionEntitiesPtr &coll_out) { - - ThickPolylines polylines_gapfill; - double min = 0.4 * scale_(params.flow->nozzle_diameter) * (1 - INSET_OVERLAP_TOLERANCE); - double max = 2. * params.flow->scaled_width(); - // collapse - //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). - min = std::max(min, double(Flow::new_from_spacing(EPSILON, params.flow->nozzle_diameter, params.flow->height, false).scaled_width())); - //ExPolygons gapfill_areas_collapsed = diff_ex( - // offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)), - // offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)), - // true); - ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2)); - for (const ExPolygon &ex : gapfill_areas_collapsed) { - //remove too small gaps that are too hard to fill. - //ie one that are smaller than an extrusion with width of min and a length of max. - if (ex.area() > scale_(params.flow->nozzle_diameter)*scale_(params.flow->nozzle_diameter) * 2) { - MedialAxis{ ex, params.flow->scaled_width() * 2, params.flow->scaled_width() / 5, coord_t(params.flow->height) }.build(polylines_gapfill); - } - } - if (!polylines_gapfill.empty() && params.role != erBridgeInfill) { - //test -#ifdef _DEBUG - for (ThickPolyline poly : polylines_gapfill) { - for (coordf_t width : poly.width) { - if (width > params.flow->scaled_width() * 2.2) { - std::cerr << "ERRROR!!!! recti gapfill width = " << unscaled(width) << " > max_width = " << (params.flow->width * 2) << "\n"; - } - } - } -#endif - - ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, *params.flow); - //set role if needed - if (params.role != erSolidInfill) { - ExtrusionSetRole set_good_role(params.role); - gap_fill.visit(set_good_role); - } - //move them into the collection - if (!gap_fill.entities.empty()) { - ExtrusionEntityCollection *coll_gapfill = new ExtrusionEntityCollection(); - coll_gapfill->no_sort = this->no_sort(); - coll_gapfill->append(std::move(gap_fill.entities)); - coll_out.push_back(coll_gapfill); - } - } -} - - } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillRectilinear2.hpp b/src/libslic3r/Fill/FillRectilinear2.hpp index 642e1e51c..9e9e25fd0 100644 --- a/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/src/libslic3r/Fill/FillRectilinear2.hpp @@ -16,13 +16,13 @@ class FillRectilinear2 : public Fill public: virtual Fill* clone() const { return new FillRectilinear2(*this); }; virtual ~FillRectilinear2() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + void init_spacing(coordf_t spacing, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: virtual std::vector _vert_lines_for_polygon(const ExPolygonWithOffset &poly_with_offset, const BoundingBox &bounding_box, const FillParams ¶ms, coord_t line_spacing) const; - virtual coord_t _line_spacing_for_density(float density) const; - bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out); + bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) const; }; class FillGrid2 : public FillRectilinear2 @@ -30,7 +30,7 @@ class FillGrid2 : public FillRectilinear2 public: virtual Fill* clone() const { return new FillGrid2(*this); }; virtual ~FillGrid2() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -42,7 +42,7 @@ class FillTriangles : public FillRectilinear2 public: virtual Fill* clone() const { return new FillTriangles(*this); }; virtual ~FillTriangles() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -54,7 +54,7 @@ class FillStars : public FillRectilinear2 public: virtual Fill* clone() const { return new FillStars(*this); }; virtual ~FillStars() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -66,7 +66,7 @@ class FillCubic : public FillRectilinear2 public: virtual Fill* clone() const { return new FillCubic(*this); }; virtual ~FillCubic() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -80,7 +80,7 @@ public: virtual Fill* clone() const { return new FillRectilinear2Peri(*this); }; virtual ~FillRectilinear2Peri() {} //virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms); - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override; }; @@ -90,7 +90,7 @@ class FillScatteredRectilinear : public FillRectilinear2 public: virtual Fill* clone() const override{ return new FillScatteredRectilinear(*this); }; virtual ~FillScatteredRectilinear() {} - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; protected: virtual float _layer_angle(size_t idx) const; @@ -103,7 +103,7 @@ public: virtual Fill* clone() const { return new FillRectilinearSawtooth(*this); }; virtual ~FillRectilinearSawtooth() {} - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override; }; @@ -113,7 +113,7 @@ public: virtual Fill* clone() const { return new FillRectilinear2WGapFill(*this); }; virtual ~FillRectilinear2WGapFill() {} - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override; static void split_polygon_gap_fill(const Surface &surface, const FillParams ¶ms, ExPolygons &rectilinear, ExPolygons &gapfill); }; diff --git a/src/libslic3r/Fill/FillSmooth.cpp b/src/libslic3r/Fill/FillSmooth.cpp index eabc0fd2c..81670e3e1 100644 --- a/src/libslic3r/Fill/FillSmooth.cpp +++ b/src/libslic3r/Fill/FillSmooth.cpp @@ -10,7 +10,7 @@ namespace Slic3r { - Polylines FillSmooth::fill_surface(const Surface *surface, const FillParams ¶ms) + Polylines FillSmooth::fill_surface(const Surface *surface, const FillParams ¶ms) const { //ERROR: you shouldn't call that. Default to the rectilinear one. printf("FillSmooth::fill_surface() : you call the wrong method (fill_surface instead of fill_surface_extrusion).\n"); @@ -20,7 +20,7 @@ namespace Slic3r { /// @idx: the index of the step (0 = first step, 1 = second step, ...) The first lay down the volume and the others smoothen the surface. void FillSmooth::perform_single_fill(const int idx, ExtrusionEntityCollection &eecroot, const Surface &srf_source, - const FillParams ¶ms, const double volume){ + const FillParams ¶ms, const double volume) const { if (srf_source.expolygon.empty()) return; // Save into layer smoothing path. @@ -44,7 +44,7 @@ namespace Slic3r { } else{ Surface surfaceNoOverlap(srf_source); - for (ExPolygon &poly : this->no_overlap_expolygons) { + for (const ExPolygon &poly : this->no_overlap_expolygons) { if (poly.empty()) continue; surfaceNoOverlap.expolygon = poly; this->fill_expolygon(idx, *eec, surfaceNoOverlap, params_modifided, volume); @@ -56,11 +56,11 @@ namespace Slic3r { } void FillSmooth::fill_expolygon(const int idx, ExtrusionEntityCollection &eec, const Surface &srf_to_fill, - const FillParams ¶ms, const double volume){ + const FillParams ¶ms, const double volume) const { std::unique_ptr f2 = std::unique_ptr(Fill::new_from_type(fillPattern[idx])); f2->bounding_box = this->bounding_box; - f2->spacing = this->spacing; + f2->init_spacing(this->get_spacing(),params); f2->layer_id = this->layer_id; f2->z = this->z; f2->angle = anglePass[idx] + this->angle; @@ -102,7 +102,7 @@ namespace Slic3r { } - void FillSmooth::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) + void FillSmooth::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { coordf_t init_spacing = this->spacing; diff --git a/src/libslic3r/Fill/FillSmooth.hpp b/src/libslic3r/Fill/FillSmooth.hpp index e5d97f4db..789978002 100644 --- a/src/libslic3r/Fill/FillSmooth.hpp +++ b/src/libslic3r/Fill/FillSmooth.hpp @@ -37,8 +37,8 @@ public: } virtual Fill* clone() const{ return new FillSmooth(*this); } - virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override; - virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override; + virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms) const override; + virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override; protected: int nbPass=2; @@ -56,9 +56,9 @@ protected: InfillPattern fillPattern[3]; void perform_single_fill(const int idx, ExtrusionEntityCollection &eecroot, const Surface &srf_source, - const FillParams ¶ms, const double volume); + const FillParams ¶ms, const double volume) const; void fill_expolygon(const int idx, ExtrusionEntityCollection &eec, const Surface &srf_to_fill, - const FillParams ¶ms, const double volume); + const FillParams ¶ms, const double volume) const; }; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index db2377ec2..f12a08b7d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1220,9 +1220,9 @@ void PrintConfigDef::init_fff_params() def->full_label = L("Ironing flow distribution"); def->category = OptionCategory::infill; def->tooltip = L("This is the percentage of the flow that is used for the second ironing pass. Typical 0-20%. " - "Should not be lower than 20%, unless you have your top extrusion width greatly superior to your nozzle width."); - def->min = 0; - def->max = 0.9; + "Should not be higher than 20%, unless you have your top extrusion width greatly superior to your nozzle width."); + //def->min = 0; + //def->max = 0.9; def->mode = comExpert; def->set_default_value(new ConfigOptionFloatOrPercent(10, true)); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1f14a52d0..60c31d8e9 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2337,7 +2337,8 @@ static inline void fill_expolygons_generate_paths( Fill *filler, float density, ExtrusionRole role, - const Flow &flow) + const Flow &flow, + coordf_t spacing) { FillParams fill_params; fill_params.density = density; @@ -2345,6 +2346,7 @@ static inline void fill_expolygons_generate_paths( fill_params.dont_adjust = true; fill_params.flow = &flow; fill_params.role = role; + filler->init_spacing(spacing, fill_params); for (ExPolygon &expoly : expolygons) { Surface surface(stPosInternal | stDensSparse, std::move(expoly)); filler->fill_surface_extrusion(&surface, fill_params, dst); @@ -3059,8 +3061,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // value that guarantees that all layers are correctly aligned. Fill *filler = filler_support.get(); filler->angle = raft_angle_base; - filler->spacing = m_support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); + filler->link_max_length = coord_t(scale_(m_support_material_flow.spacing() * link_max_length_factor / support_density)); fill_expolygons_generate_paths( // Destination support_layer.support_fills.entities, @@ -3069,7 +3070,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Filler and its parameters filler, float(support_density), // Extrusion parameters - erSupportMaterial, flow); + erSupportMaterial, flow, m_support_material_flow.spacing()); } } } @@ -3077,6 +3078,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( Fill *filler = filler_interface.get(); Flow flow = m_first_layer_flow; float density = 0.f; + coordf_t spacing = 0.f; if (support_layer_id == 0) { // Base flange. if (this->m_object_config->support_material_solid_first_layer.value) { @@ -3088,17 +3090,17 @@ void PrintObjectSupportMaterial::generate_toolpaths( // 70% of density on the 1st layer. density = 0.7f; } - filler->spacing = m_first_layer_flow.spacing(); + spacing = m_first_layer_flow.spacing(); } else if (support_layer_id >= m_slicing_params.base_raft_layers) { filler->angle = raft_angle_interface; // We don't use $base_flow->spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - filler->spacing = m_support_material_flow.spacing(); + spacing = m_support_material_flow.spacing(); flow = Flow(float(m_support_material_interface_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); density = float(interface_density); } else continue; - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); + filler->link_max_length = coord_t(scale_(spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination support_layer.support_fills.entities, @@ -3107,7 +3109,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow); + (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, spacing); } }); @@ -3214,21 +3216,22 @@ void PrintObjectSupportMaterial::generate_toolpaths( layer_ex.layer->bridging); Fill *filler = i == 2 ? filler_intermediate_interface.get() : filler_interface.get(); float density = interface_density; + coordf_t spacing; //if first layer and solid first layer : draw concentric with 100% density if (support_layer.id() == 0 && this->m_object_config->support_material_solid_first_layer.value) { filler = filler_solid.get(); density = 1.f; interface_flow = m_first_layer_flow; filler->angle = 0; - filler->spacing = interface_flow.spacing(); + spacing = interface_flow.spacing(); } else { filler->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. interface_angle; - filler->spacing = m_support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); + spacing = m_support_material_interface_flow.spacing(); + filler->link_max_length = coord_t(scale_(spacing * link_max_length_factor / density)); } fill_expolygons_generate_paths( // Destination @@ -3238,7 +3241,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Filler and its parameters filler, float(density), // Extrusion parameters - erSupportMaterialInterface, interface_flow); + erSupportMaterialInterface, interface_flow, spacing); } // Base support or flange. @@ -3253,8 +3256,8 @@ void PrintObjectSupportMaterial::generate_toolpaths( float(base_layer.layer->height), m_support_material_flow.nozzle_diameter, base_layer.layer->bridging); - filler->spacing = m_support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); + coordf_t spacing = m_support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(spacing * link_max_length_factor / support_density)); float density = float(support_density); // find centerline of the external loop/extrusions ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? @@ -3271,12 +3274,12 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler = filler_interface.get(); filler->angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); density = 0.5f; - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); + filler->link_max_length = coord_t(scale_(spacing * link_max_length_factor / density)); } // use the proper spacing for first layer as we don't need to align // its pattern to the other layers flow = m_first_layer_flow; - filler->spacing = flow.spacing(); + spacing = flow.spacing(); } else if (with_sheath) { // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. // TODO: use brim ordering algorithm @@ -3296,7 +3299,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - erSupportMaterial, flow); + erSupportMaterial, flow, spacing); } layer_cache.overlaps.reserve(4); diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index 0bb4fabaa..e7e4871be 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -56,7 +56,6 @@ class Surface public: SurfaceType surface_type; ExPolygon expolygon; - ExPolygons notOverlaps; double thickness; // in mm unsigned short thickness_layers; // in layers double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined) diff --git a/src/test/libslic3r/test_fill.cpp b/src/test/libslic3r/test_fill.cpp index 82568bded..7a004236d 100644 --- a/src/test/libslic3r/test_fill.cpp +++ b/src/test/libslic3r/test_fill.cpp @@ -9,9 +9,11 @@ #include "../../libslic3r/Flow.hpp" #include "../../libslic3r/ClipperUtils.hpp" #include "../../libslic3r/SVG.hpp" +#include "../test_data.hpp" // get access to init_print, etc using namespace Slic3r; using namespace Slic3r::Geometry; +using namespace Slic3r::Test; bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle = 0, double density = 1.0); @@ -32,10 +34,11 @@ Polylines test(const ExPolygon& poly, Fill &filler, const FillParams ¶ms){ TEST_CASE("Fill: Pattern Path Length") { Fill* filler {Slic3r::Fill::new_from_type("rectilinear")}; filler->angle = -(PI) / 2.0; - filler->spacing = 5; - FillParams params{}; - params.dont_adjust = true; - params.density = filler->spacing / 50.0; + FillParams params{}; + params.dont_adjust = true; + params.density = 0.1; // 5/50 + filler->set_bounding_box(BoundingBox(Point(0, 0), Point::new_scale(Point(100, 100)))); + filler->init_spacing(5, params); //params.endpoints_overlap = false; @@ -85,7 +88,7 @@ TEST_CASE("Fill: Pattern Path Length") { for (double angle : {-(PI/2.0), -(PI/4.0), -(PI), PI/2.0, PI}) { for (double spacing : {25.0, 5.0, 7.5, 8.5}) { FillParams params_local = params; - params_local.density = filler->spacing / spacing; + params_local.density = filler->get_spacing() / spacing; filler->angle = angle; Slic3r::ExPolygon e{}; e.contour = Slic3r::Polygon(test_square); @@ -110,14 +113,15 @@ TEST_CASE("Fill: Pattern Path Length") { } SECTION("Regression: Missing infill segments in some rare circumstances") { FillParams params_local = params; + params_local.density = 1; + params_local.dont_adjust = false; Fill* filler_local = { Slic3r::Fill::new_from_type("rectilinear") }; filler_local->angle = (PI/4.0); - params_local.dont_adjust = false; - filler_local->spacing = 0.654498; + filler_local->set_bounding_box(BoundingBox(Point(0, 0), Point(2512749, 2512749))); + filler_local->init_spacing(0.654498, params_local); //filler_local->endpoints_overlap = unscale(359974); - params_local.density = 1; - filler_local->layer_id = 66; - filler_local->z = 20.15; + filler_local->layer_id = 66; + filler_local->z = 20.15; Points points {Point{25771516,14142125},Point{14142138,25771515},Point{2512749,14142131},Point{14142125,2512749}}; Slic3r::ExPolygon expoly{}; @@ -142,8 +146,8 @@ TEST_CASE("Fill: Pattern Path Length") { Surface surface {(stPosTop|stDensSolid), expolygon}; Flow flow {0.69f, 0.4f, 0.50f}; - filler->spacing = flow.spacing(); - params.density = 1.0; + params.density = 1.0; + filler->init_spacing(flow.spacing(), params); for (auto angle : { 0.0, 45.0}) { surface.expolygon.rotate(angle, Point{0,0}); @@ -204,6 +208,219 @@ TEST_CASE("Fill: Pattern Path Length") { } +class ExtrusionGetVolume : public ExtrusionVisitor { + double volume = 0; +public: + ExtrusionGetVolume() {} + void use(ExtrusionPath &path) override { + volume += unscaled(path.length()) * path.mm3_per_mm; } + void use(ExtrusionPath3D &path3D) override { volume += unscaled(path3D.length()) * path3D.mm3_per_mm; } + void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath path : multipath.paths) path.visit(*this); } + void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath path : multipath.paths) path.visit(*this); } + void use(ExtrusionLoop &loop) override { for (ExtrusionPath path : loop.paths) path.visit(*this); } + void use(ExtrusionEntityCollection &collection) override { for (ExtrusionEntity *entity : collection.entities) entity->visit(*this); } + double get(ExtrusionEntityCollection &coll) { + for (ExtrusionEntity *entity : coll.entities) entity->visit(*this); + return volume; + } +}; + +#include "../../libslic3r/GCodeReader.hpp" +TEST_CASE("Fill: extrude gcode and check it") +{ + + SECTION("simple square") { + Model model{}; + TriangleMesh sample_mesh = make_cube(5, 5, 0.2); + double volume = (5 * 5 * 0.2); + sample_mesh.repair(); + + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + config->set_key_value("perimeters", new ConfigOptionInt(1)); + config->set_key_value("top_solid_layers", new ConfigOptionInt(1)); + config->set_key_value("bottom_solid_layers", new ConfigOptionInt(1)); + + config->set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true)); + config->set_key_value("infill_overlap", new ConfigOptionFloatOrPercent(0.1, true)); + + config->set_key_value("skirts", new ConfigOptionInt(0)); + + config->set_key_value("layer_height", new ConfigOptionFloat(0.2)); // get a known number of layers + config->set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, false)); + + config->set_key_value("extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("solid_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("top_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + auto event_counter{ 0U }; + std::string stage; + Print print{}; + Slic3r::Test::init_print(print, { sample_mesh }, model, config); + print.process(); + + std::string gcode_filepath{ "" }; + Slic3r::Test::gcode(gcode_filepath, print); + //std::cout << "gcode generation done\n"; + std::string gcode_from_file = read_to_string(gcode_filepath); + + //string[] lineArray = gcode_from_file + GCodeReader parser; + double volume_extruded = 0; + int idx = 0; + double volume_perimeter_extruded = 0; + double volume_infill_extruded = 0; + // add remaining time lines where needed + parser.parse_buffer(gcode_from_file, + [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) + { + if (line.cmd_is("G1")) + { + if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) { + //std::cout << "add " << line.dist_E(reader)<<" now "<< volume_extruded<<"=>"; + volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + //std::cout << volume_extruded << "\n"; + if (idx<4)volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + else volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + idx++; + } + } + }); + + double perimeterRoundGapRemove = unscaled(print.objects()[0]->layers()[0]->slices.expolygons[0].contour.length()) * 0.1*0.1 * (2 - (PI / 2)); + double perimeterRoundGapAdd = unscaled(print.objects()[0]->layers()[0]->slices.expolygons[0].contour.length()) * 0.1*0.1 * ((PI / 2)); + //for (Line &l : print.objects()[0]->layers()[0]->slices.expolygons[0].contour.lines()) { + + //} + //std::cout << "flow mm3permm: " << Flow{ 0.5f,0.2f,0.4f,false }.mm3_per_mm() << "\n"; + //std::cout << "perimeter : " << unscaled(print.objects()[0]->layers()[0]->slices.expolygons[0].contour.length()) << " != " << (PI * 10) << "\n"; + + //std::cout << "created a mesh of volume " << volume << " and i have extruded " << volume_extruded << " mm3.\n"; + //std::cout << "Note that if we remove the bits of the external extrusion, it's only a volume of " << (volume - perimeterRoundGapRemove) << " that needs to be filled\n"; + //std::cout << "Note that if we add the bits of the external extrusion, it's a volume of " << (volume + perimeterRoundGapAdd) << " that needs to be filled\n"; + + double volumeExtrPerimeter = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->perimeters); + double volumeExtrInfill = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->fills); + + double volumeInfill = 0; + for (const ExPolygon & p : print.objects()[0]->layers()[0]->regions()[0]->fill_no_overlap_expolygons) { + volumeInfill += unscaled(unscaled(p.area())); + } + volumeInfill *= 0.2;/* + std::cout << "volumeRealr=" << (volume_perimeter_extruded + volume_infill_extruded) << " volumeRealPerimeter= " << volume_perimeter_extruded << " and volumeRealInfill=" << volume_infill_extruded << " mm3." << "\n"; + std::cout << "volumeExtr=" << (volumeExtrPerimeter + volumeExtrInfill) << " volumeExtrPerimeter= " << volumeExtrPerimeter << " and volumeExtrInfill=" << volumeExtrInfill << " mm3." << "\n"; + std::cout << "volumePerimeter= " << (volume - volumeInfill) << " volumePerimeter(wo/bits)= " << (volume - volumeInfill- perimeterRoundGapRemove) << " and volumeInfill=" << volumeInfill << " mm3." << "\n";*/ + + //Flow fl{0.5f, 0.2f, 0.4f, false}; + + //{ + // std::stringstream stri; + // stri << "extrusion_width_learning" << ".svg"; + // SVG svg(stri.str()); + // //svg.draw(bounds); + // svg.draw(print.objects()[0]->layers()[0]->slices.expolygons[0].contour, "green"); + // svg.draw(print.objects()[0]->layers()[0]->regions()[0]->fill_no_overlap_expolygons, "black", scale_(0.01)); + // svg.draw(print.objects()[0]->layers()[0]->regions()[0]->perimeters.as_polylines(), "orange", fl.scaled_width()); + // svg.draw(print.objects()[0]->layers()[0]->regions()[0]->perimeters.as_polylines(), "red", fl.scaled_spacing()); + // svg.draw(print.objects()[0]->layers()[0]->regions()[0]->fills.as_polylines(), "cyan", fl.scaled_width()); + // svg.draw(print.objects()[0]->layers()[0]->regions()[0]->fills.as_polylines(), "blue", fl.scaled_spacing()); + // svg.Close(); + //} + + //std::cout << gcode_from_file; + REQUIRE(abs(volumeInfill - volumeExtrInfill) < EPSILON); + REQUIRE(abs(volumeInfill - volume_infill_extruded) < 0.01); + REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volumeExtrPerimeter) < 0.01); + REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volume_perimeter_extruded) < 0.1); //there are a bit less for seam mitigation + clean_file(gcode_filepath, "gcode"); + + } + SECTION("simple disk") { + Model model{}; + TriangleMesh sample_mesh = make_cylinder(5, 0.2); + double volume = (PI * 25 * 0.2); + sample_mesh.repair(); + + DynamicPrintConfig *config = Slic3r::DynamicPrintConfig::new_from_defaults(); + config->set_key_value("perimeters", new ConfigOptionInt(1)); + config->set_key_value("top_solid_layers", new ConfigOptionInt(1)); + config->set_key_value("bottom_solid_layers", new ConfigOptionInt(1)); + + config->set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true)); + config->set_key_value("infill_overlap", new ConfigOptionFloatOrPercent(0.1, true)); + + config->set_key_value("skirts", new ConfigOptionInt(0)); + + config->set_key_value("layer_height", new ConfigOptionFloat(0.2)); // get a known number of layers + config->set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.2, false)); + + config->set_key_value("extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("first_layer_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("external_perimeter_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("solid_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("top_infill_extrusion_width", new ConfigOptionFloatOrPercent(0.5, false)); + auto event_counter{ 0U }; + std::string stage; + Print print{}; + Slic3r::Test::init_print(print, { sample_mesh }, model, config); + print.process(); + + std::string gcode_filepath{ "" }; + Slic3r::Test::gcode(gcode_filepath, print); + //std::cout << "gcode generation done\n"; + std::string gcode_from_file = read_to_string(gcode_filepath); + + //string[] lineArray = gcode_from_file + GCodeReader parser; + double volume_extruded = 0; + int idx = 0; + double volume_perimeter_extruded = 0; + double volume_infill_extruded = 0; + // add remaining time lines where needed + parser.parse_buffer(gcode_from_file, + [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) + { + if (line.cmd_is("G1")) + { + if (line.dist_E(reader) > 0 && line.dist_XY(reader) > 0) { + //std::cout << "add " << line.dist_E(reader)<<" now "<< volume_extruded<<"=>"; + volume_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + //std::cout << volume_extruded << "\n"; + if (idx<36)volume_perimeter_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + else volume_infill_extruded += line.dist_E(reader)*(PI*1.75*1.75 / 4.); + idx++; + } + } + }); + + double perimeterRoundGapRemove = unscaled(print.objects()[0]->layers()[0]->slices.expolygons[0].contour.length()) * 0.1*0.1 * (2 - (PI / 2)); + double perimeterRoundGapAdd = unscaled(print.objects()[0]->layers()[0]->slices.expolygons[0].contour.length()) * 0.1*0.1 * ((PI / 2)); + + double volumeExtrPerimeter = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->perimeters); + double volumeExtrInfill = ExtrusionGetVolume{}.get(print.objects()[0]->layers()[0]->regions()[0]->fills); + + double volumeInfill = 0; + for (const ExPolygon & p : print.objects()[0]->layers()[0]->regions()[0]->fill_no_overlap_expolygons) { + volumeInfill += unscaled(unscaled(p.area())); + } + volumeInfill *= 0.2; + std::cout << "volumeRealr=" << (volume_perimeter_extruded + volume_infill_extruded) << " volumeRealPerimeter= " << volume_perimeter_extruded << " and volumeRealInfill=" << volume_infill_extruded << " mm3." << "\n"; + std::cout << "volumeExtr=" << (volumeExtrPerimeter + volumeExtrInfill) << " volumeExtrPerimeter= " << volumeExtrPerimeter << " and volumeExtrInfill=" << volumeExtrInfill << " mm3." << "\n"; + std::cout << "volumePerimeter= " << (volume - volumeInfill) << " volumePerimeter(wo/bits)= " << (volume - volumeInfill - perimeterRoundGapRemove) << " and volumeInfill=" << volumeInfill << " mm3." << "\n"; + + REQUIRE(abs(volumeInfill - volumeExtrInfill) < EPSILON); + REQUIRE(abs(volumeInfill - volume_infill_extruded) < 0.01); + REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volumeExtrPerimeter) < EPSILON); + REQUIRE(abs((volume - volumeInfill - perimeterRoundGapRemove) - volume_perimeter_extruded) < 0.1); //there are a bit less for seam mitigation + clean_file(gcode_filepath, "gcode"); + + } +} + /* { @@ -458,8 +675,8 @@ bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_width, //note: here we do flow.width = flow_width , flow.gheight = 0.4, flow.nozzle_size = flow_width; Flow flow(flow_width, 0.4, flow_width); - filler->spacing = flow.spacing(); - params.density = density; + params.density = density; + filler->init_spacing(flow.spacing(), params); Polylines paths {filler->fill_surface(&surface, params)}; @@ -469,7 +686,7 @@ bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_width, // figure out what is actually going on here re: data types std::for_each(paths.begin(), paths.end(), [filler, &grown_paths] (const Slic3r::Polyline& p) { - polygons_append(grown_paths, offset(p, scale_(filler->spacing / 2.0))); + polygons_append(grown_paths, offset(p, scale_(filler->get_spacing() / 2.0))); }); ExPolygons uncovered = diff_ex(expolygon, grown_paths, true); diff --git a/src/test/libslic3r/test_print.cpp b/src/test/libslic3r/test_print.cpp index 3951cf14f..812efa9c6 100644 --- a/src/test/libslic3r/test_print.cpp +++ b/src/test/libslic3r/test_print.cpp @@ -88,7 +88,8 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t config->set_key_value("top_solid_layers", new ConfigOptionInt(2)); config->set_key_value("bottom_solid_layers", new ConfigOptionInt(1)); config->set_key_value("layer_height", new ConfigOptionFloat(0.5)); // get a known number of layers - config->set_key_value("first_layer_height", new ConfigOptionFloat(0.5)); + config->set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.5, false)); + config->set_key_value("enforce_full_fill_volume", new ConfigOptionBool(true)); Slic3r::Model model; auto event_counter {0U}; std::string stage; diff --git a/src/test/test_data.cpp b/src/test/test_data.cpp index 5488b2e26..2c84fc240 100644 --- a/src/test/test_data.cpp +++ b/src/test/test_data.cpp @@ -301,11 +301,11 @@ void init_print(Print& print, std::initializer_list meshes, Slic3r DynamicPrintConfig* config {Slic3r::DynamicPrintConfig::new_from_defaults()}; config->apply(*_config); - const std::string v {std::getenv("SLIC3R_TESTS_GCODE")}; - std::string tests_gcode {(v == "" ? "" : v)}; + //const std::string v {std::getenv("SLIC3R_TESTS_GCODE")}; + //std::string tests_gcode {(v == "" ? "" : v)}; - if (tests_gcode != ""s) - config->set_key_value("gcode_comments", new ConfigOptionBool(true)); + //if (tests_gcode != ""s) + //config->set_key_value("gcode_comments", new ConfigOptionBool(true)); for (const TriangleMesh& t : meshes) { ModelObject* object {model.add_object()};