Reverted the perimeter generator to not save the perimeter areas.

These could be calculated from the fill areas if needed.
On the other side, the non-classified (non-split) fill areas are stored
now for use in the "ensure vertical wall thickness" feature,
also the non-split fill areas are re-used when recalculating the infills.
This is safer than trying to stitch the fill region together from the
classified fragments.

Modified the "ensure vertical wall thickness" feature to use the non-split
fill areas instead of perimeter areas for the calculation
of non-supported regions. This is cheaper as the fill areas contain
roughly half the edges.
This commit is contained in:
bubnikv 2016-11-17 23:22:59 +01:00
parent 2085a482c7
commit 79f5a16536
12 changed files with 94 additions and 116 deletions

View file

@ -259,7 +259,7 @@ sub slice {
}
}
# merge all regions' slices to get islands
# Merge all regions' slices to get islands, chain them by a shortest path.
$layer->make_slices;
}
@ -367,6 +367,9 @@ sub _slice_region {
return $mesh->slice($z);
}
# 1) Merges typed region slices into stInternal type.
# 2) Increases an "extra perimeters" counter at region slices where needed.
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
sub make_perimeters {
my $self = shift;
@ -377,7 +380,8 @@ sub make_perimeters {
$self->set_step_started(STEP_PERIMETERS);
$self->print->status_cb->(20, "Generating perimeters");
# merge slices if they were split into types
# Merge region slices if they were split into types.
# FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration.
if ($self->typed_slices) {
$_->merge_slices for @{$self->layers};
$self->set_typed_slices(0);
@ -487,11 +491,12 @@ sub prepare_infill {
$self->set_step_started(STEP_PREPARE_INFILL);
$self->print->status_cb->(30, "Preparing infill");
# this will assign a type (top/bottom/internal) to $layerm->slices
# and transform $layerm->fill_surfaces from expolygon
# to typed top/bottom/internal surfaces;
# This will assign a type (top/bottom/internal) to $layerm->slices.
# Then the classifcation of $layerm->slices is transfered onto
# the $layerm->fill_surfaces by clipping $layerm->fill_surfaces
# by the cummulative area of the previous $layerm->fill_surfaces.
$self->detect_surfaces_type;
# Mark the object to have the slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
# Mark the object to have the region slices classified (typed, which also means they are split based on whether they are supported, bridging, top layers etc.)
$self->set_typed_slices(1);
# Decide what surfaces are to be filled.
@ -615,6 +620,7 @@ sub infill {
);
### we could free memory now, but this would make this step not idempotent
### Vojtech: Cannot release the fill_surfaces, they are used by the support generator.
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
$self->set_step_done(STEP_INFILL);

View file

@ -34,7 +34,7 @@ use Slic3r::Test;
expolygon => $_,
)) for @$expolygons;
my ($region_config, $object_config, $print_config, $loops, $gap_fill, $perimeter_surfaces, $fill_surfaces);
my ($region_config, $object_config, $print_config, $loops, $gap_fill, $fill_surfaces);
my $g = Slic3r::Layer::PerimeterGenerator->new(
# input:
$slices,
@ -47,7 +47,6 @@ use Slic3r::Test;
# output:
($loops = Slic3r::ExtrusionPath::Collection->new),
($gap_fill = Slic3r::ExtrusionPath::Collection->new),
($perimeter_surfaces = Slic3r::Surface::Collection->new),
($fill_surfaces = Slic3r::Surface::Collection->new),
);
$g->config->apply_dynamic($config);

View file

@ -264,7 +264,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
if (rate < 40.f) {
printf("Extremely low flow rate: %f. Line %d, Length: %f, extrusion: %f Old position: (%f, %f, %f), new position: (%f, %f, %f)\n",
rate,
line_idx,
int(line_idx),
sqrt(len2), sqrt((diff[3]*diff[3])/len2),
m_current_pos[0], m_current_pos[1], m_current_pos[2],
new_pos[0], new_pos[1], new_pos[2]);

View file

@ -103,8 +103,7 @@ Layer::make_slices()
} else {
Polygons slices_p;
FOREACH_LAYERREGION(this, layerm) {
Polygons region_slices_p = (*layerm)->slices;
slices_p.insert(slices_p.end(), region_slices_p.begin(), region_slices_p.end());
polygons_append(slices_p, to_polygons((*layerm)->slices));
}
union_(slices_p, &slices);
}
@ -123,9 +122,8 @@ Layer::make_slices()
Slic3r::Geometry::chained_path(ordering_points, order);
// populate slices vector
for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it) {
this->slices.expolygons.push_back(slices[*it]);
}
for (std::vector<Points::size_type>::const_iterator it = order.begin(); it != order.end(); ++it)
this->slices.expolygons.push_back(STDMOVE(slices[*it]));
}
void
@ -200,68 +198,35 @@ Layer::make_perimeters()
if (layerms.size() == 1) { // optimization
(*layerm)->fill_surfaces.surfaces.clear();
(*layerm)->perimeter_surfaces.surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->perimeter_surfaces, &(*layerm)->fill_surfaces);
this->perimeter_expolygons.expolygons.clear();
for (Surfaces::const_iterator it = (*layerm)->perimeter_surfaces.surfaces.begin(); it != (*layerm)->perimeter_surfaces.surfaces.end(); ++ it)
this->perimeter_expolygons.expolygons.push_back(it->expolygon);
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces);
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
} else {
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) {
slices[s->extra_perimeters].push_back(*s);
}
}
// merge the surfaces assigned to each group
SurfaceCollection new_slices;
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) {
ExPolygons expp = union_ex(it->second, true);
for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = it->second.front(); // clone type and extra_perimeters
s.expolygon = *ex;
new_slices.surfaces.push_back(s);
{
// group slices (surfaces) according to number of extra perimeters
std::map<unsigned short,Surfaces> slices; // extra_perimeters => [ surface, surface... ]
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
for (Surfaces::iterator s = (*l)->slices.surfaces.begin(); s != (*l)->slices.surfaces.end(); ++s) {
slices[s->extra_perimeters].push_back(*s);
}
}
// merge the surfaces assigned to each group
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
}
// make perimeters
SurfaceCollection perimeter_surfaces;
SurfaceCollection fill_surfaces;
(*layerm)->make_perimeters(new_slices, &perimeter_surfaces, &fill_surfaces);
// Copy the perimeter surfaces to the layer's surfaces before splitting them into the regions.
this->perimeter_expolygons.expolygons.clear();
for (Surfaces::const_iterator it = perimeter_surfaces.surfaces.begin(); it != perimeter_surfaces.surfaces.end(); ++ it)
this->perimeter_expolygons.expolygons.push_back(it->expolygon);
(*layerm)->make_perimeters(new_slices, &fill_surfaces);
// assign fill_surfaces to each layer
if (!fill_surfaces.surfaces.empty()) {
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(
fill_surfaces,
(*l)->slices
);
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.surfaces.clear();
for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = fill_surfaces.surfaces.front(); // clone type and extra_perimeters
s.expolygon = *ex;
(*l)->fill_surfaces.surfaces.push_back(s);
}
// Separate the perimeter surfaces.
expp = intersection_ex(
perimeter_surfaces,
(*l)->slices
);
(*l)->perimeter_surfaces.surfaces.clear();
for (ExPolygons::iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = fill_surfaces.surfaces.front(); // clone type and extra_perimeters
s.expolygon = *ex;
(*l)->perimeter_surfaces.surfaces.push_back(s);
}
surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
}
}
}

