Dense infill update:

* now under solid layers instead of top layers
* split surface if too big
* add pattern selection (default to rectilinear)
* add pattern angle
* use external_infill_margin to be a bit bigger than the solid area
* also a better count algorithm, but not perfect (i can see some shenanigan but it's not feature-breaking)
This commit is contained in:
supermerill 2018-07-24 18:58:11 +02:00
parent 87245ae3c1
commit 6b2bd269cd
11 changed files with 208 additions and 42 deletions

View File

@ -56,6 +56,32 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
//FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions.
std::vector<SurfacesPtr> groups;
layerm.fill_surfaces.group(&groups);
//if internal infill can be dense, place it on his own group
if (layerm.region()->config.infill_dense_layers.getInt() > 0) {
SurfacesPtr *denseGroup = NULL;
const uint32_t nbGroups = groups.size();
for (uint32_t num_group = 0; num_group < nbGroups; ++num_group) {
for (uint32_t num_srf = 0; num_srf < groups[num_group].size(); ++num_srf) {
Surface *srf = groups[num_group][num_srf];
//if solid, wrong group
if (srf->is_solid()) break;
//find surface that can be used as dense infill
if (srf->maxNbSolidLayersOnTop <= layerm.region()->config.infill_dense_layers.getInt()
&& srf->maxNbSolidLayersOnTop > 0) {
//remove from the group
groups[num_group].erase(groups[num_group].begin() + num_srf);
--num_srf;
//add to the "dense" group
if (denseGroup == NULL) {
groups.resize(groups.size() + 1);
denseGroup = &groups.back();
}
denseGroup->push_back(srf);
}
}
}
}
// merge compatible groups (we can generate continuous infill for them)
{
@ -91,7 +117,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round.
for (size_t round = 0; round < 2; ++ round) {
for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) {
for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++it_group) {
const SurfacesPtr &group = *it_group;
bool is_bridge = group.front()->bridge_angle >= 0;
if (is_bridge != (round == 0))
@ -99,12 +125,13 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Make a union of polygons defining the infiill regions of a group, use a safety offset.
Polygons union_p = union_(to_polygons(*it_group), true);
// Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
if (! polygons_bridged.empty() && ! is_bridge)
if (!polygons_bridged.empty() && !is_bridge)
union_p = diff(union_p, polygons_bridged, true);
// subtract any other surface already processed
//FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
// Using group.front() as a template.
surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
}
}
}
@ -149,7 +176,6 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ],
// );
}
for (const Surface &surface : surfaces) {
if (surface.surface_type == stInternalVoid)
continue;
@ -158,7 +184,8 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
FlowRole role = (surface.is_top()) ? frTopSolidInfill :
(surface.is_solid() ? frSolidInfill : frInfill);
bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
bool is_denser = false;
if (surface.is_solid()) {
density = 100.;
fill_pattern = (surface.is_external() && ! is_bridge) ?
@ -166,9 +193,11 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
ipRectilinear;
} else {
if (layerm.region()->config.infill_dense_layers.getInt() > 0
&& surface.maxNbLayersOnTop < layerm.region()->config.infill_dense_layers.getInt() + layerm.region()->config.top_solid_layers.getInt()
&& surface.maxNbLayersOnTop >= layerm.region()->config.top_solid_layers.getInt()){
&& surface.maxNbSolidLayersOnTop <= layerm.region()->config.infill_dense_layers.getInt()
&& surface.maxNbSolidLayersOnTop > 0) {
density = layerm.region()->config.infill_dense_density.getFloat();
is_denser = true;
fill_pattern = layerm.region()->config.infill_dense_pattern.value;
}
if (density <= 0)
continue;
@ -223,12 +252,12 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
f->layer_id = layerm.layer()->id();
f->z = layerm.layer()->print_z;
f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value));
if (is_denser)f->angle = float(Geometry::deg2rad(layerm.region()->config.infill_dense_angle.value));
else f->angle = float(Geometry::deg2rad(layerm.region()->config.fill_angle.value));
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
// f->layer_height = h;
//give the overlap size, it's not the real value (it can depend of the external_perimeter_extrusion_width)
f->overlap = layerm.region()->config.infill_overlap.get_abs_value(flow.nozzle_diameter);
// apply half spacing using this flow's own spacing and generate infill
@ -249,7 +278,8 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
float flow_percent = 1;
if(surface.is_overBridge()){
params.flow_mult = layerm.region()->config.over_bridge_flow_ratio;
params.density = layerm.region()->config.over_bridge_flow_ratio;
//params.flow_mult = layerm.region()->config.over_bridge_flow_ratio;
}
f->fill_surface_extrusion(&surface, params, flow, out);

