Skip to content

Commit

Permalink
Ported PrintObject::combine_infill() to C++
Browse files Browse the repository at this point in the history
  • Loading branch information
alranel committed Nov 26, 2018
1 parent 450042a commit 2bbb089
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 178 deletions.
131 changes: 0 additions & 131 deletions lib/Slic3r/Print/Object.pm
Original file line number Diff line number Diff line change
Expand Up @@ -252,135 +252,4 @@ sub _support_material {
);
}

# combine fill surfaces across layers
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
sub combine_infill {
my $self = shift;

# define the type used for voids
my %voidtype = (
&S_TYPE_INTERNAL() => S_TYPE_INTERNALVOID,
);

# work on each region separately
for my $region_id (0 .. ($self->print->region_count-1)) {
my $region = $self->print->get_region($region_id);
my $every = $region->config->infill_every_layers;
next unless $every > 1 && $region->config->fill_density > 0;

# limit the number of combined layers to the maximum height allowed by this regions' nozzle
my $nozzle_diameter = min(
$self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1),
$self->print->config->get_at('nozzle_diameter', $region->config->solid_infill_extruder-1),
);

# define the combinations
my %combine = (); # layer_idx => number of additional combined lower layers
{
my $current_height = my $layers = 0;
for my $layer_idx (0 .. ($self->layer_count-1)) {
my $layer = $self->get_layer($layer_idx);
next if $layer->id == 0; # skip first print layer (which may not be first layer in array because of raft)
my $height = $layer->height;

# check whether the combination of this layer with the lower layers' buffer
# would exceed max layer height or max combined layer count
if ($current_height + $height >= $nozzle_diameter + epsilon || $layers >= $every) {
# append combination to lower layer
$combine{$layer_idx-1} = $layers;
$current_height = $layers = 0;
}

$current_height += $height;
$layers++;
}

# append lower layers (if any) to uppermost layer
$combine{$self->layer_count-1} = $layers;
}

# loop through layers to which we have assigned layers to combine
for my $layer_idx (sort keys %combine) {
next unless $combine{$layer_idx} > 1;

# get all the LayerRegion objects to be combined
my @layerms = map $self->get_layer($_)->get_region($region_id),
($layer_idx - ($combine{$layer_idx}-1) .. $layer_idx);

# only combine internal infill
for my $type (S_TYPE_INTERNAL) {
# we need to perform a multi-layer intersection, so let's split it in pairs

# initialize the intersection with the candidates of the lowest layer
my $intersection = [ map $_->expolygon, @{$layerms[0]->fill_surfaces->filter_by_type($type)} ];

# start looping from the second layer and intersect the current intersection with it
for my $layerm (@layerms[1 .. $#layerms]) {
$intersection = intersection_ex(
[ map @$_, @$intersection ],
[ map @{$_->expolygon}, @{$layerm->fill_surfaces->filter_by_type($type)} ],
);
}

my $area_threshold = $layerms[0]->infill_area_threshold;
@$intersection = grep $_->area > $area_threshold, @$intersection;
next if !@$intersection;
Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
scalar(@$intersection),
($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'),
$layer_idx-($every-1), $layer_idx;

# $intersection now contains the regions that can be combined across the full amount of layers
# so let's remove those areas from all layers

my @intersection_with_clearance = map @{$_->offset(
$layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width / 2
+ $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2
# Because fill areas for rectilinear and honeycomb are grown
# later to overlap perimeters, we need to counteract that too.
+ (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|grid|line|honeycomb)/)
? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width
: 0)
)}, @$intersection;


foreach my $layerm (@layerms) {
my @this_type = @{$layerm->fill_surfaces->filter_by_type($type)};
my @other_types = map $_->clone, grep $_->surface_type != $type, @{$layerm->fill_surfaces};

my @new_this_type = map Slic3r::Surface->new(expolygon => $_, surface_type => $type),
@{diff_ex(
[ map $_->p, @this_type ],
[ @intersection_with_clearance ],
)};

# apply surfaces back with adjusted depth to the uppermost layer
if ($layerm->layer->id == $self->get_layer($layer_idx)->id) {
push @new_this_type,
map Slic3r::Surface->new(
expolygon => $_,
surface_type => $type,
thickness => sum(map $_->layer->height, @layerms),
thickness_layers => scalar(@layerms),
),
@$intersection;
} else {
# save void surfaces
push @new_this_type,
map Slic3r::Surface->new(expolygon => $_, surface_type => $voidtype{$type}),
@{intersection_ex(
[ map @{$_->expolygon}, @this_type ],
[ @intersection_with_clearance ],
)};
}

$layerm->fill_surfaces->clear;
$layerm->fill_surfaces->append($_) for (@new_this_type, @other_types);
}
}
}
}
}