View file

@ -36,8 +36,11 @@ class LayerRegion
// collection of extrusion paths/loops filling gaps
ExtrusionEntityCollection thin_fills;
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
// and for re-starting of infills.
ExPolygons fill_expolygons;
// collection of surfaces for infill generation
SurfaceCollection fill_surfaces;
SurfaceCollection fill_surfaces;
// Collection of perimeter surfaces. This is a cached result of diff(slices, fill_surfaces).
// While not necessary, the memory consumption is meager and it speeds up calculation.
@ -63,7 +66,7 @@ class LayerRegion
void merge_slices();
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces);
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
void process_external_surfaces(const Layer* lower_layer);
double infill_area_threshold() const;
@ -103,10 +106,9 @@ public:
// collection of expolygons generated by slicing the original geometry;
// also known as 'islands' (all regions and surface types are merged here)
// The slices are chained by the shortest traverse distance and this traversal
// order will be recovered by the G-code generator.
ExPolygonCollection slices;
// Surfaces of the perimeters including their gap fill.
ExPolygonCollection perimeter_expolygons;
size_t region_count() const;
const LayerRegion* get_region(int idx) const { return this->regions.at(idx); }

View file

@ -58,7 +58,8 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
// in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case
// that combine_infill() turns some fill_surface into VOID surfaces.
Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces));
// Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces));
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
this->fill_surfaces.surfaces.clear();
for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface)
surfaces_append(
@ -68,7 +69,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
void
LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces)
LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
{
this->perimeters.clear();
this->thin_fills.clear();
@ -85,7 +86,6 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
// output:
&this->perimeters,
&this->thin_fills,
perimeter_surfaces,
fill_surfaces
);
@ -367,15 +367,10 @@ LayerRegion::prepare_fill_surfaces()
// if no solid layers are requested, turn top/bottom surfaces to internal
if (this->region()->config.top_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
if (surface->surface_type == stTop) {
if (this->layer()->object()->config.infill_only_where_needed) {
surface->surface_type = stInternalVoid;
} else {
surface->surface_type = stInternal;
}
}
}
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
if (surface->surface_type == stTop)
surface->surface_type = (this->layer()->object()->config.infill_only_where_needed) ?
stInternalVoid : stInternal;
}
if (this->region()->config.bottom_solid_layers == 0) {
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {

View file

@ -246,15 +246,6 @@ PerimeterGenerator::process()
if (!entities.empty())
this->loops->append(entities);
} // for each loop of an island
{
//FIXME how about the gaps?
// Calculate the region of surface->expolygon covered by the perimeters and their gap fills.
// The perimeters will later be used to calculate the object skin.
ExPolygons expp = diff_ex((Polygons)surface->expolygon, last, true);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
this->perimeter_surfaces->surfaces.push_back(Surface(stPerimeter, *ex));
}
// fill gaps
if (!gaps.empty()) {
@ -322,15 +313,15 @@ PerimeterGenerator::process()
// collapse too narrow infill areas
coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
expp = offset2_ex(
pp,
-inset -min_perimeter_infill_spacing/2,
+min_perimeter_infill_spacing/2
);
// append infill areas to fill_surfaces
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
this->fill_surfaces->surfaces.push_back(Surface(stInternal, *ex)); // use a bogus surface type
surfaces_append(
this->fill_surfaces->surfaces,
offset2_ex(
pp,
-inset -min_perimeter_infill_spacing/2,
+min_perimeter_infill_spacing/2),
stInternal);
}
} // for each island
}