View File

@ -25,7 +25,7 @@ struct FillParams
fill_exactly = false;
}
bool full_infill() const { return density > 0.9999f; }
bool full_infill() const { return density > 0.9999f && density < 1.0001f; }
// Fill density, fraction in <0, 1>
float density;

View File

@ -205,7 +205,7 @@ public:
void _make_perimeters();
void _infill();
void clip_fill_surfaces();
void count_distance_top();
void count_distance_solid();
void discover_horizontal_shells();
void combine_infill();
void _generate_support_material();

View File

@ -844,6 +844,15 @@ PrintConfigDef::PrintConfigDef()
def->cli = "infill-dense-layers=i";
def->min = 0;
def->default_value = new ConfigOptionInt(0);
def = this->add("infill_dense_angle", coFloat);
def->label = L("angle");
def->category = L("Infill");
def->tooltip = L("Set the Angle of dense infill.");
def->sidetext = L("layers");
def->cli = "infill-dense-angle=i";
def->min = 0;
def->default_value = new ConfigOptionFloat(0);
def = this->add("infill_dense_density", coPercent);
def->gui_type = "f_enum_open";
@ -882,6 +891,40 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("75");
def->enum_labels.push_back("100");
def->default_value = new ConfigOptionPercent(42);
def = this->add("infill_dense_pattern", coEnum);
def->label = L("pattern");
def->category = L("Sparse fill pattern");
def->tooltip = L("Fill pattern for denser-density sparse infill.");
def->cli = "dense-fill-pattern=s";
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("grid");
def->enum_values.push_back("triangles");
def->enum_values.push_back("stars");
def->enum_values.push_back("cubic");
def->enum_values.push_back("line");
def->enum_values.push_back("concentric");
def->enum_values.push_back("honeycomb");
def->enum_values.push_back("3dhoneycomb");
def->enum_values.push_back("gyroid");
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
def->enum_labels.push_back("Rectilinear");
def->enum_labels.push_back("Grid");
def->enum_labels.push_back("Triangles");
def->enum_labels.push_back("Stars");
def->enum_labels.push_back("Cubic");
def->enum_labels.push_back("Line");
def->enum_labels.push_back("Concentric");
def->enum_labels.push_back("Honeycomb");
def->enum_labels.push_back("3D Honeycomb");
def->enum_labels.push_back("Gyroid");
def->enum_labels.push_back("Hilbert Curve");
def->enum_labels.push_back("Archimedean Chords");
def->enum_labels.push_back("Octagram Spiral");
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
def = this->add("infill_extruder", coInt);
def->label = L("Infill extruder");
@ -2233,6 +2276,11 @@ std::string FullPrintConfig::validate()
// --bottom-fill-pattern
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(this->bottom_fill_pattern.serialize()))
return "Invalid value for --bottom-fill-pattern";
// --infill-dense-pattern
if (! print_config_def.get("infill_dense_pattern")->has_enum_value(this->infill_dense_pattern.serialize()))
return "Invalid value for --infill-dense-pattern";
// --fill-density
if (fabs(this->fill_density.value - 100.) < EPSILON &&

View File

@ -409,7 +409,9 @@ public:
ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed;
ConfigOptionInt infill_dense_layers;
ConfigOptionFloat infill_dense_angle;
ConfigOptionPercent infill_dense_density;
ConfigOptionEnum<InfillPattern> infill_dense_pattern;
ConfigOptionBool overhangs;
ConfigOptionBool no_perimeter_unsupported;
ConfigOptionInt min_perimeter_unsupported;
@ -458,7 +460,9 @@ protected:
OPT_PTR(infill_overlap);
OPT_PTR(infill_speed);
OPT_PTR(infill_dense_layers);
OPT_PTR(infill_dense_angle);
OPT_PTR(infill_dense_density);
OPT_PTR(infill_dense_pattern);
OPT_PTR(overhangs);
OPT_PTR(no_perimeter_unsupported);
OPT_PTR(min_perimeter_unsupported);

View File

@ -193,7 +193,9 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "infill_every_layers"
|| opt_key == "solid_infill_every_layers"
|| opt_key == "infill_dense_layers"
|| opt_key == "infill_dense_angle"
|| opt_key == "infill_dense_density"
|| opt_key == "infill_dense_pattern"
|| opt_key == "bottom_solid_layers"
|| opt_key == "top_solid_layers"
|| opt_key == "solid_infill_below_area"
@ -381,8 +383,8 @@ void PrintObject::_prepare_infill()
this->combine_infill();
// count the distance from the nearest top surface, to allow to use denser infill
// if neded and if infill_dense_layers is positive.
this->count_distance_top();
// if needed and if infill_dense_layers is positive.
this->count_distance_solid();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
@ -399,36 +401,116 @@ void PrintObject::_prepare_infill()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
void PrintObject::count_distance_top(){
void PrintObject::count_distance_solid() {
for (int idx_region = 0; idx_region < this->_print->regions.size(); ++idx_region) {
//count how many surface there are on each one
LayerRegion *previousOne = NULL;
if (this->layers.size() > 1) previousOne = this->layers[this->layers.size() - 1]->get_region(idx_region);
if (previousOne != NULL && previousOne->region()->config.infill_dense_layers.getInt() > 0){
if (previousOne != NULL && previousOne->region()->config.infill_dense_layers.getInt() > 0) {
for (Surface &surf : previousOne->fill_surfaces.surfaces) {
if (surf.is_solid()) {
surf.maxNbSolidLayersOnTop = 0;
}
}
for (int idx_layer = this->layers.size() - 2; idx_layer >= 0; --idx_layer){
LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region);
for (Surface &surf : layerm->fill_surfaces.surfaces){
if (!surf.is_top()){
surf.maxNbLayersOnTop = 65000;
Surfaces surf_to_add;
for (auto it_surf = layerm->fill_surfaces.surfaces.begin(); it_surf != layerm->fill_surfaces.surfaces.end(); ++it_surf) {
Surface &surf = *it_surf;
if (!surf.is_solid()){
surf.maxNbSolidLayersOnTop = 30000;
uint16_t dense_dist = 30000;
ExPolygons dense_polys;
ExPolygons sparse_polys = { surf.expolygon };
//find the surface which intersect with the smalle maxNb possible
for (Surface &upp : previousOne->fill_surfaces.surfaces){
// i'm using that because the result is better & different than
// upp.expolygon.overlaps(surf.expolygon), surf.expolygon.overlaps(upp.expolygon)
if (intersection_ex(surf, upp).size() > 0){
surf.maxNbLayersOnTop = std::min(surf.maxNbLayersOnTop, (unsigned short)(upp.maxNbLayersOnTop + 1));
for (Surface &upp : previousOne->fill_surfaces.surfaces) {
// i'm using intersection_ex because the result different than
// upp.expolygon.overlaps(surf.expolygon) or surf.expolygon.overlaps(upp.expolygon)
ExPolygons intersect = intersection_ex(sparse_polys, ExPolygons() = { upp.expolygon }, true);
if (!intersect.empty()) {
uint16_t dist = (uint16_t)(upp.maxNbSolidLayersOnTop + 1);
if (dist <= layerm->region()->config.infill_dense_layers.getInt()) {
// it will be a dense infill, split the surface if needed
uint64_t area_intersect = 0;
for (ExPolygon poly_inter : intersect) area_intersect += poly_inter.area();
//if it's in a dense area and the current surface isn't a dense one yet and the not-dense is too small.
if (surf.area() > area_intersect * 3 &&
surf.maxNbSolidLayersOnTop > layerm->region()->config.infill_dense_layers.getInt()) {
//split in two
if (dist == 1) {
//if just under the solid area, we can expand a bit
//remove too small sections and grew a bit to anchor it into the part
intersect = offset2_ex(intersect,
-layerm->flow(frInfill).scaled_width(),
layerm->flow(frInfill).scaled_width() + scale_(layerm->region()->config.external_infill_margin));
} else {
//just remove too small sections
intersect = offset2_ex(intersect,
-layerm->flow(frInfill).scaled_width(),
layerm->flow(frInfill).scaled_width());
}
if (!intersect.empty()) {
ExPolygons sparse_surfaces = offset2_ex(
diff_ex(sparse_polys, intersect, true),
-layerm->flow(frInfill).scaled_width(),
layerm->flow(frInfill).scaled_width());
ExPolygons dense_surfaces = diff_ex(sparse_polys, sparse_surfaces, true);
//assign (copy)
sparse_polys.clear();
sparse_polys.insert(sparse_polys.begin(), sparse_surfaces.begin(), sparse_surfaces.end());
dense_polys.insert(dense_polys.end(), dense_surfaces.begin(), dense_surfaces.end());
dense_dist = std::min(dense_dist, dist);
}
} else {
surf.maxNbSolidLayersOnTop = std::min(surf.maxNbSolidLayersOnTop, dist);
}
} else {
surf.maxNbSolidLayersOnTop = std::min(surf.maxNbSolidLayersOnTop, dist);
}
}
}
}else{
surf.maxNbLayersOnTop = 0;
//check if we need to split the surface
if (dense_dist != 30000) {
uint64_t area_dense = 0;
for (ExPolygon poly_inter : dense_polys) area_dense += poly_inter.area();
uint64_t area_sparse = 0;
for (ExPolygon poly_inter : sparse_polys) area_sparse += poly_inter.area();
if (area_sparse > area_dense * 3) {
//split
dense_polys = union_ex(dense_polys);
for (ExPolygon dense_poly : dense_polys) {
Surface dense_surf(surf, dense_poly);
dense_surf.maxNbSolidLayersOnTop = dense_dist;
surf_to_add.push_back(dense_surf);
}
sparse_polys = union_ex(sparse_polys);
for (ExPolygon sparse_poly : sparse_polys) {
Surface sparse_surf(surf, sparse_poly);
surf_to_add.push_back(sparse_surf);
}
//layerm->fill_surfaces.surfaces.erase(it_surf);
} else {
surf.maxNbSolidLayersOnTop = dense_dist;
surf_to_add.push_back(surf);
}
} else {
surf_to_add.push_back(surf);
}
} else {
surf.maxNbSolidLayersOnTop = 0;
surf_to_add.push_back(surf);
}
}
//if (!surf_to_add.empty()) {
// layerm->fill_surfaces.surfaces.insert(layerm->fill_surfaces.surfaces.begin(), surf_to_add.begin(), surf_to_add.end());
//}
layerm->fill_surfaces.surfaces.clear();
layerm->fill_surfaces.surfaces.insert(layerm->fill_surfaces.surfaces.begin(), surf_to_add.begin(), surf_to_add.end());
previousOne = layerm;
}
}
}
}
// This function analyzes slices of a region (SurfaceCollection slices).

View File

@ -41,42 +41,42 @@ public:
unsigned short thickness_layers; // in layers
double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined)
unsigned short extra_perimeters;
unsigned short maxNbLayersOnTop;
uint16_t maxNbSolidLayersOnTop;
Surface(const Slic3r::Surface &rhs)
: surface_type(rhs.surface_type), expolygon(rhs.expolygon),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters),
maxNbLayersOnTop(rhs.maxNbLayersOnTop)
maxNbSolidLayersOnTop(rhs.maxNbSolidLayersOnTop)
{};
Surface(SurfaceType _surface_type, const ExPolygon &_expolygon)
: surface_type(_surface_type), expolygon(_expolygon),
thickness(-1), thickness_layers(1), bridge_angle(-1), extra_perimeters(0),
maxNbLayersOnTop(0)
maxNbSolidLayersOnTop(-1)
{};
Surface(const Surface &other, const ExPolygon &_expolygon)
: surface_type(other.surface_type), expolygon(_expolygon),
thickness(other.thickness), thickness_layers(other.thickness_layers),
bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters),
maxNbLayersOnTop(other.maxNbLayersOnTop)
maxNbSolidLayersOnTop(other.maxNbSolidLayersOnTop)
{};
Surface(Surface &&rhs)
: surface_type(rhs.surface_type), expolygon(std::move(rhs.expolygon)),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters),
maxNbLayersOnTop(rhs.maxNbLayersOnTop)
maxNbSolidLayersOnTop(rhs.maxNbSolidLayersOnTop)
{};
Surface(SurfaceType _surface_type, const ExPolygon &&_expolygon)
: surface_type(_surface_type), expolygon(std::move(_expolygon)),
thickness(-1), thickness_layers(1), bridge_angle(-1), extra_perimeters(0),
maxNbLayersOnTop(0)
maxNbSolidLayersOnTop(-1)
{};
Surface(const Surface &other, const ExPolygon &&_expolygon)
: surface_type(other.surface_type), expolygon(std::move(_expolygon)),
thickness(other.thickness), thickness_layers(other.thickness_layers),
bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters),
maxNbLayersOnTop(other.maxNbLayersOnTop)
maxNbSolidLayersOnTop(other.maxNbSolidLayersOnTop)
{};
Surface& operator=(const Surface &rhs)
@ -87,7 +87,7 @@ public:
thickness_layers = rhs.thickness_layers;
bridge_angle = rhs.bridge_angle;
extra_perimeters = rhs.extra_perimeters;
maxNbLayersOnTop = rhs.maxNbLayersOnTop;
maxNbSolidLayersOnTop = rhs.maxNbSolidLayersOnTop;
return *this;
}
@ -99,7 +99,7 @@ public:
thickness_layers = rhs.thickness_layers;
bridge_angle = rhs.bridge_angle;
extra_perimeters = rhs.extra_perimeters;
maxNbLayersOnTop = rhs.maxNbLayersOnTop;
maxNbSolidLayersOnTop = rhs.maxNbSolidLayersOnTop;
return *this;
}