1;
1 change: 1 addition & 0 deletions xs/src/libslic3r/Geometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Pointf circle_taubin_newton(const Pointfs& input, size_t cycles = 20);
Pointf circle_taubin_newton(const Pointfs::const_iterator& input_start, const Pointfs::const_iterator& input_end, size_t cycles = 20);

/// Epsilon value
// FIXME: this is a duplicate from libslic3r.h
constexpr double epsilon { 1e-4 };
constexpr coord_t scaled_epsilon { static_cast<coord_t>(epsilon / SCALING_FACTOR) };

Expand Down
118 changes: 71 additions & 47 deletions xs/src/libslic3r/PrintObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,107 +1175,133 @@ PrintObject::prepare_infill()
}


// combine fill surfaces across layers
// Idempotence of this method is guaranteed by the fact that we don't remove things from
// fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
void
PrintObject::combine_infill()
{
// Work on each region separately.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions[region_id];
const int every = region->config.infill_every_layers.value;
const int every = region->config.infill_every_layers();
if (every < 2 || region->config.fill_density == 0.)
continue;

// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height
double nozzle_diameter = std::min(
this->print()->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1),
this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1));
// FIXME: limit the layer height to max_layer_height
const double nozzle_diameter = std::min(
this->_print->config.nozzle_diameter.get_at(region->config.infill_extruder.value - 1),
this->_print->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)
);

// define the combinations
std::vector<size_t> combine(this->layers.size(), 0);
std::vector<size_t> combine(this->layers.size(), 0); // layer_idx => number of additional combined lower layers
{
double current_height = 0.;
size_t num_layers = 0;
for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) {
for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++layer_idx) {
const Layer *layer = this->layers[layer_idx];

// Skip first print layer (which may not be first layer in array because of raft).
if (layer->id() == 0)
// Skip first print layer (which may not be first layer in array because of raft).
continue;

// Check whether the combination of this layer with the lower layers' buffer
// would exceed max layer height or max combined layer count.
if (current_height + layer->height >= nozzle_diameter + EPSILON || (every < 0 || num_layers >= static_cast<size_t>(every)) ) {
if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= static_cast<size_t>(every) ) {
// Append combination to lower layer.
combine[layer_idx - 1] = num_layers;
current_height = 0.;
num_layers = 0;
}
current_height += layer->height;
++ num_layers;
++num_layers;
}

// Append lower layers (if any) to uppermost layer.
combine[this->layers.size() - 1] = num_layers;
}

// loop through layers to which we have assigned layers to combine
for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) {
size_t num_layers = combine[layer_idx];
for (size_t layer_idx = 0; layer_idx < combine.size(); ++layer_idx) {
const size_t& num_layers = combine[layer_idx];
if (num_layers <= 1)
continue;

// Get all the LayerRegion objects to be combined.
std::vector<LayerRegion*> layerms;
layerms.reserve(num_layers);
for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i)
layerms.emplace_back(this->layers[i]->regions[region_id]);
for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++i)
layerms.push_back(this->layers[i]->regions[region_id]);

// We need to perform a multi-layer intersection, so let's split it in pairs.

// Initialize the intersection with the candidates of the lowest layer.
ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal));