View file

@ -52,7 +52,6 @@ public:
// Outputs:
ExtrusionEntityCollection* loops;
ExtrusionEntityCollection* gap_fill;
SurfaceCollection* perimeter_surfaces;
SurfaceCollection* fill_surfaces;
PerimeterGenerator(
@ -68,15 +67,13 @@ public:
ExtrusionEntityCollection* loops,
// Gaps without the thin walls
ExtrusionEntityCollection* gap_fill,
// Perimeters including their gap fills
SurfaceCollection* perimeter_surfaces,
// Infills without the gap fills
SurfaceCollection* fill_surfaces)
: slices(slices), lower_slices(NULL), layer_height(layer_height),
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
overhang_flow(flow), solid_infill_flow(flow),
config(config), object_config(object_config), print_config(print_config),
loops(loops), gap_fill(gap_fill), perimeter_surfaces(perimeter_surfaces), fill_surfaces(fill_surfaces),
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
_ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1)
{};
void process();

View file

@ -313,7 +313,7 @@ PrintObject::has_support_material() const
}
// This function analyzes slices of a region (SurfaceCollection slices).
// Each slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface.
// Initially all slices are of type S_TYPE_INTERNAL.
// Slices are compared against the top / bottom slices and regions and classified to the following groups:
// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill.
@ -325,14 +325,12 @@ void PrintObject::detect_surfaces_type()
{
// Slic3r::debugf "Detecting solid surfaces...\n";
for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region);
layerm->slices_to_fill_surfaces_clipped();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_fill_surfaces_to_svg_debug("1_detect_surfaces_type-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
Layer *layer = this->layers[idx_layer];
@ -526,6 +524,7 @@ PrintObject::discover_vertical_shells()
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
Polygons shell;
Polygons holes;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
ExPolygons shell_ex;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
@ -561,16 +560,31 @@ PrintObject::discover_vertical_shells()
cache_top_regions[idx_layer % n_extra_top_layers].valid = false;
if (n_extra_bottom_layers > 0 && idx_layer > 0)
cache_bottom_regions[(idx_layer - 1) % n_extra_bottom_layers].valid = false;
bool hole_first = true;
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n)
if (n >= 0 && n < (int)this->layers.size()) {
Layer &neighbor_layer = *this->layers[n];
LayerRegion &neighbor_region = *neighbor_layer.get_region(int(idx_region));
polygons_append(shell, neighbor_layer.perimeter_expolygons.expolygons);
Polygons newholes;
for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region)
polygons_append(newholes, to_polygons(neighbor_layer.get_region(idx_region)->fill_expolygons));
if (hole_first) {
hole_first = false;
polygons_append(holes, STDMOVE(newholes));
}
else if (! holes.empty()) {
holes = intersection(holes, newholes);
}
size_t n_shell_old = shell.size();
if (n > int(idx_layer)) {
// Collect top surfaces.
DiscoverVerticalShellsCacheEntry &cache = cache_top_regions[n % n_extra_top_layers];
if (! cache.valid) {
cache.valid = true;
// neighbor_region.slices contain the source top regions,
// so one would think that they encompass the top fill_surfaces. But the fill_surfaces could have been
// expanded before, therefore they may protrude out of neighbor_region.slices's top surfaces.
//FIXME one should probably use the cummulative top surfaces over all regions here.
cache.slices = offset(to_expolygons(neighbor_region.slices.filter_by_type(stTop)), min_perimeter_infill_spacing);
cache.fill_surfaces = offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing);
}
@ -582,6 +596,7 @@ PrintObject::discover_vertical_shells()
DiscoverVerticalShellsCacheEntry &cache = cache_bottom_regions[n % n_extra_bottom_layers];
if (! cache.valid) {
cache.valid = true;
//FIXME one should probably use the cummulative top surfaces over all regions here.
cache.slices = offset(to_expolygons(neighbor_region.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing);
cache.fill_surfaces = offset(to_expolygons(neighbor_region.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing);
}
@ -590,7 +605,8 @@ PrintObject::discover_vertical_shells()
}
// Running the union_ using the Clipper library piece by piece is cheaper
// than running the union_ all at once.
shell = union_(shell, false);
if (n_shell_old < shell.size())
shell = union_(shell, false);
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
@ -613,8 +629,8 @@ PrintObject::discover_vertical_shells()
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
}
if (shell.empty())
continue;
//if (shell.empty())
// continue;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
@ -660,6 +676,7 @@ PrintObject::discover_vertical_shells()
const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid };
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2));
shell = intersection(shell, polygonsInternal, true);
polygons_append(shell, diff(polygonsInternal, holes));
if (shell.empty())
continue;