View File

@ -569,7 +569,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
break;
case coEnum:{
if (opt_key.compare("top_fill_pattern") == 0 || opt_key.compare("bottom_fill_pattern") == 0 ||
opt_key.compare("fill_pattern") == 0)
opt_key.compare("fill_pattern") == 0 || opt_key.compare("infill_dense_pattern") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value)));
else if (opt_key.compare("gcode_flavor") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value)));

View File

@ -448,7 +448,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
break;
case coEnum:{
if (opt_key.compare("top_fill_pattern") == 0 || opt_key.compare("bottom_fill_pattern") == 0 ||
opt_key.compare("fill_pattern") == 0 ){
opt_key.compare("fill_pattern") == 0 || opt_key.compare("infill_dense_pattern") == 0 ){
ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value);
}
else if (opt_key.compare("gcode_flavor") == 0 ){

View File

@ -300,8 +300,8 @@ const std::vector<std::string>& Preset::print_options()
"over_bridge_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "only_one_perimeter_top", "compatible_printers",
"compatible_printers_condition", "inherits", "infill_dense_layers", "infill_dense_density", "no_perimeter_unsupported",
"min_perimeter_unsupported", "noperi_bridge_only"
"compatible_printers_condition", "inherits", "infill_dense_layers", "infill_dense_density", "infill_dense_pattern",
"infill_dense_angle", "no_perimeter_unsupported", "min_perimeter_unsupported", "noperi_bridge_only"
};
return s_opts;
}

View File

@ -817,7 +817,7 @@ void TabPrint::build()
optgroup->append_single_option_line("seam_position");
optgroup->append_single_option_line("external_perimeters_first");
page = add_options_page(_(L("Infill")), "infill.png");
page = add_options_page(_(L("Infill")), "infill.png");
optgroup = page->new_optgroup(_(L("Infill")));
optgroup->append_single_option_line("fill_density");
optgroup->append_single_option_line("fill_pattern");
@ -828,9 +828,11 @@ void TabPrint::build()
optgroup = page->new_optgroup(_(L("Reducing printing time")));
optgroup->append_single_option_line("infill_every_layers");
optgroup->append_single_option_line("infill_only_where_needed");
line = { _(L("Use denser infill below top layers")), "" };
line = { _(L("Use denser infill below solid layers")), "" };
line.append_option(optgroup->get_option("infill_dense_layers"));
line.append_option(optgroup->get_option("infill_dense_density"));
line.append_option(optgroup->get_option("infill_dense_pattern"));
line.append_option(optgroup->get_option("infill_dense_angle"));
optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Advanced")));