New --fill-every-layers option to get high accuracy on external surfaces while speeding up infill

This commit is contained in:
Alessandro Ranellucci 2011-10-18 15:57:53 +02:00
parent 54cc6216a1
commit 7f341cfcd3
16 changed files with 177 additions and 43 deletions

View File

@ -1,25 +1,42 @@
Build.PL Build.PL
lib/Slic3r.pm lib/Slic3r.pm
lib/Slic3r/Config.pm
lib/Slic3r/ExPolygon.pm
lib/Slic3r/Extruder.pm lib/Slic3r/Extruder.pm
lib/Slic3r/ExtrusionLoop.pm lib/Slic3r/ExtrusionLoop.pm
lib/Slic3r/ExtrusionPath.pm lib/Slic3r/ExtrusionPath.pm
lib/Slic3r/ExtrusionPath/Collection.pm lib/Slic3r/ExtrusionPath/Collection.pm
lib/Slic3r/Fill.pm lib/Slic3r/Fill.pm
lib/Slic3r/Fill/Base.pm
lib/Slic3r/Fill/Rectilinear.pm lib/Slic3r/Fill/Rectilinear.pm
lib/Slic3r/Fill/Rectilinear2.pm
lib/Slic3r/Geometry.pm lib/Slic3r/Geometry.pm
lib/Slic3r/Geometry/Clipper.pm
lib/Slic3r/Geometry/DouglasPeucker.pm lib/Slic3r/Geometry/DouglasPeucker.pm
lib/Slic3r/GUI.pm
lib/Slic3r/GUI/OptionsGroup.pm
lib/Slic3r/GUI/SkeinPanel.pm
lib/Slic3r/Layer.pm lib/Slic3r/Layer.pm
lib/Slic3r/Line.pm lib/Slic3r/Line.pm
lib/Slic3r/Line/FacetEdge.pm
lib/Slic3r/Line/FacetEdge/Bottom.pm
lib/Slic3r/Line/FacetEdge/Top.pm
lib/Slic3r/Perimeter.pm lib/Slic3r/Perimeter.pm
lib/Slic3r/Point.pm lib/Slic3r/Point.pm
lib/Slic3r/Polygon.pm
lib/Slic3r/Polyline.pm lib/Slic3r/Polyline.pm
lib/Slic3r/Polyline/Closed.pm lib/Slic3r/Polyline/Closed.pm
lib/Slic3r/Print.pm lib/Slic3r/Print.pm
lib/Slic3r/Skein.pm
lib/Slic3r/STL.pm lib/Slic3r/STL.pm
lib/Slic3r/Surface.pm lib/Slic3r/Surface.pm
lib/Slic3r/Surface/Collection.pm lib/Slic3r/Surface/Bridge.pm
lib/Slic3r/SVG.pm lib/Slic3r/SVG.pm
MANIFEST This list of files MANIFEST This list of files
README.markdown README.markdown
slic3r.pl slic3r.pl
t/clean_polylines.t t/clean_polylines.t
t/clipper.t
t/geometry.t
t/polyclip.t
t/stl.t

View File

