mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-15 08:26:05 +08:00
Merge branch 'master' into alexrj_flagssurfacetype
This commit is contained in:
commit
74b02336ba
@ -255,134 +255,6 @@ EOF
|
|||||||
print "Done.\n" unless $params{quiet};
|
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 {
|
sub make_brim {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
|
@ -34,175 +34,6 @@ sub support_layers {
|
|||||||
return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ];
|
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 {
|
sub generate_support_material {
|
||||||
my $self = shift;
|
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;
|
1;
|
||||||
|
@ -282,7 +282,7 @@ bool test_6_checks(Print &print)
|
|||||||
|
|
||||||
// Pre-Processing.
|
// Pre-Processing.
|
||||||
PrintObject *print_object = print.objects.front();
|
PrintObject *print_object = print.objects.front();
|
||||||
print_object->_infill();
|
print_object->infill();
|
||||||
SupportMaterial *support_material = print.objects.front()->_support_material();
|
SupportMaterial *support_material = print.objects.front()->_support_material();
|
||||||
support_material->generate(print_object);
|
support_material->generate(print_object);
|
||||||
// TODO but not needed in test 6 (make brims and make skirts).
|
// TODO but not needed in test 6 (make brims and make skirts).
|
||||||
|
@ -41,9 +41,12 @@ class ExPolygonCollection
|
|||||||
/// ExPolygons and check if at least one contains the point.
|
/// ExPolygons and check if at least one contains the point.
|
||||||
bool contains(const Point &point) const;
|
bool contains(const Point &point) const;
|
||||||
|
|
||||||
|
bool empty() const { return expolygons.empty(); }
|
||||||
size_t size() const { return expolygons.size(); }
|
size_t size() const { return expolygons.size(); }
|
||||||
ExPolygons::iterator begin() { return expolygons.begin(); }
|
ExPolygons::iterator begin() { return expolygons.begin(); }
|
||||||
ExPolygons::iterator end() { return expolygons.end(); }
|
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 cbegin() const { return expolygons.cbegin();}
|
||||||
ExPolygons::const_iterator cend() const { return expolygons.cend();}
|
ExPolygons::const_iterator cend() const { return expolygons.cend();}
|
||||||
ExPolygon& at(size_t i) { return expolygons.at(i); }
|
ExPolygon& at(size_t i) { return expolygons.at(i); }
|
||||||
|
@ -74,6 +74,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity
|
|||||||
|
|
||||||
ExtrusionEntitiesPtr::iterator begin() { return entities.begin(); }
|
ExtrusionEntitiesPtr::iterator begin() { return entities.begin(); }
|
||||||
ExtrusionEntitiesPtr::iterator end() { return entities.end(); }
|
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 cbegin() const { return entities.cbegin(); }
|
||||||
ExtrusionEntitiesPtr::const_iterator cend() const { return entities.cend(); }
|
ExtrusionEntitiesPtr::const_iterator cend() const { return entities.cend(); }
|
||||||
|
|
||||||
|
@ -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);
|
Pointf circle_taubin_newton(const Pointfs::const_iterator& input_start, const Pointfs::const_iterator& input_end, size_t cycles = 20);
|
||||||
|
|
||||||
/// Epsilon value
|
/// Epsilon value
|
||||||
|
// FIXME: this is a duplicate from libslic3r.h
|
||||||
constexpr double epsilon { 1e-4 };
|
constexpr double epsilon { 1e-4 };
|
||||||
constexpr coord_t scaled_epsilon { static_cast<coord_t>(epsilon / SCALING_FACTOR) };
|
constexpr coord_t scaled_epsilon { static_cast<coord_t>(epsilon / SCALING_FACTOR) };
|
||||||
|
|
||||||
|
@ -140,16 +140,16 @@ Print::make_brim()
|
|||||||
void
|
void
|
||||||
Print::make_skirt()
|
Print::make_skirt()
|
||||||
{
|
{
|
||||||
|
if (this->state.is_done(psSkirt)) return;
|
||||||
|
this->state.set_started(psSkirt);
|
||||||
|
|
||||||
// prereqs
|
// prereqs
|
||||||
for(auto& obj: this->objects) {
|
for (auto* obj: this->objects) {
|
||||||
obj->make_perimeters();
|
obj->make_perimeters();
|
||||||
obj->infill();
|
obj->infill();
|
||||||
obj->generate_support_material();
|
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*
|
// since this method must be idempotent, we clear skirt paths *before*
|
||||||
// checking whether we need to generate them
|
// checking whether we need to generate them
|
||||||
this->skirt.clear();
|
this->skirt.clear();
|
||||||
@ -173,10 +173,11 @@ Print::make_skirt()
|
|||||||
// prepended to the first 'n' layers (with 'n' = skirt_height).
|
// prepended to the first 'n' layers (with 'n' = skirt_height).
|
||||||
// $skirt_height_z in this case is the highest possible skirt height for safety.
|
// $skirt_height_z in this case is the highest possible skirt height for safety.
|
||||||
double skirt_height_z {-1.0};
|
double skirt_height_z {-1.0};
|
||||||
for (const auto& object : this->objects) {
|
for (const auto* object : this->objects) {
|
||||||
const size_t skirt_height {
|
const size_t skirt_height {
|
||||||
this->has_infinite_skirt() ? object->layer_count() :
|
this->has_infinite_skirt()
|
||||||
std::min(size_t(this->config.skirt_height()), object->layer_count())
|
? object->layer_count()
|
||||||
|
: std::min(size_t(this->config.skirt_height()), object->layer_count())
|
||||||
};
|
};
|
||||||
const Layer* highest_layer { object->get_layer(skirt_height - 1) };
|
const Layer* highest_layer { object->get_layer(skirt_height - 1) };
|
||||||
skirt_height_z = std::max(skirt_height_z, highest_layer->print_z);
|
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
|
// collect points from all layers contained in skirt height
|
||||||
Points points;
|
Points points;
|
||||||
for(auto* object : this->objects) {
|
for (auto* object : this->objects) {
|
||||||
Points object_points;
|
Points object_points;
|
||||||
|
|
||||||
// get object layers up to skirt_height_z
|
// get object layers up to skirt_height_z
|
||||||
for(auto* layer : object->layers) {
|
for (const auto* layer : object->layers) {
|
||||||
if(layer->print_z > skirt_height_z)break;
|
if (layer->print_z > skirt_height_z) break;
|
||||||
for(ExPolygon poly : layer->slices){
|
for (const ExPolygon ex : layer->slices)
|
||||||
for(Point point : static_cast<Points>(poly)){
|
append_to(object_points, static_cast<Points>(ex));
|
||||||
object_points.push_back(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get support layers up to $skirt_height_z
|
// get support layers up to skirt_height_z
|
||||||
for(auto* layer : object->support_layers) {
|
for (const auto* layer : object->support_layers) {
|
||||||
if(layer->print_z > skirt_height_z)break;
|
if (layer->print_z > skirt_height_z) break;
|
||||||
for(auto* ee : layer->support_fills){
|
for (auto* ee : layer->support_fills)
|
||||||
for(Point point : ee->as_polyline().points){
|
append_to(object_points, ee->as_polyline().points);
|
||||||
object_points.push_back(point);
|
for (auto* ee : layer->support_interface_fills)
|
||||||
}
|
append_to(object_points, ee->as_polyline().points);
|
||||||
}
|
|
||||||
for(auto* ee : layer->support_interface_fills){
|
|
||||||
for(Point point : ee->as_polyline().points){
|
|
||||||
object_points.push_back(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// repeat points for each object copy
|
// repeat points for each object copy
|
||||||
for(auto copy : object->_shifted_copies) {
|
for (const auto& copy : object->_shifted_copies) {
|
||||||
for(Point point : object_points){
|
for (Point p : object_points) {
|
||||||
point.translate(copy);
|
p.translate(copy);
|
||||||
points.push_back(point);
|
points.push_back(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (points.size() < 3) return; // at least three points required for a convex hull
|
if (points.size() < 3) return; // at least three points required for a convex hull
|
||||||
|
|
||||||
// find out 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,
|
// skirt may be printed on several layers, having distinct layer heights,
|
||||||
// but loops must be aligned so can't vary width/spacing
|
// but loops must be aligned so can't vary width/spacing
|
||||||
// TODO: use each extruder's own flow
|
// TODO: use each extruder's own flow
|
||||||
auto first_layer_height = this->skirt_first_layer_height();
|
const auto first_layer_height = this->skirt_first_layer_height();
|
||||||
auto flow = this->skirt_flow();
|
const auto flow = this->skirt_flow();
|
||||||
auto spacing = flow.spacing();
|
const auto spacing = flow.scaled_spacing();
|
||||||
auto mm3_per_mm = flow.mm3_per_mm();
|
const auto mm3_per_mm = flow.mm3_per_mm();
|
||||||
|
|
||||||
|
int skirts = this->config.skirts();
|
||||||
auto skirts = this->config.skirts;
|
if (skirts == 0 && this->has_infinite_skirt())
|
||||||
if(this->has_infinite_skirt() && skirts == 0){
|
|
||||||
skirts = 1;
|
skirts = 1;
|
||||||
}
|
|
||||||
|
|
||||||
//my @extruded_length = (); # for each extruder
|
const std::set<size_t> extruders{ this->extruders() };
|
||||||
//extruders_e_per_mm = ();
|
auto extruder_it { extruders.begin() };
|
||||||
//size_t extruder_idx = 0;
|
std::vector<float> e_per_mm{0}, extruded_length{0};
|
||||||
|
if (this->config.min_skirt_length() > 0)
|
||||||
// new to the cpp implementation
|
for (auto i : extruders)
|
||||||
float e_per_mm {0.0}, extruded_length = 0;
|
e_per_mm[i] = Extruder(i, &this->config).e_per_mm(mm3_per_mm);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw outlines from outside to inside
|
// 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
|
// 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--) {
|
for (int i = skirts; i > 0; i--) {
|
||||||
distance += scale_(spacing);
|
distance += spacing;
|
||||||
auto loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0);
|
const Polygon loop = offset(Polygons{convex}, distance, 1, jtRound, scale_(0.1)).at(0);
|
||||||
auto epath = ExtrusionPath(erSkirt,
|
auto epath = ExtrusionPath(
|
||||||
|
erSkirt,
|
||||||
mm3_per_mm, // this will be overridden at G-code export time
|
mm3_per_mm, // this will be overridden at G-code export time
|
||||||
flow.width,
|
flow.width,
|
||||||
first_layer_height // this will be overridden at G-code export time
|
first_layer_height // this will be overridden at G-code export time
|
||||||
);
|
);
|
||||||
epath.polyline = loop.split_at_first_point();
|
epath.polyline = loop.split_at_first_point();
|
||||||
auto eloop = ExtrusionLoop(epath,elrSkirt);
|
auto eloop = ExtrusionLoop(epath, elrSkirt);
|
||||||
this->skirt.append(eloop);
|
this->skirt.append(eloop);
|
||||||
|
|
||||||
if (this->config.min_skirt_length.getFloat() > 0) {
|
if (this->config.min_skirt_length() > 0) {
|
||||||
// Alternative simpler method
|
extruded_length[*extruder_it] += unscale(loop.length()) * e_per_mm[*extruder_it];
|
||||||
extruded_length += unscale(loop.length()) * e_per_mm;
|
for (auto j : extruders) {
|
||||||
if(extruded_length >= this->config.min_skirt_length.getFloat()){
|
if (extruded_length[j] < this->config.min_skirt_length()) {
|
||||||
extruders_warm++;
|
++i;
|
||||||
extruded_length = 0;
|
break;
|
||||||
}
|
}
|
||||||
if (extruders_warm < this->extruders().size()){
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
if (extruded_length[*extruder_it] >= this->config.min_skirt_length() && extruder_it != extruders.end())
|
||||||
/*$extruded_length[$extruder_idx] ||= 0;
|
++extruder_it;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,6 @@ class PrintObject
|
|||||||
void _slice();
|
void _slice();
|
||||||
std::vector<ExPolygons> _slice_region(size_t region_id, std::vector<float> z, bool modifier);
|
std::vector<ExPolygons> _slice_region(size_t region_id, std::vector<float> z, bool modifier);
|
||||||
|
|
||||||
void _make_perimeters();
|
|
||||||
void _infill();
|
void _infill();
|
||||||
|
|
||||||
/// Initialize and generate support material.
|
/// Initialize and generate support material.
|
||||||
|
@ -329,15 +329,18 @@ PrintObject::has_support_material() const
|
|||||||
|| this->config.support_material_enforce_layers > 0;
|
|| 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
|
void
|
||||||
PrintObject::detect_surfaces_type()
|
PrintObject::detect_surfaces_type()
|
||||||
{
|
{
|
||||||
// prerequisites
|
|
||||||
// this->slice();
|
|
||||||
|
|
||||||
if (this->state.is_done(posDetectSurfaces)) return;
|
if (this->state.is_done(posDetectSurfaces)) return;
|
||||||
this->state.set_started(posDetectSurfaces);
|
this->state.set_started(posDetectSurfaces);
|
||||||
|
|
||||||
|
// prerequisites
|
||||||
|
this->slice();
|
||||||
|
|
||||||
parallelize<Layer*>(
|
parallelize<Layer*>(
|
||||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<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),
|
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;
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/*
|
||||||
PrintObject::make_perimeters()
|
1) Decides Z positions of the layers,
|
||||||
{
|
2) Initializes layers and their regions
|
||||||
if (this->state.is_done(posPerimeters)) return;
|
3) Slices the object meshes
|
||||||
if (this->typed_slices)
|
4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
|
||||||
this->state.invalidate(posSlice);
|
5) Applies size compensation (offsets the slices in XY plane)
|
||||||
this->slice(); // take care of prereqs
|
6) Replaces bad slices by the slices reconstructed from the upper/lower layer
|
||||||
this->_make_perimeters();
|
Resulting expolygons of layer regions are marked as Internal.
|
||||||
}
|
|
||||||
|
|
||||||
|
This should be idempotent.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
PrintObject::slice()
|
PrintObject::slice()
|
||||||
{
|
{
|
||||||
@ -965,7 +969,6 @@ PrintObject::slice()
|
|||||||
_print->status_cb(10, "Processing triangulated mesh");
|
_print->status_cb(10, "Processing triangulated mesh");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this->_slice();
|
this->_slice();
|
||||||
|
|
||||||
// detect slicing errors
|
// detect slicing errors
|
||||||
@ -975,22 +978,94 @@ PrintObject::slice()
|
|||||||
<< "I tried to repair it, however you might want to check "
|
<< "I tried to repair it, however you might want to check "
|
||||||
<< "the results or repair the input file and retry.\n";
|
<< "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";
|
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?
|
return; // make this throw an exception instead?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this->typed_slices = false;
|
this->typed_slices = false;
|
||||||
this->state.set_done(posSlice);
|
this->state.set_done(posSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintObject::_make_perimeters()
|
PrintObject::make_perimeters()
|
||||||
{
|
{
|
||||||
if (this->state.is_done(posPerimeters)) return;
|
if (this->state.is_done(posPerimeters)) return;
|
||||||
this->state.set_started(posPerimeters);
|
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
|
// merge slices if they were split into types
|
||||||
// This is not currently taking place because since merge_slices + detect_surfaces_type
|
// 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
|
// are not truly idempotent we are invalidating posSlice here (see the Perl part of
|
||||||
@ -1110,11 +1185,14 @@ PrintObject::_make_perimeters()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintObject::_infill()
|
PrintObject::infill()
|
||||||
{
|
{
|
||||||
if (this->state.is_done(posInfill)) return;
|
if (this->state.is_done(posInfill)) return;
|
||||||
this->state.set_started(posInfill);
|
this->state.set_started(posInfill);
|
||||||
|
|
||||||
|
// prerequisites
|
||||||
|
this->prepare_infill();
|
||||||
|
|
||||||
parallelize<Layer*>(
|
parallelize<Layer*>(
|
||||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<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),
|
boost::bind(&Slic3r::Layer::make_fills, _1),
|
||||||
@ -1131,7 +1209,8 @@ PrintObject::_infill()
|
|||||||
void
|
void
|
||||||
PrintObject::prepare_infill()
|
PrintObject::prepare_infill()
|
||||||
{
|
{
|
||||||
if (this->state.is_done(posInfill)) return;
|
if (this->state.is_done(posPrepareInfill)) return;
|
||||||
|
|
||||||
// This prepare_infill() is not really idempotent.
|
// This prepare_infill() is not really idempotent.
|
||||||
// TODO: It should clear and regenerate fill_surfaces at every run
|
// TODO: It should clear and regenerate fill_surfaces at every run
|
||||||
// instead of modifying it in place.
|
// instead of modifying it in place.
|
||||||
@ -1144,16 +1223,13 @@ PrintObject::prepare_infill()
|
|||||||
// prerequisites
|
// prerequisites
|
||||||
this->detect_surfaces_type();
|
this->detect_surfaces_type();
|
||||||
|
|
||||||
if (this->print()->status_cb != nullptr)
|
if (this->_print->status_cb != nullptr)
|
||||||
this->print()->status_cb(30, "Preparing infill");
|
this->_print->status_cb(30, "Preparing infill");
|
||||||
|
|
||||||
|
|
||||||
// decide what surfaces are to be filled
|
// decide what surfaces are to be filled
|
||||||
for (auto& layer : this->layers) {
|
for (auto& layer : this->layers)
|
||||||
for (auto& region : layer->regions) {
|
for (auto& layerm : layer->regions)
|
||||||
region->prepare_fill_surfaces();
|
layerm->prepare_fill_surfaces();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will detect bridges and reverse bridges
|
// this will detect bridges and reverse bridges
|
||||||
// and rearrange top/bottom/internal surfaces
|
// 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
|
void
|
||||||
PrintObject::combine_infill()
|
PrintObject::combine_infill()
|
||||||
{
|
{
|
||||||
// Work on each region separately.
|
// Work on each region separately.
|
||||||
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
|
||||||
const PrintRegion *region = this->print()->regions[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.)
|
if (every < 2 || region->config.fill_density == 0.)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
||||||
//FIXME limit the layer height to max_layer_height
|
// FIXME: limit the layer height to max_layer_height
|
||||||
double nozzle_diameter = std::min(
|
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.infill_extruder.value - 1),
|
||||||
this->print()->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1));
|
this->_print->config.nozzle_diameter.get_at(region->config.solid_infill_extruder.value - 1)
|
||||||
|
);
|
||||||
|
|
||||||
// define the combinations
|
// 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.;
|
double current_height = 0.;
|
||||||
size_t num_layers = 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];
|
const Layer *layer = this->layers[layer_idx];
|
||||||
if (layer->id() == 0)
|
|
||||||
// Skip first print layer (which may not be first layer in array because of raft).
|
// Skip first print layer (which may not be first layer in array because of raft).
|
||||||
|
if (layer->id() == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check whether the combination of this layer with the lower layers' buffer
|
// Check whether the combination of this layer with the lower layers' buffer
|
||||||
// would exceed max layer height or max combined layer count.
|
// 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.
|
// Append combination to lower layer.
|
||||||
combine[layer_idx - 1] = num_layers;
|
combine[layer_idx - 1] = num_layers;
|
||||||
current_height = 0.;
|
current_height = 0.;
|
||||||
num_layers = 0;
|
num_layers = 0;
|
||||||
}
|
}
|
||||||
current_height += layer->height;
|
current_height += layer->height;
|
||||||
++ num_layers;
|
++num_layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append lower layers (if any) to uppermost layer.
|
// 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
|
// loop through layers to which we have assigned layers to combine
|
||||||
for (size_t layer_idx = 0; layer_idx < this->layers.size(); ++ layer_idx) {
|
for (size_t layer_idx = 0; layer_idx < combine.size(); ++layer_idx) {
|
||||||
size_t num_layers = combine[layer_idx];
|
const size_t& num_layers = combine[layer_idx];
|
||||||
if (num_layers <= 1)
|
if (num_layers <= 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Get all the LayerRegion objects to be combined.
|
// Get all the LayerRegion objects to be combined.
|
||||||
std::vector<LayerRegion*> layerms;
|
std::vector<LayerRegion*> layerms;
|
||||||
layerms.reserve(num_layers);
|
layerms.reserve(num_layers);
|
||||||
for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++ i)
|
for (size_t i = layer_idx + 1 - num_layers; i <= layer_idx; ++i)
|
||||||
layerms.emplace_back(this->layers[i]->regions[region_id]);
|
layerms.push_back(this->layers[i]->regions[region_id]);
|
||||||
|
|
||||||
// We need to perform a multi-layer intersection, so let's split it in pairs.
|
// 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.
|
// Initialize the intersection with the candidates of the lowest layer.
|
||||||
ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal));
|
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.
|
// 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(
|
intersection = intersection_ex(
|
||||||
to_polygons(intersection),
|
to_polygons(intersection),
|
||||||
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)),
|
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.)
|
// 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(),
|
intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
|
||||||
[area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
|
[area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }),
|
||||||
intersection.end());
|
intersection.end());
|
||||||
|
|
||||||
if (intersection.empty())
|
if (intersection.empty())
|
||||||
continue;
|
continue;
|
||||||
// Slic3r::debugf " combining %d %s regions from layers %d-%d\n",
|
|
||||||
// scalar(@$intersection),
|
#ifdef SLIC3R_DEBUG
|
||||||
// ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'),
|
std::cout << " combining " << intersection.size()
|
||||||
// $layer_idx-($every-1), $layer_idx;
|
<< " 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,
|
// intersection now contains the regions that can be combined across the full amount of layers,
|
||||||
// so let's remove those areas from all layers.
|
// so let's remove those areas from all layers.
|
||||||
Polygons intersection_with_clearance;
|
|
||||||
intersection_with_clearance.reserve(intersection.size());
|
const float clearance_offset =
|
||||||
float clearance_offset =
|
|
||||||
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
|
0.5f * layerms.back()->flow(frPerimeter).scaled_width() +
|
||||||
// Because fill areas for rectilinear and honeycomb are grown
|
// Because fill areas for rectilinear and honeycomb are grown
|
||||||
// later to overlap perimeters, we need to counteract that too.
|
// later to overlap perimeters, we need to counteract that too.
|
||||||
((region->config.fill_pattern == ipRectilinear ||
|
((region->config.fill_pattern == ipRectilinear ||
|
||||||
region->config.fill_pattern == ipGrid ||
|
region->config.fill_pattern == ipGrid ||
|
||||||
region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
region->config.fill_pattern == ipHoneycomb) ? 1.5f : 0.5f)
|
||||||
layerms.back()->flow(frSolidInfill).scaled_width();
|
* layerms.back()->flow(frSolidInfill).scaled_width();
|
||||||
for (ExPolygon &expoly : intersection)
|
|
||||||
|
Polygons intersection_with_clearance;
|
||||||
|
intersection_with_clearance.reserve(intersection.size());
|
||||||
|
for (const ExPolygon &expoly : intersection)
|
||||||
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
|
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
|
||||||
|
|
||||||
for (LayerRegion *layerm : layerms) {
|
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.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()) {
|
if (layerm == layerms.back()) {
|
||||||
// Apply surfaces back with adjusted depth to the uppermost layer.
|
// Apply surfaces back with adjusted depth to the uppermost layer.
|
||||||
Surface templ(stInternal, ExPolygon());
|
Surface templ(stInternal, ExPolygon());
|
||||||
templ.thickness = 0.;
|
templ.thickness = 0.;
|
||||||
for (LayerRegion *layerm2 : layerms)
|
for (const LayerRegion *layerm2 : layerms)
|
||||||
templ.thickness += layerm2->layer()->height;
|
templ.thickness += layerm2->layer()->height;
|
||||||
templ.thickness_layers = (unsigned short)layerms.size();
|
templ.thickness_layers = (unsigned short)layerms.size();
|
||||||
layerm->fill_surfaces.append(intersection, templ);
|
layerm->fill_surfaces.append(intersection, templ);
|
||||||
} else {
|
} else {
|
||||||
// Save void surfaces.
|
// Save void surfaces.
|
||||||
layerm->fill_surfaces.append(
|
layerm->fill_surfaces.append(
|
||||||
intersection_ex(internal, intersection_with_clearance, false),
|
intersection_ex(internal, intersection_with_clearance),
|
||||||
stInternal | stVoid);
|
stInternal | stVoid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1282,13 +1383,6 @@ PrintObject::combine_infill()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PrintObject::infill()
|
|
||||||
{
|
|
||||||
this->prepare_infill();
|
|
||||||
this->_infill();
|
|
||||||
}
|
|
||||||
|
|
||||||
SupportMaterial *
|
SupportMaterial *
|
||||||
PrintObject::_support_material()
|
PrintObject::_support_material()
|
||||||
{
|
{
|
||||||
@ -1624,8 +1718,9 @@ PrintObject::clip_fill_surfaces()
|
|||||||
// get our current internal fill boundaries
|
// get our current internal fill boundaries
|
||||||
Polygons lower_layer_internal_surfaces;
|
Polygons lower_layer_internal_surfaces;
|
||||||
for (const auto* layerm : lower_layer->regions)
|
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, to_polygons(
|
||||||
polygons_append(lower_layer_internal_surfaces, *s);
|
layerm->fill_surfaces.filter_by_type({ stInternal, stInternal | stVoid })
|
||||||
|
));
|
||||||
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
|
upper_internal = intersection(overhangs, lower_layer_internal_surfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1634,10 +1729,7 @@ PrintObject::clip_fill_surfaces()
|
|||||||
if (layerm->region()->config.fill_density.value == 0)
|
if (layerm->region()->config.fill_density.value == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Polygons internal;
|
Polygons internal{ to_polygons(layerm->fill_surfaces.filter_by_type({ stInternal, stInternal | stVoid })) };
|
||||||
for (const auto* s : layerm->fill_surfaces.filter_by_type({ stInternal, stInternal | stVoid }))
|
|
||||||
polygons_append(internal, *s);
|
|
||||||
|
|
||||||
layerm->fill_surfaces.remove_types({ 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(intersection_ex(internal, upper_internal, true), stInternal);
|
||||||
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternal | stVoid);
|
layerm->fill_surfaces.append(diff_ex (internal, upper_internal, true), stInternal | stVoid);
|
||||||
|
@ -126,11 +126,13 @@ _constant()
|
|||||||
void set_step_started(PrintObjectStep step)
|
void set_step_started(PrintObjectStep step)
|
||||||
%code%{ THIS->state.set_started(step); %};
|
%code%{ THIS->state.set_started(step); %};
|
||||||
|
|
||||||
%name{_detect_surfaces_type} void detect_surfaces_type();
|
void detect_surfaces_type();
|
||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
|
void combine_infill();
|
||||||
void discover_horizontal_shells();
|
void discover_horizontal_shells();
|
||||||
void clip_fill_surfaces();
|
void clip_fill_surfaces();
|
||||||
|
void slice();
|
||||||
void _slice();
|
void _slice();
|
||||||
SV* _slice_region(size_t region_id, std::vector<double> z, bool modifier)
|
SV* _slice_region(size_t region_id, std::vector<double> z, bool modifier)
|
||||||
%code%{
|
%code%{
|
||||||
@ -151,8 +153,9 @@ _constant()
|
|||||||
}
|
}
|
||||||
RETVAL = (SV*)newRV_noinc((SV*)layers_av);
|
RETVAL = (SV*)newRV_noinc((SV*)layers_av);
|
||||||
%};
|
%};
|
||||||
void _make_perimeters();
|
void make_perimeters();
|
||||||
void _infill();
|
void prepare_infill();
|
||||||
|
void infill();
|
||||||
void _simplify_slices(double distance);
|
void _simplify_slices(double distance);
|
||||||
|
|
||||||
int ptr()
|
int ptr()
|
||||||
@ -284,6 +287,7 @@ _constant()
|
|||||||
double skirt_first_layer_height();
|
double skirt_first_layer_height();
|
||||||
Clone<Flow> brim_flow();
|
Clone<Flow> brim_flow();
|
||||||
Clone<Flow> skirt_flow();
|
Clone<Flow> skirt_flow();
|
||||||
|
void make_skirt();
|
||||||
void _make_brim();
|
void _make_brim();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user