From 7b71d74b3814d74571c4e0e7e78c5232e1cd2753 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Apr 2019 13:17:45 +0200 Subject: [PATCH] new solid infill pattern : rectilinear with gapfill. * it replace rectilinear fill by gapfill when width < spacing * 1.6 * it permit to fill spaces too thin to be filled by infill (if it's not already filled by the perimeter gapfill) * it print the gapfill after the rectilinear infill. * the exact volume option only do his thing on the rectilinear area, not the gapfill one (as it's already done in the medial axis). --- doc/How to build - Windows.md | 3 +- src/libslic3r/Fill/FillBase.cpp | 1 + src/libslic3r/Fill/FillBase.hpp | 15 +++ src/libslic3r/Fill/FillConcentric.cpp | 12 --- src/libslic3r/Fill/FillRectilinear2.cpp | 128 ++++++++++++++++++++++-- src/libslic3r/Fill/FillRectilinear2.hpp | 9 ++ src/libslic3r/LayerRegion.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 17 +++- src/libslic3r/PrintConfig.hpp | 3 +- 9 files changed, 164 insertions(+), 26 deletions(-) diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index b37adbd1f..e09c2a1d2 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -103,5 +103,6 @@ Refer to the CMake scripts inside the `deps` directory to see which dependencies ### building tests -You must use vs 2015 or 2017, and convert the slic3r_test project to your version. Also, you have to add the "CRT SDK" to your windows 2017 installation (can be done via the VS intaller). +You must use visual studio 2017, and build all deps & all projects with it (not the same compiler as vs 2013). Also, you have to add the "CRT SDK" to your windows 2017 installation (can be done via the VS intaller). +Tested only with vs 2017. Should work with 2015 and 2019. diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 84850fb2a..b7abb4f96 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -28,6 +28,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipGyroid: return new FillGyroid(); case ipRectilinear: return new FillRectilinear2(); // case ipRectilinear: return new FillRectilinear(); + case ipRectilinearWGapFill: return new FillRectilinear2WGapFill(); case ipScatteredRectilinear:return new FillScatteredRectilinear(); case ipLine: return new FillLine(); case ipGrid: return new FillGrid2(); diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index d0043c73f..fa35ab226 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -13,6 +13,8 @@ #include "../PrintConfig.hpp" #include "../Utils.hpp" #include "../Flow.hpp" +#include "../ExtrusionEntity.hpp" +#include "../ExtrusionEntityCollection.hpp" namespace Slic3r { @@ -166,6 +168,19 @@ public: { return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); } }; + +class ExtrusionSetRole : public ExtrusionVisitor { + ExtrusionRole new_role; +public: + ExtrusionSetRole(ExtrusionRole role) : new_role(role) {} + void use(ExtrusionPath &path) override { path.set_role(new_role); } + void use(ExtrusionPath3D &path3D) override { path3D.set_role(new_role); } + void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } + void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } + void use(ExtrusionLoop &loop) override { for (ExtrusionPath path : loop.paths) path.set_role(new_role); } + void use(ExtrusionEntityCollection &collection) override { for (ExtrusionEntity *entity : collection.entities) entity->visit(*this); } +}; + } // namespace Slic3r #endif // slic3r_FillBase_hpp_ diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 1f22e0f0f..27147f746 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -65,18 +65,6 @@ void FillConcentric::_fill_surface_single( // We want the loops to be split inside the G-code generator to get optimum path planning. } -class ExtrusionSetRole : public ExtrusionVisitor { - ExtrusionRole new_role; -public: - ExtrusionSetRole(ExtrusionRole role) : new_role(role) {} - void use(ExtrusionPath &path) override { path.set_role(new_role); } - void use(ExtrusionPath3D &path3D) override { path3D.set_role(new_role); } - void use(ExtrusionMultiPath &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } - void use(ExtrusionMultiPath3D &multipath) override { for (ExtrusionPath path : multipath.paths) path.set_role(new_role); } - void use(ExtrusionLoop &loop) override { for (ExtrusionPath path : loop.paths) path.set_role(new_role); } - void use(ExtrusionEntityCollection &collection) override { for (ExtrusionEntity *entity : collection.entities) entity->visit(*this); } -}; - void FillConcentricWGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 17844ab13..21976ff5d 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -1439,7 +1439,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams ¶ms) { Polylines polylines_out; - if (! fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out)) { + if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out)) { printf("FillRectilinear2::fill_surface() failed to fill a region.\n"); } return polylines_out; @@ -1657,7 +1657,7 @@ void FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { 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 ; + const coord_t tooth_spacing_min = scaled_nozzle_diam; const coord_t tooth_spacing_max = scaled_nozzle_diam * 3; const coord_t tooth_zhop = scaled_nozzle_diam; Polylines polylines_out; @@ -1678,7 +1678,7 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi extrusions->paths.push_back(ExtrusionPath3D(good_role, params.flow->mm3_per_mm() * params.flow_mult, params.flow->width * params.flow_mult, params.flow->height)); ExtrusionPath3D *current_extrusion = &(extrusions->paths.back()); Points &pts = poly.points; - coord_t next_zhop = tooth_spacing_min + (coord_t) abs((rand() / (float)RAND_MAX) * (tooth_spacing_max - tooth_spacing_min)); + coord_t next_zhop = tooth_spacing_min + (coord_t)abs((rand() / (float)RAND_MAX) * (tooth_spacing_max - tooth_spacing_min)); size_t idx = 1; current_extrusion->push_back(pts[0], 0); @@ -1688,7 +1688,7 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi while (idx < poly.size() && maxLength > tooth_spacing_max) { //go next hop line //do not use the "return" line nor the tangent ones. - while (idx < poly.size() && maxLength > tooth_spacing_min && (next_zhop >= line_length || line_length < clearance + while (idx < poly.size() && maxLength > tooth_spacing_min && (next_zhop >= line_length || line_length < clearance || (std::abs(std::abs((int)(this->angle * 180 / PI) % 180) - 90) > 45 ? pts[idx].y() < pts[idx - 1].y() : pts[idx].x() < pts[idx - 1].x()))) { if (line_length < clearance || pts[idx].x() < pts[idx - 1].x()) { @@ -1740,9 +1740,9 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi current_extrusion = &(extrusions->paths.back()); current_extrusion->push_back(last, 0); line_length = (coord_t)last.distance_to(pts[idx]); - + //re-init - next_zhop = tooth_spacing_min + (coord_t) abs((rand() / (float)RAND_MAX) * (tooth_spacing_max - tooth_spacing_min)); + next_zhop = tooth_spacing_min + (coord_t)abs((rand() / (float)RAND_MAX) * (tooth_spacing_max - tooth_spacing_min)); } } while (idx < poly.size()) { @@ -1767,5 +1767,121 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi } +void +FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) { + ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); + coll_nosort->no_sort = true; //can be sorted inside the pass + ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); + + // remove areas for gapfill + ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * 0.8f, params.flow->scaled_spacing() * 0.8f); + ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, rectilinear_areas); + double rec_area = 0; + for (ExPolygon &p : rectilinear_areas)rec_area += p.area(); + double gf_area = 0; + for (ExPolygon &p : gapfill_areas)gf_area += p.area(); + std::cout << unscaled(unscaled(surface->expolygon.area())) << " = " << unscaled(unscaled(rec_area)) << " + " << unscaled(unscaled(gf_area)) << "\n"; + + // rectilinear + Polylines polylines_rectilinear; + Surface rectilinear_surface{ *surface }; + for (const ExPolygon &rectilinear_area : rectilinear_areas) { + rectilinear_surface.expolygon = rectilinear_area; + if (!fill_surface_by_lines(&rectilinear_surface, params, 0.f, 0.f, polylines_rectilinear)) { + printf("FillRectilinear2::fill_surface() failed to fill a region.\n"); + } + } + if (!polylines_rectilinear.empty()) { + double flow_mult_exact_volume = 1; + //check if not over-extruding + if (!params.dont_adjust && params.full_infill() && !params.flow->bridge && params.fill_exactly) { + // compute the path of the nozzle -> extruded volume + double lengthTot = 0; + int nbLines = 0; + for (const Polyline &pline : polylines_rectilinear) { + Lines lines = pline.lines(); + for (auto line = lines.begin(); line != lines.end(); ++line) { + lengthTot += unscaled(line->length()); + nbLines++; + } + } + double extrudedVolume = params.flow->mm3_per_mm() * lengthTot; + // compute real volume + double poylineVolume = 0; + ExPolygons rectilinear_no_overlap_areas = intersection_ex(rectilinear_areas, this->no_overlap_expolygons); + for (const ExPolygon &poly : rectilinear_no_overlap_areas) { + 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, + // (poylineVolume), + // (poylineVolume) / extrudedVolume, + // this->no_overlap_expolygons.size()); + if (extrudedVolume != 0 && poylineVolume != 0) flow_mult_exact_volume = poylineVolume / extrudedVolume; + //failsafe, it can happen + if (flow_mult_exact_volume > 1.3) flow_mult_exact_volume = 1.3; + if (flow_mult_exact_volume < 0.8) flow_mult_exact_volume = 0.8; + } + + //Create extrusions + ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); + /// pass the no_sort attribute to the extrusion path + eec->no_sort = this->no_sort(); + + extrusion_entities_append_paths( + eec->entities, polylines_rectilinear, + good_role, + params.flow->mm3_per_mm() * params.flow_mult * flow_mult_exact_volume, + params.flow->width * params.flow_mult * flow_mult_exact_volume, + params.flow->height); + + coll_nosort->entities.push_back(eec); + } + + //gapfill + ThickPolylines polylines_gapfill; + double min = 0.2 * params.flow->scaled_width() * (1 - INSET_OVERLAP_TOLERANCE); + double max = 2. * params.flow->scaled_width(); + for (const ExPolygon &ex : gapfill_areas) { + //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() > min * max) { + ex.medial_axis(ex, params.flow->scaled_width()*2, params.flow->scaled_width()/5, &polylines_gapfill, params.flow->height); + } + } + if (!polylines_gapfill.empty() && good_role != erBridgeInfill) { + ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, *params.flow); + //set role if needed + if (good_role != erSolidInfill) { + ExtrusionSetRole set_good_role(good_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_nosort->entities.push_back(coll_gapfill); + } + } + + // === end === + if (!coll_nosort->empty()) { + out.push_back(coll_nosort); + } else { + delete coll_nosort; + } + +} + + } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillRectilinear2.hpp b/src/libslic3r/Fill/FillRectilinear2.hpp index e91ecdb08..9b33a6af1 100644 --- a/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/src/libslic3r/Fill/FillRectilinear2.hpp @@ -111,6 +111,15 @@ public: }; +class FillRectilinear2WGapFill : public FillRectilinear2 +{ +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; +}; + }; // namespace Slic3r diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index a4039f865..6b55f3234 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -98,7 +98,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) coord_t max_margin = 0; if ((this->region()->config().perimeters > 0)) { - max_margin = scale_(this->flow(frExternalPerimeter).width) + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1); + max_margin = this->flow(frExternalPerimeter).scaled_width() + this->flow(frPerimeter).scaled_spacing() * (this->region()->config().perimeters.value - 1); } const Surfaces &surfaces = this->fill_surfaces.surfaces; const bool has_infill = this->region()->config().fill_density.value > 0.; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2decc53fe..f7f3ec366 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -444,6 +444,7 @@ void PrintConfigDef::init_fff_params() def->cli = "top-fill-pattern|external-fill-pattern=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("rectilineargapfill"); def->enum_values.push_back("concentric"); def->enum_values.push_back("concentricgapfill"); def->enum_values.push_back("hilbertcurve"); @@ -454,6 +455,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("smoothtriple"); def->enum_values.push_back("smoothhilbert"); def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Rectilinear (filled)")); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Concentric (filled)")); def->enum_labels.push_back(L("Hilbert Curve")); @@ -471,12 +473,14 @@ void PrintConfigDef::init_fff_params() def->cli = "bottom-fill-pattern|external-fill-pattern=s"; def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("rectilineargapfill"); def->enum_values.push_back("concentric"); def->enum_values.push_back("concentricgapfill"); def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_values.push_back("smooth"); + def->enum_labels.push_back(L("Rectilinear (filled)")); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Concentric (filled)")); @@ -490,24 +494,27 @@ void PrintConfigDef::init_fff_params() def = this->add("solid_fill_pattern", coEnum); def->label = L("Solid pattern"); def->category = L("Infill"); - def->tooltip = L("Fill pattern for solid (internal) infill. This only affects the solid not-visible layers. You should use rectilinear is most cases. You can use ironing for transluscnet material."); + def->tooltip = L("Fill pattern for solid (internal) infill. This only affects the solid not-visible layers. You should use rectilinear is most cases. You can try ironing for transluscnet material." + " Rectilinear (filled) replace zig-zag patterns by a single big line & is more efficient for filling little spaces."); def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); - def->enum_values.push_back("rectilinear"); def->enum_values.push_back("smooth"); + def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("rectilineargapfill"); def->enum_values.push_back("concentric"); def->enum_values.push_back("concentricgapfill"); def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); - def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Ironing")); + def->enum_labels.push_back(L("Rectilinear")); + def->enum_labels.push_back(L("Rectilinear (filled)")); def->enum_labels.push_back(L("Concentric")); def->enum_labels.push_back(L("Concentric (filled)")); def->enum_labels.push_back(L("Hilbert Curve")); def->enum_labels.push_back(L("Archimedean Chords")); def->enum_labels.push_back(L("Octagram Spiral")); def->mode = comExpert; - def->default_value = new ConfigOptionEnum(ipRectilinear); + def->default_value = new ConfigOptionEnum(ipRectilinearWGapFill); def = this->add("enforce_full_fill_volume", coBool); def->label = L("Enforce 100% fill volume"); @@ -1014,7 +1021,7 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); - def->enum_values.push_back("scatteredrectilinear"); + def->enum_values.push_back("rectilineargapfill"); def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Triangles")); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 802d9bcb8..ecc514196 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -42,7 +42,7 @@ enum PrintHostType { enum InfillPattern { ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSmooth, ipSmoothHilbert, ipSmoothTriple, - ipRectiWithPerimeter, ipConcentricGapFill, ipScatteredRectilinear, ipSawtooth + ipRectiWithPerimeter, ipConcentricGapFill, ipScatteredRectilinear, ipSawtooth, ipRectilinearWGapFill }; enum SupportMaterialPattern { @@ -149,6 +149,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g keys_map["smoothhilbert"] = ipSmoothHilbert; keys_map["rectiwithperimeter"] = ipRectiWithPerimeter; keys_map["scatteredrectilinear"] = ipScatteredRectilinear; + keys_map["rectilineargapfill"] = ipRectilinearWGapFill; keys_map["sawtooth"] = ipSawtooth; } return keys_map;