View file

@ -97,6 +97,15 @@ inline Polygons to_polygons(const SurfacesPtr &src)
return polygons;
}
inline ExPolygons to_expolygons(const Surfaces &src)
{
ExPolygons expolygons;
expolygons.reserve(src.size());
for (Surfaces::const_iterator it = src.begin(); it != src.end(); ++it)
expolygons.push_back(it->expolygon);
return expolygons;
}
inline ExPolygons to_expolygons(const SurfacesPtr &src)
{
ExPolygons expolygons;

View file

@ -32,8 +32,8 @@
%code%{ RETVAL = THIS->flow(role, bridge, width); %};
void merge_slices();
void prepare_fill_surfaces();
void make_perimeters(SurfaceCollection* slices, SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces)
%code%{ THIS->make_perimeters(*slices, perimeter_surfaces, fill_surfaces); %};
void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces)
%code%{ THIS->make_perimeters(*slices, fill_surfaces); %};
double infill_area_threshold();
void export_region_slices_to_svg(const char *path);
@ -80,9 +80,6 @@
Ref<ExPolygonCollection> slices()
%code%{ RETVAL = &THIS->slices; %};
Ref<ExPolygonCollection> perimeter_expolygons()
%code%{ RETVAL = &THIS->perimeter_expolygons; %};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};

View file

@ -10,12 +10,12 @@
StaticPrintConfig* region_config, StaticPrintConfig* object_config,
StaticPrintConfig* print_config, ExtrusionEntityCollection* loops,
ExtrusionEntityCollection* gap_fill,
SurfaceCollection* perimeter_surfaces, SurfaceCollection* fill_surfaces)
SurfaceCollection* fill_surfaces)
%code{% RETVAL = new PerimeterGenerator(slices, layer_height, *flow,
dynamic_cast<PrintRegionConfig*>(region_config),
dynamic_cast<PrintObjectConfig*>(object_config),
dynamic_cast<PrintConfig*>(print_config),
loops, gap_fill, perimeter_surfaces, fill_surfaces); %};
loops, gap_fill, fill_surfaces); %};
~PerimeterGenerator();
void set_lower_slices(ExPolygonCollection* lower_slices)