// Start looping from the second layer and intersect the current intersection with it.
for (size_t i = 1; i < layerms.size(); ++ i)
for (size_t i = 1; i < layerms.size(); ++i)
intersection = intersection_ex(
to_polygons(intersection),
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)),
false);
double area_threshold = layerms.front()->infill_area_threshold();
if (! intersection.empty() && area_threshold > 0.)
intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
[area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
intersection.end());
to_polygons(intersection),
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal))
);

// Remove ExPolygons whose area is <= infill_area_threshold()
const double area_threshold = layerms.front()->infill_area_threshold();
intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
[area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
intersection.end());

if (intersection.empty())
continue;
// Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
// scalar(@$intersection),
// ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'),
// $layer_idx-($every-1), $layer_idx;

#ifdef SLIC3R_DEBUG
std::cout << " combining " << intersection.size()
<< " internal regions from layers " << (layer_idx-(every-1))
<< "-" << layer_idx << std::endl;
#endif

// intersection now contains the regions that can be combined across the full amount of layers,
// so let's remove those areas from all layers.

const float clearance_offset =
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
// Because fill areas for rectilinear and honeycomb are grown
// later to overlap perimeters, we need to counteract that too.
((region->config.fill_pattern == ipRectilinear ||
region->config.fill_pattern == ipGrid ||
region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f)
* layerms.back()->flow(frSolidInfill).scaled_width();

Polygons intersection_with_clearance;
intersection_with_clearance.reserve(intersection.size());
float clearance_offset =
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
// Because fill areas for rectilinear and honeycomb are grown
// later to overlap perimeters, we need to counteract that too.
((region->config.fill_pattern == ipRectilinear ||
region->config.fill_pattern == ipGrid ||
region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
layerms.back()->flow(frSolidInfill).scaled_width();
for (ExPolygon &expoly : intersection)
for (const ExPolygon &expoly : intersection)
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));

for (LayerRegion *layerm : layerms) {
Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal));
const Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal));
layerm->fill_surfaces.remove_type(stInternal);
layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance, false), stInternal);

layerm->fill_surfaces.append(
diff_ex(internal, intersection_with_clearance),
stInternal
);

if (layerm == layerms.back()) {
// Apply surfaces back with adjusted depth to the uppermost layer.
Surface templ(stInternal, ExPolygon());
templ.thickness = 0.;
for (LayerRegion *layerm2 : layerms)
for (const LayerRegion *layerm2 : layerms)
templ.thickness += layerm2->layer()->height;
templ.thickness_layers = (unsigned short)layerms.size();
layerm->fill_surfaces.append(intersection, templ);
} else {
// Save void surfaces.
layerm->fill_surfaces.append(
intersection_ex(internal, intersection_with_clearance, false),
stInternalVoid);
intersection_ex(internal, intersection_with_clearance),
stInternalVoid
);
}
}
}
Expand Down Expand Up @@ -1624,8 +1650,9 @@ PrintObject::clip_fill_surfaces()
// get our current internal fill boundaries
Polygons lower_layer_internal_surfaces;
for (const auto* layerm : lower_layer->regions)
for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid }))
polygons_append(lower_layer_internal_surfaces, *s);
polygons_append(lower_layer_internal_surfaces, to_polygons(
layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })
));
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
}

Expand All @@ -1634,10 +1661,7 @@ PrintObject::clip_fill_surfaces()
if (layerm->region()->config.fill_density.value == 0)
continue;

Polygons internal;
for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid }))
polygons_append(internal, *s);

Polygons internal{ to_polygons(layerm->fill_surfaces.filter_by_type({ stInternal, stInternalVoid })) };
layerm->fill_surfaces.remove_types({ stInternal, stInternalVoid });
layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal);
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternalVoid);
Expand Down
1 change: 1 addition & 0 deletions xs/xsp/Print.xsp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ _constant()
%name{_detect_surfaces_type} void detect_surfaces_type();
void process_external_surfaces();
void bridge_over_infill();
void combine_infill();
void discover_horizontal_shells();
void clip_fill_surfaces();
void _slice();
Expand Down

0 comments on commit 2bbb089

Please sign in to comment.