New --fill-every-layers option to get high accuracy on external surfaces while speeding up infill
This commit is contained in:
parent
54cc6216a1
commit
7f341cfcd3
19
MANIFEST
19
MANIFEST
@ -1,25 +1,42 @@
|
||||
Build.PL
|
||||
lib/Slic3r.pm
|
||||
lib/Slic3r/Config.pm
|
||||
lib/Slic3r/ExPolygon.pm
|
||||
lib/Slic3r/Extruder.pm
|
||||
lib/Slic3r/ExtrusionLoop.pm
|
||||
lib/Slic3r/ExtrusionPath.pm
|
||||
lib/Slic3r/ExtrusionPath/Collection.pm
|
||||
lib/Slic3r/Fill.pm
|
||||
lib/Slic3r/Fill/Base.pm
|
||||
lib/Slic3r/Fill/Rectilinear.pm
|
||||
lib/Slic3r/Fill/Rectilinear2.pm
|
||||
lib/Slic3r/Geometry.pm
|
||||
lib/Slic3r/Geometry/Clipper.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/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/Point.pm
|
||||
lib/Slic3r/Polygon.pm
|
||||
lib/Slic3r/Polyline.pm
|
||||
lib/Slic3r/Polyline/Closed.pm
|
||||
lib/Slic3r/Print.pm
|
||||
lib/Slic3r/Skein.pm
|
||||
lib/Slic3r/STL.pm
|
||||
lib/Slic3r/Surface.pm
|
||||
lib/Slic3r/Surface/Collection.pm
|
||||
lib/Slic3r/Surface/Bridge.pm
|
||||
lib/Slic3r/SVG.pm
|
||||
MANIFEST This list of files
|
||||
README.markdown
|
||||
slic3r.pl
|
||||
t/clean_polylines.t
|
||||
t/clipper.t
|
||||
t/geometry.t
|
||||
t/polyclip.t
|
||||
t/stl.t
|
||||
|
@ -40,7 +40,7 @@ Slic3r current features are:
|
||||
* retraction;
|
||||
* skirt (with rounded corners);
|
||||
* 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;
|
||||
* save configuration profiles;
|
||||
* center print around bed center point;
|
||||
@ -53,6 +53,7 @@ Roadmap includes the following goals:
|
||||
|
||||
* output some statistics;
|
||||
* support material for internal perimeters;
|
||||
* new and better GUI;
|
||||
* cool;
|
||||
* other fill patterns.
|
||||
|
||||
@ -111,9 +112,8 @@ The author is Alessandro Ranellucci (me).
|
||||
|
||||
Accuracy options:
|
||||
--layer-height Layer height in mm (default: 0.4)
|
||||
--high-res-perimeters
|
||||
Print perimeters at half layer height to get surface accuracy
|
||||
(default: disabled)
|
||||
--infill-every-layers
|
||||
Infill every N layers (default: 1)
|
||||
|
||||
Print options:
|
||||
--perimeters Number of perimeters/horizontal skins (range: 1+,
|
||||
|
@ -50,7 +50,7 @@ our $bottom_layer_speed_ratio = 0.3;
|
||||
# accuracy options
|
||||
our $resolution = 0.00000001;
|
||||
our $layer_height = 0.4;
|
||||
our $high_res_perimeters = 0;
|
||||
our $infill_every_layers = 1;
|
||||
our $thickness_ratio = 1;
|
||||
our $flow_width;
|
||||
|
||||
|
@ -64,9 +64,9 @@ our $Options = {
|
||||
label => 'Layer height (mm)',
|
||||
type => 'f',
|
||||
},
|
||||
'high_res_perimeters' => {
|
||||
label => 'High-res perimeters',
|
||||
type => 'bool',
|
||||
'infill_every_layers' => {
|
||||
label => 'Infill every N layers',
|
||||
type => 'i',
|
||||
},
|
||||
|
||||
# print options
|
||||
@ -262,6 +262,12 @@ sub validate {
|
||||
die "Invalid value for --fill-density\n"
|
||||
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
|
||||
die "Invalid value for --scale\n"
|
||||
if $Slic3r::scale <= 0;
|
||||
|
@ -84,7 +84,7 @@ sub extrude {
|
||||
|
||||
# compensate retraction
|
||||
$gcode .= $self->unretract if $self->retracted;
|
||||
|
||||
XXX "yes!\n" if $path->depth_layers > 1;
|
||||
# extrude while going to next points
|
||||
foreach my $line ($path->lines) {
|
||||
# calculate how much filament to drive into the extruder
|
||||
@ -93,7 +93,8 @@ sub extrude {
|
||||
* (($Slic3r::nozzle_diameter**2) / ($Slic3r::filament_diameter ** 2))
|
||||
* $Slic3r::thickness_ratio
|
||||
* $self->flow_ratio
|
||||
* $Slic3r::filament_packing_density;
|
||||
* $Slic3r::filament_packing_density
|
||||
* $path->depth_layers;
|
||||
|
||||
$gcode .= $self->G1($line->b, undef, $e, $description);
|
||||
}
|
||||
|
@ -3,6 +3,10 @@ use Moo;
|
||||
|
||||
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);
|
||||
|
||||
sub clip_end {
|
||||
|
@ -48,14 +48,20 @@ sub make_fill {
|
||||
$filler = 'rectilinear';
|
||||
}
|
||||
|
||||
push @path_collection, $self->fillers->{$filler}->fill_surface($surface,
|
||||
my @paths = $self->fillers->{$filler}->fill_surface(
|
||||
$surface,
|
||||
density => $density,
|
||||
);
|
||||
|
||||
push @path_collection, map Slic3r::ExtrusionPath->cast(
|
||||
[ @$_ ],
|
||||
depth_layers => $surface->depth_layers,
|
||||
), @paths;
|
||||
}
|
||||
|
||||
# save into layer
|
||||
push @{ $layer->fills }, Slic3r::ExtrusionPath::Collection->new(
|
||||
paths => [ map Slic3r::ExtrusionPath->cast([ @$_ ]), @path_collection ],
|
||||
paths => [ @path_collection ],
|
||||
);
|
||||
$layer->fills->[-1]->cleanup;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ sub infill_direction {
|
||||
@shift = @{$rotate[1]};
|
||||
|
||||
# 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;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ sub new {
|
||||
),
|
||||
accuracy => Slic3r::GUI::OptionsGroup->new($self,
|
||||
title => 'Accuracy',
|
||||
options => [qw(layer_height high_res_perimeters)],
|
||||
options => [qw(layer_height infill_every_layers)],
|
||||
),
|
||||
print => Slic3r::GUI::OptionsGroup->new($self,
|
||||
title => 'Print settings',
|
||||
|
@ -4,11 +4,27 @@ use warnings;
|
||||
|
||||
require 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';
|
||||
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 {
|
||||
my ($subject, $clip) = @_;
|
||||
|
||||
@ -29,4 +45,13 @@ sub union_ex {
|
||||
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;
|
||||
|
@ -4,7 +4,7 @@ use Moo;
|
||||
use Math::Clipper ':all';
|
||||
use Slic3r::Geometry qw(polygon_lines points_coincide angle3points polyline_lines nearest_point
|
||||
line_length);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex);
|
||||
use Slic3r::Geometry::Clipper qw(safety_offset union_ex);
|
||||
use XXX;
|
||||
|
||||
use constant PI => 4 * atan2(1, 1);
|
||||
@ -184,7 +184,7 @@ sub make_surfaces {
|
||||
#) if !$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;
|
||||
|
||||
my @ordered_next_lines = sort
|
||||
@ -284,10 +284,7 @@ sub process_bridges {
|
||||
# offset the surface a bit to avoid approximation issues when doing the
|
||||
# intersection below (this is to make sure we overlap with supporting
|
||||
# surfaces, otherwise a little gap will result from intersection)
|
||||
{
|
||||
my $offset = offset([$surface_p], 100, 100, JT_MITER, 2);
|
||||
$surface_p = $offset->[0];
|
||||
}
|
||||
$surface_p = safety_offset([$surface_p])->[0];
|
||||
|
||||
#use Slic3r::SVG;
|
||||
#Slic3r::SVG::output(undef, "bridge.svg",
|
||||
|
@ -22,10 +22,10 @@ sub id {
|
||||
|
||||
sub cast {
|
||||
my $class = shift;
|
||||
my ($points) = @_;
|
||||
my ($points, %args) = @_;
|
||||
|
||||
$points = [ map { ref $_ eq 'ARRAY' ? Slic3r::Point->new($_) : $_ } @$points ];
|
||||
return $class->new(points => $points);
|
||||
return $class->new(points => $points, %args);
|
||||
}
|
||||
|
||||
sub lines {
|
||||
|
@ -2,7 +2,7 @@ package Slic3r::Print;
|
||||
use Moo;
|
||||
|
||||
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 constant X => 0;
|
||||
@ -145,7 +145,7 @@ sub detect_surfaces_type {
|
||||
|
||||
# okay, this is an Ugly Hack(tm) to avoid floating point math problems
|
||||
# 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] };
|
||||
}
|
||||
|
||||
@ -301,6 +301,91 @@ sub split_bridges_fills {
|
||||
$_->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 {
|
||||
my $self = shift;
|
||||
|
||||
@ -345,19 +430,6 @@ sub export_gcode {
|
||||
|
||||
# write gcode commands layer by layer
|
||||
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
|
||||
printf $fh $extruder->move_z($Slic3r::z_offset + $layer->z * $Slic3r::resolution);
|
||||
|
||||
|
@ -47,6 +47,9 @@ sub go {
|
||||
# they will be split in internal and internal-solid surfaces
|
||||
$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
|
||||
$print->extrude_fills;
|
||||
|
||||
|
@ -19,6 +19,9 @@ has 'surface_type' => (
|
||||
#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 {
|
||||
my $class = shift;
|
||||
my ($polygon, %args) = @_;
|
||||
@ -34,6 +37,7 @@ sub cast_from_expolygon {
|
||||
my ($expolygon, %args) = @_;
|
||||
|
||||
if (ref $expolygon ne 'HASH') {
|
||||
use XXX; ZZZ $expolygon if ref $expolygon eq 'ARRAY';
|
||||
$expolygon = $expolygon->clipper_expolygon;
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ GetOptions(
|
||||
|
||||
# accuracy options
|
||||
'layer-height=f' => \$Slic3r::layer_height,
|
||||
'high-res-perimeters' => \$Slic3r::high_res_perimeters,
|
||||
'infill-every-layers=i' => \$Slic3r::infill_every_layers,
|
||||
|
||||
# print options
|
||||
'perimeters=i' => \$Slic3r::perimeter_offsets,
|
||||
@ -147,9 +147,8 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
|
||||
|
||||
Accuracy options:
|
||||
--layer-height Layer height in mm (default: $Slic3r::layer_height)
|
||||
--high-res-perimeters
|
||||
Print perimeters at half layer height to get surface accuracy
|
||||
(default: disabled)
|
||||
--infill-every-layers
|
||||
Infill every N layers (default: $Slic3r::infill_every_layers)
|
||||
|
||||
Print options:
|
||||
--perimeters Number of perimeters/horizontal skins (range: 1+,
|
||||
|
Loading…
Reference in New Issue
Block a user