@ -40,7 +40,7 @@ Slic3r current features are:
* retraction; * retraction;
* skirt (with rounded corners); * skirt (with rounded corners);
* use relative or absolute extrusion commands; * use relative or absolute extrusion commands;
* high-res perimeters (like the "Skin" plugin for Skeinforge); * infill every N layers (like the "Skin" plugin for Skeinforge);
* detect optimal infill direction for bridges; * detect optimal infill direction for bridges;
* save configuration profiles; * save configuration profiles;
* center print around bed center point; * center print around bed center point;
@ -53,6 +53,7 @@ Roadmap includes the following goals:
* output some statistics; * output some statistics;
* support material for internal perimeters; * support material for internal perimeters;
* new and better GUI;
* cool; * cool;
* other fill patterns. * other fill patterns.
@ -111,9 +112,8 @@ The author is Alessandro Ranellucci (me).
Accuracy options: Accuracy options:
--layer-height Layer height in mm (default: 0.4) --layer-height Layer height in mm (default: 0.4)
--high-res-perimeters --infill-every-layers
Print perimeters at half layer height to get surface accuracy Infill every N layers (default: 1)
(default: disabled)
Print options: Print options:
--perimeters Number of perimeters/horizontal skins (range: 1+, --perimeters Number of perimeters/horizontal skins (range: 1+,

View File

@ -50,7 +50,7 @@ our $bottom_layer_speed_ratio = 0.3;
# accuracy options # accuracy options
our $resolution = 0.00000001; our $resolution = 0.00000001;
our $layer_height = 0.4; our $layer_height = 0.4;
our $high_res_perimeters = 0; our $infill_every_layers = 1;
our $thickness_ratio = 1; our $thickness_ratio = 1;
our $flow_width; our $flow_width;

View File

@ -64,9 +64,9 @@ our $Options = {
label => 'Layer height (mm)', label => 'Layer height (mm)',
type => 'f', type => 'f',
}, },
'high_res_perimeters' => { 'infill_every_layers' => {
label => 'High-res perimeters', label => 'Infill every N layers',
type => 'bool', type => 'i',
}, },
# print options # print options
@ -262,6 +262,12 @@ sub validate {
die "Invalid value for --fill-density\n" die "Invalid value for --fill-density\n"
if $Slic3r::fill_density < 0 || $Slic3r::fill_density > 1; if $Slic3r::fill_density < 0 || $Slic3r::fill_density > 1;
# --infill-every-layers
die "Invalid value for --infill-every-layers\n"
if $Slic3r::infill_every_layers !~ /^\d+$/ || $Slic3r::infill_every_layers < 1;
die "Maximum infill thickness can't exceed nozzle diameter\n"
if $Slic3r::infill_every_layers * $Slic3r::layer_height > $Slic3r::nozzle_diameter;
# --scale # --scale
die "Invalid value for --scale\n" die "Invalid value for --scale\n"
if $Slic3r::scale <= 0; if $Slic3r::scale <= 0;

View File

@ -84,7 +84,7 @@ sub extrude {
# compensate retraction # compensate retraction
$gcode .= $self->unretract if $self->retracted; $gcode .= $self->unretract if $self->retracted;
XXX "yes!\n" if $path->depth_layers > 1;
# extrude while going to next points # extrude while going to next points
foreach my $line ($path->lines) { foreach my $line ($path->lines) {
# calculate how much filament to drive into the extruder # calculate how much filament to drive into the extruder
@ -93,7 +93,8 @@ sub extrude {
* (($Slic3r::nozzle_diameter**2) / ($Slic3r::filament_diameter ** 2)) * (($Slic3r::nozzle_diameter**2) / ($Slic3r::filament_diameter ** 2))
* $Slic3r::thickness_ratio * $Slic3r::thickness_ratio
* $self->flow_ratio * $self->flow_ratio
* $Slic3r::filament_packing_density; * $Slic3r::filament_packing_density
* $path->depth_layers;
$gcode .= $self->G1($line->b, undef, $e, $description); $gcode .= $self->G1($line->b, undef, $e, $description);
} }

View File

@ -3,6 +3,10 @@ use Moo;
extends 'Slic3r::Polyline'; extends 'Slic3r::Polyline';
# this integer represents the vertical thickness of the extrusion
# expressed in layers
has 'depth_layers' => (is => 'ro', default => sub {1});
use constant PI => 4 * atan2(1, 1); use constant PI => 4 * atan2(1, 1);
sub clip_end { sub clip_end {

View File

@ -48,14 +48,20 @@ sub make_fill {
$filler = 'rectilinear'; $filler = 'rectilinear';
} }
push @path_collection, $self->fillers->{$filler}->fill_surface($surface, my @paths = $self->fillers->{$filler}->fill_surface(
$surface,
density => $density, density => $density,
); );
push @path_collection, map Slic3r::ExtrusionPath->cast(
[ @$_ ],
depth_layers => $surface->depth_layers,
), @paths;
} }
# save into layer # save into layer
push @{ $layer->fills }, Slic3r::ExtrusionPath::Collection->new( push @{ $layer->fills }, Slic3r::ExtrusionPath::Collection->new(
paths => [ map Slic3r::ExtrusionPath->cast([ @$_ ]), @path_collection ], paths => [ @path_collection ],
); );
$layer->fills->[-1]->cleanup; $layer->fills->[-1]->cleanup;
} }

View File

@ -19,7 +19,7 @@ sub infill_direction {
@shift = @{$rotate[1]}; @shift = @{$rotate[1]};
# alternate fill direction # alternate fill direction
if ($self->layer->id % 2) { if (($self->layer->id / $surface->depth_layers) % 2) {
$rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2; $rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2;
} }

View File

@ -29,7 +29,7 @@ sub new {
), ),
accuracy => Slic3r::GUI::OptionsGroup->new($self, accuracy => Slic3r::GUI::OptionsGroup->new($self,
title => 'Accuracy', title => 'Accuracy',
options => [qw(layer_height high_res_perimeters)], options => [qw(layer_height infill_every_layers)],
), ),
print => Slic3r::GUI::OptionsGroup->new($self, print => Slic3r::GUI::OptionsGroup->new($self,
title => 'Print settings', title => 'Print settings',

View File

@ -4,11 +4,27 @@ use warnings;
require Exporter; require Exporter;
our @ISA = qw(Exporter); our @ISA = qw(Exporter);
our @EXPORT_OK = qw(diff_ex diff union_ex); our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset
diff_ex diff union_ex intersection_ex);
use Math::Clipper 1.02 ':all'; use Math::Clipper 1.02 ':all';
our $clipper = Math::Clipper->new; our $clipper = Math::Clipper->new;
sub explode_expolygon {
my ($expolygon) = @_;
return ($expolygon->{outer}, @{ $expolygon->{holes} });
}
sub explode_expolygons {
my ($expolygons) = @_;
return map explode_expolygon($_), @$expolygons;
}
sub safety_offset {
my ($polygons) = @_;
return Math::Clipper::offset($polygons, 100, 100, JT_MITER, 2);
}
sub diff_ex { sub diff_ex {
my ($subject, $clip) = @_; my ($subject, $clip) = @_;
@ -29,4 +45,13 @@ sub union_ex {
return $clipper->ex_execute(CT_UNION, PFT_NONZERO, PFT_NONZERO); return $clipper->ex_execute(CT_UNION, PFT_NONZERO, PFT_NONZERO);
} }
sub intersection_ex {
my ($subject, $clip) = @_;
$clipper->clear;
$clipper->add_subject_polygons($subject);
$clipper->add_clip_polygons($clip);
return $clipper->ex_execute(CT_INTERSECTION, PFT_NONZERO, PFT_NONZERO);
}
1; 1;

View File

@ -4,7 +4,7 @@ use Moo;
use Math::Clipper ':all'; use Math::Clipper ':all';
use Slic3r::Geometry qw(polygon_lines points_coincide angle3points polyline_lines nearest_point use Slic3r::Geometry qw(polygon_lines points_coincide angle3points polyline_lines nearest_point
line_length); line_length);
use Slic3r::Geometry::Clipper qw(union_ex); use Slic3r::Geometry::Clipper qw(safety_offset union_ex);
use XXX; use XXX;
use constant PI => 4 * atan2(1, 1); use constant PI => 4 * atan2(1, 1);
@ -184,7 +184,7 @@ sub make_surfaces {
#) if !$next_lines; #) if !$next_lines;
$next_lines $next_lines
or die sprintf("No lines start at point %s. This shouldn't happen", $get_point_id->($points[-1])); or die sprintf("No lines start at point %s. This shouldn't happen. Please check the model for manifoldness.", $get_point_id->($points[-1]));
last CYCLE if !@$next_lines; last CYCLE if !@$next_lines;
my @ordered_next_lines = sort my @ordered_next_lines = sort
@ -284,10 +284,7 @@ sub process_bridges {
# offset the surface a bit to avoid approximation issues when doing the # offset the surface a bit to avoid approximation issues when doing the
# intersection below (this is to make sure we overlap with supporting # intersection below (this is to make sure we overlap with supporting
# surfaces, otherwise a little gap will result from intersection) # surfaces, otherwise a little gap will result from intersection)
{ $surface_p = safety_offset([$surface_p])->[0];
my $offset = offset([$surface_p], 100, 100, JT_MITER, 2);
$surface_p = $offset->[0];
}
#use Slic3r::SVG; #use Slic3r::SVG;
#Slic3r::SVG::output(undef, "bridge.svg", #Slic3r::SVG::output(undef, "bridge.svg",

View File

@ -22,10 +22,10 @@ sub id {
sub cast { sub cast {
my $class = shift; my $class = shift;
my ($points) = @_; my ($points, %args) = @_;
$points = [ map { ref $_ eq 'ARRAY' ? Slic3r::Point->new($_) : $_ } @$points ]; $points = [ map { ref $_ eq 'ARRAY' ? Slic3r::Point->new($_) : $_ } @$points ];
return $class->new(points => $points); return $class->new(points => $points, %args);
} }
sub lines { sub lines {

View File

@ -2,7 +2,7 @@ package Slic3r::Print;
use Moo; use Moo;
use Math::Clipper ':all'; use Math::Clipper ':all';
use Slic3r::Geometry::Clipper qw(diff_ex union_ex); use Slic3r::Geometry::Clipper qw(explode_expolygons safety_offset diff_ex union_ex intersection_ex);
use XXX; use XXX;
use constant X => 0; use constant X => 0;
@ -145,7 +145,7 @@ sub detect_surfaces_type {
# okay, this is an Ugly Hack(tm) to avoid floating point math problems # okay, this is an Ugly Hack(tm) to avoid floating point math problems
# with diagonal bridges. will find a nicer solution, promised. # with diagonal bridges. will find a nicer solution, promised.
my $offset = offset([$surface->contour->p], 100, 100, JT_MITER, 2); my $offset = safety_offset([$surface->contour->p]);
@{$surface->contour->points} = map Slic3r::Point->new($_), @{ $offset->[0] }; @{$surface->contour->points} = map Slic3r::Point->new($_), @{ $offset->[0] };
} }
@ -301,6 +301,91 @@ sub split_bridges_fills {
$_->split_bridges_fills for @{$self->layers}; $_->split_bridges_fills for @{$self->layers};
} }
# combine fill surfaces across layers
sub infill_every_layers {
my $self = shift;
return unless $Slic3r::infill_every_layers > 1;
printf "==> COMBINING INFILL\n";
# start from bottom, skip first layer
for (my $i = 1; $i < $self->layer_count; $i++) {
my $layer = $self->layer($i);
# skip layer if no internal fill surfaces
next if !grep $_->surface_type eq 'internal', map @$_, @{$layer->fill_surfaces};
# for each possible depth, look for intersections with the lower layer
# we do this from the greater depth to the smaller
for (my $d = $Slic3r::infill_every_layers - 1; $d >= 1; $d--) {
next if ($i - $d) < 0;
my $lower_layer = $self->layer($i - 1);
# select surfaces of the lower layer having the depth we're looking for
my @lower_surfaces = grep $_->depth_layers == $d && $_->surface_type eq 'internal',
map @$_, @{$lower_layer->fill_surfaces};
next if !@lower_surfaces;
# process each group of surfaces separately
foreach my $surfaces (@{$layer->fill_surfaces}) {
# calculate intersection between our surfaces and theirs
my $intersection = intersection_ex(
[ map $_->p, grep $_->depth_layers <= $d, @lower_surfaces ],
[ map $_->p, grep $_->surface_type eq 'internal', @$surfaces ],
);
next if !@$intersection;
# new fill surfaces of the current layer are:
# - any non-internal surface
# - intersections found (with a $d + 1 depth)
# - any internal surface not belonging to the intersection (with its original depth)
{
my @new_surfaces = ();
push @new_surfaces, grep $_->surface_type ne 'internal', @$surfaces;
push @new_surfaces, map Slic3r::Surface->cast_from_expolygon
($_, surface_type => 'internal', depth_layers => $d + 1), @$intersection;
foreach my $depth (reverse $d..$Slic3r::infill_every_layers) {
push @new_surfaces, map Slic3r::Surface->cast_from_expolygon
($_, surface_type => 'internal', depth_layers => $depth),
# difference between our internal layers with depth == $depth
# and the intersection found
@{diff_ex(
[
map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth,
@$surfaces,
],
safety_offset([ explode_expolygons($intersection) ]),
)};
}
@$surfaces = @new_surfaces;
}
# now we remove the intersections from lower layer
foreach my $lower_surfaces (@{$lower_layer->fill_surfaces}) {
my @new_surfaces = ();
push @new_surfaces, grep $_->surface_type ne 'internal', @$lower_surfaces;
foreach my $depth (1..$Slic3r::infill_every_layers) {
push @new_surfaces, map Slic3r::Surface->cast_from_expolygon
($_, surface_type => 'internal', depth_layers => $depth),
# difference between internal layers with depth == $depth
# and the intersection found
@{diff_ex(
[
map $_->p, grep $_->surface_type eq 'internal' && $_->depth_layers == $depth,
@$lower_surfaces,
],
safety_offset([ explode_expolygons($intersection) ]),
)};
}
@$lower_surfaces = @new_surfaces;
}
}
}
}
}
sub extrude_fills { sub extrude_fills {
my $self = shift; my $self = shift;
@ -345,19 +430,6 @@ sub export_gcode {
# write gcode commands layer by layer # write gcode commands layer by layer
foreach my $layer (@{ $self->layers }) { foreach my $layer (@{ $self->layers }) {
# with the --high-res-perimeters options enabled we extrude perimeters for
# each layer twice at half height
if ($Slic3r::high_res_perimeters && $layer->id > 0) {
# go to half-layer
printf $fh $extruder->move_z($Slic3r::z_offset + $layer->z * $Slic3r::resolution - $Slic3r::layer_height/2);
# extrude perimeters
$extruder->flow_ratio(0.5);
printf $fh $extruder->extrude_loop($_, 'perimeter') for @{ $layer->perimeters };
$extruder->flow_ratio(1);
}
# go to layer # go to layer
printf $fh $extruder->move_z($Slic3r::z_offset + $layer->z * $Slic3r::resolution); printf $fh $extruder->move_z($Slic3r::z_offset + $layer->z * $Slic3r::resolution);

View File

@ -47,6 +47,9 @@ sub go {
# they will be split in internal and internal-solid surfaces # they will be split in internal and internal-solid surfaces
$print->discover_horizontal_shells; $print->discover_horizontal_shells;
# combine fill surfaces to honor the "infill every N layers" option
$print->infill_every_layers;
# this will generate extrusion paths for each layer # this will generate extrusion paths for each layer
$print->extrude_fills; $print->extrude_fills;

View File

@ -19,6 +19,9 @@ has 'surface_type' => (
#isa => enum([qw(internal internal-solid bottom top)]), #isa => enum([qw(internal internal-solid bottom top)]),
); );
# this integer represents the thickness of the surface expressed in layers
has 'depth_layers' => (is => 'ro', default => sub {1});
sub cast_from_polygon { sub cast_from_polygon {
my $class = shift; my $class = shift;
my ($polygon, %args) = @_; my ($polygon, %args) = @_;
@ -34,6 +37,7 @@ sub cast_from_expolygon {
my ($expolygon, %args) = @_; my ($expolygon, %args) = @_;
if (ref $expolygon ne 'HASH') { if (ref $expolygon ne 'HASH') {
use XXX; ZZZ $expolygon if ref $expolygon eq 'ARRAY';
$expolygon = $expolygon->clipper_expolygon; $expolygon = $expolygon->clipper_expolygon;
} }

View File

@ -41,7 +41,7 @@ GetOptions(
# accuracy options # accuracy options
'layer-height=f' => \$Slic3r::layer_height, 'layer-height=f' => \$Slic3r::layer_height,
'high-res-perimeters' => \$Slic3r::high_res_perimeters, 'infill-every-layers=i' => \$Slic3r::infill_every_layers,
# print options # print options
'perimeters=i' => \$Slic3r::perimeter_offsets, 'perimeters=i' => \$Slic3r::perimeter_offsets,
@ -147,9 +147,8 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
Accuracy options: Accuracy options:
--layer-height Layer height in mm (default: $Slic3r::layer_height) --layer-height Layer height in mm (default: $Slic3r::layer_height)
--high-res-perimeters --infill-every-layers
Print perimeters at half layer height to get surface accuracy Infill every N layers (default: $Slic3r::infill_every_layers)
(default: disabled)
Print options: Print options:
--perimeters Number of perimeters/horizontal skins (range: 1+, --perimeters Number of perimeters/horizontal skins (range: 1+,