Merge branch 'master' into alexrj_flagssurfacetype

This commit is contained in:
Merill 2018-11-27 14:25:16 +01:00 committed by GitHub
commit 74b02336ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 243 additions and 602 deletions

View File

@ -255,134 +255,6 @@ EOF
print "Done.\n" unless $params{quiet};
}
sub make_skirt {
my $self = shift;
# prerequisites
$_->make_perimeters for @{$self->objects};
$_->infill for @{$self->objects};
$_->generate_support_material for @{$self->objects};
return if $self->step_done(STEP_SKIRT);
$self->set_step_started(STEP_SKIRT);
# since this method must be idempotent, we clear skirt paths *before*
# checking whether we need to generate them
$self->skirt->clear;
if (!$self->has_skirt) {
$self->set_step_done(STEP_SKIRT);
return;
}
$self->status_cb->(88, "Generating skirt");
# First off we need to decide how tall the skirt must be.
# The skirt_height option from config is expressed in layers, but our
# object might have different layer heights, so we need to find the print_z
# of the highest layer involved.
# Note that unless has_infinite_skirt() == true
# the actual skirt might not reach this $skirt_height_z value since the print
# order of objects on each layer is not guaranteed and will not generally
# include the thickest object first. It is just guaranteed that a skirt is
# prepended to the first 'n' layers (with 'n' = skirt_height).
# $skirt_height_z in this case is the highest possible skirt height for safety.
my $skirt_height_z = -1;
foreach my $object (@{$self->objects}) {
my $skirt_height = $self->has_infinite_skirt
? $object->layer_count
: min($self->config->skirt_height, $object->layer_count);
my $highest_layer = $object->get_layer($skirt_height - 1);
$skirt_height_z = max($skirt_height_z, $highest_layer->print_z);
}
# collect points from all layers contained in skirt height
my @points = ();
foreach my $object (@{$self->objects}) {
my @object_points = ();
# get object layers up to $skirt_height_z
foreach my $layer (@{$object->layers}) {
last if $layer->print_z > $skirt_height_z;
push @object_points, map @$_, map @$_, @{$layer->slices};
}
# get support layers up to $skirt_height_z
foreach my $layer (@{$object->support_layers}) {
last if $layer->print_z > $skirt_height_z;
push @object_points, map @{$_->polyline}, @{$layer->support_fills} if $layer->support_fills;
push @object_points, map @{$_->polyline}, @{$layer->support_interface_fills} if $layer->support_interface_fills;
}
# repeat points for each object copy
foreach my $copy (@{$object->_shifted_copies}) {
my @copy_points = map $_->clone, @object_points;
$_->translate(@$copy) for @copy_points;
push @points, @copy_points;
}
}
return if @points < 3; # at least three points required for a convex hull
# find out convex hull
my $convex_hull = convex_hull(\@points);
my @extruded_length = (); # for each extruder
# skirt may be printed on several layers, having distinct layer heights,
# but loops must be aligned so can't vary width/spacing
# TODO: use each extruder's own flow
my $first_layer_height = $self->skirt_first_layer_height;
my $flow = $self->skirt_flow;
my $spacing = $flow->spacing;
my $mm3_per_mm = $flow->mm3_per_mm;
my @extruders_e_per_mm = ();
my $extruder_idx = 0;
my $skirts = $self->config->skirts;
$skirts ||= 1 if $self->has_infinite_skirt;
# draw outlines from outside to inside
# loop while we have less skirts than required or any extruder hasn't reached the min length if any
my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
for (my $i = $skirts; $i > 0; $i--) {
$distance += scale $spacing;
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
my $eloop = Slic3r::ExtrusionLoop->new_from_paths(
Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point,
role => EXTR_ROLE_SKIRT,
mm3_per_mm => $mm3_per_mm, # this will be overridden at G-code export time
width => $flow->width,
height => $first_layer_height, # this will be overridden at G-code export time
),
);
$eloop->role(EXTRL_ROLE_SKIRT);
$self->skirt->append($eloop);
if ($self->config->min_skirt_length > 0) {
$extruded_length[$extruder_idx] ||= 0;
if (!$extruders_e_per_mm[$extruder_idx]) {
my $config = Slic3r::Config::GCode->new;
$config->apply_static($self->config);
my $extruder = Slic3r::Extruder->new($extruder_idx, $config);
$extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm);
}
$extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
$i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders};
if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) {
if ($extruder_idx < $#{$self->extruders}) {
$extruder_idx++;
next;
}
}
}
}
$self->skirt->reverse;
$self->set_step_done(STEP_SKIRT);
}
sub make_brim {
my $self = shift;

View File

@ -34,175 +34,6 @@ sub support_layers {
return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ];
}
# 1) Decides Z positions of the layers,
# 2) Initializes layers and their regions
# 3) Slices the object meshes
# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
# 5) Applies size compensation (offsets the slices in XY plane)
# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
# Resulting expolygons of layer regions are marked as Internal.
#
# this should be idempotent
sub slice {
my $self = shift;
return if $self->step_done(STEP_SLICE);
$self->set_step_started(STEP_SLICE);
$self->print->status_cb->(10, "Processing triangulated mesh");
$self->_slice;
# detect slicing errors
my $warning_thrown = 0;
for my $i (0 .. ($self->layer_count - 1)) {
my $layer = $self->get_layer($i);
next unless $layer->slicing_errors;
if (!$warning_thrown) {
warn "The model has overlapping or self-intersecting facets. I tried to repair it, "
. "however you might want to check the results or repair the input file and retry.\n";
$warning_thrown = 1;
}
# try to repair the layer surfaces by merging all contours and all holes from
# neighbor layers
Slic3r::debugf "Attempting to repair layer %d\n", $i;
foreach my $region_id (0 .. ($layer->region_count - 1)) {
my $layerm = $layer->region($region_id);
my (@upper_surfaces, @lower_surfaces);
for (my $j = $i+1; $j < $self->layer_count; $j++) {
if (!$self->get_layer($j)->slicing_errors) {
@upper_surfaces = @{$self->get_layer($j)->region($region_id)->slices};
last;
}
}
for (my $j = $i-1; $j >= 0; $j--) {
if (!$self->get_layer($j)->slicing_errors) {
@lower_surfaces = @{$self->get_layer($j)->region($region_id)->slices};
last;
}
}
my $union = union_ex([
map $_->expolygon->contour, @upper_surfaces, @lower_surfaces,
]);
my $diff = diff_ex(
[ map @$_, @$union ],
[ map @{$_->expolygon->holes}, @upper_surfaces, @lower_surfaces, ],
);
$layerm->slices->clear;
$layerm->slices->append($_)
for map Slic3r::Surface->new
(expolygon => $_, surface_type => S_TYPE_INTERNAL),
@$diff;
}
# update layer slices after repairing the single regions
$layer->make_slices;
}
# remove empty layers from bottom
while (@{$self->layers} && !@{$self->get_layer(0)->slices}) {
$self->delete_layer(0);
for (my $i = 0; $i <= $#{$self->layers}; $i++) {
$self->get_layer($i)->set_id( $self->get_layer($i)->id-1 );
}
}
# simplify slices if required
if ($self->print->config->resolution) {
$self->_simplify_slices(scale($self->print->config->resolution));
}
die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"
if !@{$self->layers};
$self->set_typed_slices(0);
$self->set_step_done(STEP_SLICE);
}
sub make_perimeters {
my ($self) = @_;
return if $self->step_done(STEP_PERIMETERS);
# Temporary workaround for detect_surfaces_type() not being idempotent (see #3764).
# We can remove this when idempotence is restored. This make_perimeters() method
# will just call merge_slices() to undo the typed slices and invalidate posDetectSurfaces.
if ($self->typed_slices) {
$self->invalidate_step(STEP_SLICE);
}
# prerequisites
$self->slice;
$self->_make_perimeters;
}
# This will assign a type (top/bottom/internal) to $layerm->slices
# and transform $layerm->fill_surfaces from expolygon
# to typed top/bottom/internal surfaces;
sub detect_surfaces_type {
my ($self) = @_;
# prerequisites
$self->slice;
$self->_detect_surfaces_type;
}
sub prepare_infill {
my ($self) = @_;
return if $self->step_done(STEP_PREPARE_INFILL);
# This prepare_infill() is not really idempotent.
# TODO: It should clear and regenerate fill_surfaces at every run
# instead of modifying it in place.
$self->invalidate_step(STEP_PERIMETERS);
$self->make_perimeters;
# Do this after invalidating STEP_PERIMETERS because that would re-invalidate STEP_PREPARE_INFILL
$self->set_step_started(STEP_PREPARE_INFILL);
# prerequisites
$self->detect_surfaces_type;
$self->print->status_cb->(30, "Preparing infill");
# decide what surfaces are to be filled
$_->prepare_fill_surfaces for map @{$_->regions}, @{$self->layers};
# this will detect bridges and reverse bridges
# and rearrange top/bottom/internal surfaces
$self->process_external_surfaces;
# detect which fill surfaces are near external layers
# they will be split in internal and internal-solid surfaces
$self->discover_horizontal_shells;
$self->clip_fill_surfaces;
# the following step needs to be done before combination because it may need
# to remove only half of the combined infill
$self->bridge_over_infill;
# combine fill surfaces to honor the "infill every N layers" option
$self->combine_infill;
$self->set_step_done(STEP_PREPARE_INFILL);
}
sub infill {
my ($self) = @_;
# prerequisites
$self->prepare_infill;
$self->_infill;
}
sub generate_support_material {
my $self = shift;
@ -252,135 +83,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;

View File

@ -282,7 +282,7 @@ bool test_6_checks(Print &print)
// Pre-Processing.
PrintObject *print_object = print.objects.front();
print_object->_infill();
print_object->infill();
SupportMaterial *support_material = print.objects.front()->_support_material();
support_material->generate(print_object);
// TODO but not needed in test 6 (make brims and make skirts).

View File

@ -41,9 +41,12 @@ class ExPolygonCollection
/// ExPolygons and check if at least one contains the point.
bool contains(const Point &point) const;
bool empty() const { return expolygons.empty(); }
size_t size() const { return expolygons.size(); }
ExPolygons::iterator begin() { return expolygons.begin(); }
ExPolygons::iterator end() { return expolygons.end(); }
const ExPolygons::const_iterator begin() const { return expolygons.cbegin(); }
const ExPolygons::const_iterator end() const { return expolygons.cend(); }
ExPolygons::const_iterator cbegin() const { return expolygons.cbegin();}
ExPolygons::const_iterator cend() const { return expolygons.cend();}
ExPolygon& at(size_t i) { return expolygons.at(i); }

View File

@ -74,6 +74,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity
ExtrusionEntitiesPtr::iterator begin() { return entities.begin(); }
ExtrusionEntitiesPtr::iterator end() { return entities.end(); }
ExtrusionEntitiesPtr::const_iterator begin() const { return entities.cbegin(); }
ExtrusionEntitiesPtr::const_iterator end() const { return entities.cend(); }
ExtrusionEntitiesPtr::const_iterator cbegin() const { return entities.cbegin(); }
ExtrusionEntitiesPtr::const_iterator cend() const { return entities.cend(); }

View File

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

View File

@ -140,16 +140,16 @@ Print::make_brim()
void
Print::make_skirt()
{
if (this->state.is_done(psSkirt)) return;
this->state.set_started(psSkirt);
// prereqs
for(auto& obj: this->objects) {
for (auto* obj: this->objects) {
obj->make_perimeters();
obj->infill();
obj->generate_support_material();
}
if (this->state.is_done(psSkirt)) return;
this->state.set_started(psSkirt);
// since this method must be idempotent, we clear skirt paths *before*
// checking whether we need to generate them
this->skirt.clear();
@ -173,10 +173,11 @@ Print::make_skirt()
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
double skirt_height_z {-1.0};
for (const auto& object : this->objects) {
for (const auto* object : this->objects) {
const size_t skirt_height {
this->has_infinite_skirt() ? object->layer_count() :
std::min(size_t(this->config.skirt_height()), object->layer_count())
this->has_infinite_skirt()
? object->layer_count()
: std::min(size_t(this->config.skirt_height()), object->layer_count())
};
const Layer* highest_layer { object->get_layer(skirt_height - 1) };
skirt_height_z = std::max(skirt_height_z, highest_layer->print_z);
@ -184,116 +185,83 @@ Print::make_skirt()
// collect points from all layers contained in skirt height
Points points;
for(auto* object : this->objects) {
for (auto* object : this->objects) {
Points object_points;
// get object layers up to skirt_height_z
for(auto* layer : object->layers) {
if(layer->print_z > skirt_height_z)break;
for(ExPolygon poly : layer->slices){
for(Point point : static_cast<Points>(poly)){
object_points.push_back(point);
}
}
for (const auto* layer : object->layers) {
if (layer->print_z > skirt_height_z) break;
for (const ExPolygon ex : layer->slices)
append_to(object_points, static_cast<Points>(ex));
}
// get support layers up to $skirt_height_z
for(auto* layer : object->support_layers) {
if(layer->print_z > skirt_height_z)break;
for(auto* ee : layer->support_fills){
for(Point point : ee->as_polyline().points){
object_points.push_back(point);
}
}
for(auto* ee : layer->support_interface_fills){
for(Point point : ee->as_polyline().points){
object_points.push_back(point);
}
}
// get support layers up to skirt_height_z
for (const auto* layer : object->support_layers) {
if (layer->print_z > skirt_height_z) break;
for (auto* ee : layer->support_fills)
append_to(object_points, ee->as_polyline().points);
for (auto* ee : layer->support_interface_fills)
append_to(object_points, ee->as_polyline().points);
}
// repeat points for each object copy
for(auto copy : object->_shifted_copies) {
for(Point point : object_points){
point.translate(copy);
points.push_back(point);
for (const auto& copy : object->_shifted_copies) {
for (Point p : object_points) {
p.translate(copy);
points.push_back(p);
}
}
}
if (points.size() < 3) return; // at least three points required for a convex hull
// find out convex hull
auto convex = Geometry::convex_hull(points);
const Polygon convex = Geometry::convex_hull(points);
// skirt may be printed on several layers, having distinct layer heights,
// but loops must be aligned so can't vary width/spacing
// TODO: use each extruder's own flow
auto first_layer_height = this->skirt_first_layer_height();
auto flow = this->skirt_flow();
auto spacing = flow.spacing();
auto mm3_per_mm = flow.mm3_per_mm();
const auto first_layer_height = this->skirt_first_layer_height();
const auto flow = this->skirt_flow();
const auto spacing = flow.scaled_spacing();
const auto mm3_per_mm = flow.mm3_per_mm();
int skirts = this->config.skirts();
if (skirts == 0 && this->has_infinite_skirt())
skirts = 1;
auto skirts = this->config.skirts;
if(this->has_infinite_skirt() && skirts == 0){
skirts = 1;
}
//my @extruded_length = (); # for each extruder
//extruders_e_per_mm = ();
//size_t extruder_idx = 0;
// new to the cpp implementation
float e_per_mm {0.0}, extruded_length = 0;
size_t extruders_warm = 0;
if (this->config.min_skirt_length.getFloat() > 0) {
//my $config = Config::GCode();
//$config->apply_static($self->config);
auto extruder = Extruder(0, &this->config);
e_per_mm = extruder.e_per_mm(mm3_per_mm);
}
const std::set<size_t> extruders{ this->extruders() };
auto extruder_it { extruders.begin() };
std::vector<float> e_per_mm{0}, extruded_length{0};
if (this->config.min_skirt_length() > 0)
for (auto i : extruders)
e_per_mm[i] = Extruder(i, &this->config).e_per_mm(mm3_per_mm);
// draw outlines from outside to inside
// loop while we have less skirts than required or any extruder hasn't reached the min length if any
float distance = scale_(std::max(this->config.skirt_distance.getFloat(), this->config.brim_width.getFloat()));
float distance = scale_(std::max(this->config.skirt_distance(), this->config.brim_width()));
for (int i = skirts; i > 0; i--) {
distance += scale_(spacing);
auto loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0);
auto epath = ExtrusionPath(erSkirt,
mm3_per_mm, // this will be overridden at G-code export time
flow.width,
first_layer_height // this will be overridden at G-code export time
distance += spacing;
const Polygon loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0);
auto epath = ExtrusionPath(
erSkirt,
mm3_per_mm, // this will be overridden at G-code export time
flow.width,
first_layer_height // this will be overridden at G-code export time
);
epath.polyline = loop.split_at_first_point();
auto eloop = ExtrusionLoop(epath,elrSkirt);
auto eloop = ExtrusionLoop(epath, elrSkirt);
this->skirt.append(eloop);
if (this->config.min_skirt_length.getFloat() > 0) {
// Alternative simpler method
extruded_length += unscale(loop.length()) * e_per_mm;
if(extruded_length >= this->config.min_skirt_length.getFloat()){
extruders_warm++;
extruded_length = 0;
}
if (extruders_warm < this->extruders().size()){
i++;
}
/*$extruded_length[$extruder_idx] ||= 0;
if (!$extruders_e_per_mm[$extruder_idx]) {
my $config = Slic3r::Config::GCode->new;
$config->apply_static($self->config);
my $extruder = Slic3r::Extruder->new($extruder_idx, $config);
$extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm);
}
$extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx];
$i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders};
if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) {
if ($extruder_idx < $#{$self->extruders}) {
$extruder_idx++;
next;
if (this->config.min_skirt_length() > 0) {
extruded_length[*extruder_it] += unscale(loop.length()) * e_per_mm[*extruder_it];
for (auto j : extruders) {
if (extruded_length[j] < this->config.min_skirt_length()) {
++i;
break;
}
}*/
}
if (extruded_length[*extruder_it] >= this->config.min_skirt_length() && extruder_it != extruders.end())
++extruder_it;
}
}

View File

@ -164,7 +164,6 @@ class PrintObject
void _slice();
std::vector<ExPolygons> _slice_region(size_t region_id, std::vector<float> z, bool modifier);
void _make_perimeters();
void _infill();
/// Initialize and generate support material.

View File

@ -329,15 +329,18 @@ PrintObject::has_support_material() const
|| this->config.support_material_enforce_layers > 0;
}
// This will assign a type (top/bottom/internal) to layerm->slices
// and transform layerm->fill_surfaces from expolygon
// to typed top/bottom/internal surfaces;
void
PrintObject::detect_surfaces_type()
{
// prerequisites
// this->slice();
if (this->state.is_done(posDetectSurfaces)) return;
this->state.set_started(posDetectSurfaces);
// prerequisites
this->slice();
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::detect_surfaces_type, _1),
@ -946,16 +949,17 @@ PrintObject::_slice_region(size_t region_id, std::vector<float> z, bool modifier
return layers;
}
void
PrintObject::make_perimeters()
{
if (this->state.is_done(posPerimeters)) return;
if (this->typed_slices)
this->state.invalidate(posSlice);
this->slice(); // take care of prereqs
this->_make_perimeters();
}
/*
1) Decides Z positions of the layers,
2) Initializes layers and their regions
3) Slices the object meshes
4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
5) Applies size compensation (offsets the slices in XY plane)
6) Replaces bad slices by the slices reconstructed from the upper/lower layer
Resulting expolygons of layer regions are marked as Internal.
This should be idempotent.
*/
void
PrintObject::slice()
{
@ -964,8 +968,7 @@ PrintObject::slice()
if (_print->status_cb != nullptr) {
_print->status_cb(10, "Processing triangulated mesh");
}
this->_slice();
// detect slicing errors
@ -975,22 +978,94 @@ PrintObject::slice()
<< "I tried to repair it, however you might want to check "
<< "the results or repair the input file and retry.\n";
if (this->layers.size() == 0) {
bool warning_thrown = false;
for (size_t i = 0; i < this->layer_count(); ++i) {
Layer* layer{ this->get_layer(i) };
if (!layer->slicing_errors) continue;
if (!warning_thrown) {
Slic3r::Log::warn("PrintObject") << "The model has overlapping or self-intersecting facets. "
<< "I tried to repair it, however you might want to check "
<< "the results or repair the input file and retry.\n";
warning_thrown = true;
}
// try to repair the layer surfaces by merging all contours and all holes from
// neighbor layers
#ifdef SLIC3R_DEBUG
std::cout << "Attempting to repair layer " << i << std::endl;
#endif
for (size_t region_id = 0; region_id < layer->region_count(); ++region_id) {
LayerRegion* layerm{ layer->get_region(region_id) };
ExPolygons slices;
for (size_t j = i+1; j < this->layer_count(); ++j) {
const Layer* upper = this->get_layer(j);
if (!upper->slicing_errors) {
append_to(slices, (ExPolygons)upper->get_region(region_id)->slices);
break;
}
}
for (int j = i-1; j >= 0; --j) {
const Layer* lower = this->get_layer(j);
if (!lower->slicing_errors) {
append_to(slices, (ExPolygons)lower->get_region(region_id)->slices);
break;
}
}
// TODO: do we actually need to split contours and holes before performing the diff?
Polygons contours, holes;
for (ExPolygon ex : slices)
contours.push_back(ex.contour);
for (ExPolygon ex : slices)
append_to(holes, ex.holes);
const ExPolygons diff = diff_ex(contours, holes);
layerm->slices.clear();
layerm->slices.append(diff, stInternal);
}
// update layer slices after repairing the single regions
layer->make_slices();
}
// remove empty layers from bottom
while (!this->layers.empty() && this->get_layer(0)->slices.empty()) {
this->delete_layer(0);
for (Layer* layer : this->layers)
layer->set_id(layer->id()-1);
}
// simplify slices if required
if (this->_print->config.resolution() > 0)
this->_simplify_slices(scale_(this->_print->config.resolution()));
if (this->layers.empty()) {
Slic3r::Log::error("PrintObject") << "slice(): " << "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n";
return; // make this throw an exception instead?
}
this->typed_slices = false;
this->state.set_done(posSlice);
}
void
PrintObject::_make_perimeters()
PrintObject::make_perimeters()
{
if (this->state.is_done(posPerimeters)) return;
this->state.set_started(posPerimeters);
// Temporary workaround for detect_surfaces_type() not being idempotent (see #3764).
// We can remove this when idempotence is restored. This make_perimeters() method
// will just call merge_slices() to undo the typed slices and invalidate posDetectSurfaces.
if (this->typed_slices)
this->state.invalidate(posSlice);
// prerequisites
this->slice();
// merge slices if they were split into types
// This is not currently taking place because since merge_slices + detect_surfaces_type
// are not truly idempotent we are invalidating posSlice here (see the Perl part of
@ -1110,11 +1185,14 @@ PrintObject::_make_perimeters()
}
void
PrintObject::_infill()
PrintObject::infill()
{
if (this->state.is_done(posInfill)) return;
this->state.set_started(posInfill);
// prerequisites
this->prepare_infill();
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::make_fills, _1),
@ -1131,7 +1209,8 @@ PrintObject::_infill()
void
PrintObject::prepare_infill()
{
if (this->state.is_done(posInfill)) return;
if (this->state.is_done(posPrepareInfill)) return;
// This prepare_infill() is not really idempotent.
// TODO: It should clear and regenerate fill_surfaces at every run
// instead of modifying it in place.
@ -1144,16 +1223,13 @@ PrintObject::prepare_infill()
// prerequisites
this->detect_surfaces_type();
if (this->print()->status_cb != nullptr)
this->print()->status_cb(30, "Preparing infill");
if (this->_print->status_cb != nullptr)
this->_print->status_cb(30, "Preparing infill");
// decide what surfaces are to be filled
for (auto& layer : this->layers) {
for (auto& region : layer->regions) {
region->prepare_fill_surfaces();
}
}
for (auto& layer : this->layers)
for (auto& layerm : layer->regions)
layerm->prepare_fill_surfaces();
// this will detect bridges and reverse bridges
// and rearrange top/bottom/internal surfaces
@ -1175,40 +1251,48 @@ 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.
@ -1216,65 +1300,82 @@ PrintObject::combine_infill()
}
// 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),
intersection_ex(internal, intersection_with_clearance),
stInternal | stVoid);
}
}
@ -1282,13 +1383,6 @@ PrintObject::combine_infill()
}
}
void
PrintObject::infill()
{
this->prepare_infill();
this->_infill();
}
SupportMaterial *
PrintObject::_support_material()
{
@ -1624,8 +1718,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, stInternal | stVoid }))
polygons_append(lower_layer_internal_surfaces, *s);
polygons_append(lower_layer_internal_surfaces, to_polygons(
layerm->fill_surfaces.filter_by_type({ stInternal, stInternal | stVoid })
));
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
}
@ -1634,10 +1729,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, stInternal | stVoid }))
polygons_append(internal, *s);
Polygons internal{ to_polygons(layerm->fill_surfaces.filter_by_type({ stInternal, stInternal | stVoid })) };
layerm->fill_surfaces.remove_types({ stInternal, stInternal | stVoid });
layerm->fill_surfaces.append(intersection_ex(internal, upper_internal, true), stInternal);
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternal | stVoid);

View File

@ -126,11 +126,13 @@ _constant()
void set_step_started(PrintObjectStep step)
%code%{ THIS->state.set_started(step); %};
%name{_detect_surfaces_type} void 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();
void _slice();
SV* _slice_region(size_t region_id, std::vector<double> z, bool modifier)
%code%{
@ -151,8 +153,9 @@ _constant()
}
RETVAL = (SV*)newRV_noinc((SV*)layers_av);
%};
void _make_perimeters();
void _infill();
void make_perimeters();
void prepare_infill();
void infill();
void _simplify_slices(double distance);
int ptr()
@ -284,6 +287,7 @@ _constant()
double skirt_first_layer_height();
Clone<Flow> brim_flow();
Clone<Flow> skirt_flow();
void make_skirt();
void _make_brim();
%{