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).
This commit is contained in:
supermerill 2019-04-23 13:17:45 +02:00
parent dc618b72bc
commit 7b71d74b38
9 changed files with 164 additions and 26 deletions

View File

@ -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.

View File

@ -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();

View File

@ -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_

View File

@ -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 &params,
ExtrusionEntitiesPtr &out) {

View File

@ -1439,7 +1439,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams &params)
{
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 &params, 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 &params, 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

View File

@ -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 &params, ExtrusionEntitiesPtr &out) override;
};
}; // namespace Slic3r

View File

@ -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.;

View File

@ -444,6 +444,7 @@ void PrintConfigDef::init_fff_params()
def->cli = "top-fill-pattern|external-fill-pattern=s";
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::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<InfillPattern>::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<InfillPattern>::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<InfillPattern>(ipRectilinear);
def->default_value = new ConfigOptionEnum<InfillPattern>(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"));

View File

@ -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<InfillPattern>::g
keys_map["smoothhilbert"] = ipSmoothHilbert;
keys_map["rectiwithperimeter"] = ipRectiWithPerimeter;
keys_map["scatteredrectilinear"] = ipScatteredRectilinear;
keys_map["rectilineargapfill"] = ipRectilinearWGapFill;
keys_map["sawtooth"] = ipSawtooth;
}
return keys_map;