diff --git a/README.markdown b/README.markdown index daaafff56..ad65a7561 100644 --- a/README.markdown +++ b/README.markdown @@ -31,17 +31,18 @@ Slic3r current features are: * generate multiple perimeters (skins); * generate rectilinear fill (100% solid for external surfaces or with customizable less density for inner surfaces); * retraction; -* skirt; +* skirt (with rounded corners); * use relative or absolute extrusion commands; * center print around bed center point; +* multiple solid layers near horizontal external surfaces; * use different speed for bottom layer. Roadmap includes the following goals: * output some statistics; * allow the user to customize initial and final GCODE commands; -* option for filling multiple solid layers near external surfaces; * support material for internal perimeters; +* travel path optimization; * ability to infill in the direction of bridges; * input object transform (scale, rotate, multiply); * cool; diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 8ddb90085..3185c24d8 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -42,6 +42,7 @@ our $flow_width; # print options our $perimeter_offsets = 3; +our $solid_layers = 3; our $fill_density = 0.4; # 1 = 100% our $temperature = 195; @@ -51,7 +52,7 @@ our $retract_restart_extra = 0; # mm our $retract_speed = 40; # mm/sec # skirt options -our $skirts = 3; +our $skirts = 1; our $skirt_distance = 6; # mm 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 160a3785f..dc9f7ac73 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -1,6 +1,9 @@ package Slic3r::Print; use Moo; +use Math::Clipper ':all'; +use XXX; + use constant PI => 4 * atan2(1, 1); use constant X => 0; use constant Y => 1; @@ -49,6 +52,76 @@ sub layer { return $self->layers->[$layer_id]; } +sub discover_horizontal_shells { + my $self = shift; + + Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n"; + + my $clipper = Math::Clipper->new; + + for (my $i = 0; $i < $self->layer_count; $i++) { + my $layer = $self->layers->[$i]; + foreach my $type (qw(top bottom)) { + # find surfaces of current type for current layer + my @surfaces = grep $_->surface_type eq $type, @{$layer->surfaces} or next; + Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", + $i, scalar(@surfaces), $type; + + for (my $n = $type eq 'top' ? $i-1 : $i+1; + abs($n - $i) <= $Slic3r::solid_layers-1; + $type eq 'top' ? $n-- : $n++) { + + next if $n < 0 || $n >= $self->layer_count; + Slic3r::debugf " looking for neighbors on layer %d...\n", $n; + + my $neighbor_polygons = [ map $_->p, grep $_->surface_type eq 'internal', @{$self->layers->[$n]->surfaces} ]; + # find intersection between @surfaces and current layer's surfaces + $clipper->add_subject_polygons([ map $_->p, @surfaces ]); + $clipper->add_clip_polygons($neighbor_polygons); + + # intersections have contours and holes + my $intersections = $clipper->ex_execute(CT_INTERSECTION, PFT_NONZERO, PFT_NONZERO); + $clipper->clear; + next if @$intersections == 0; + Slic3r::debugf " %d intersections found\n", scalar @$intersections; + + # subtract intersections from layer surfaces to get resulting inner surfaces + $clipper->add_subject_polygons($neighbor_polygons); + $clipper->add_clip_polygons([ map { $_->{outer}, @{$_->{holes}} } @$intersections ]); + my $internal_polygons = $clipper->ex_execute(CT_DIFFERENCE, PFT_NONZERO, PFT_NONZERO); + $clipper->clear; + + # Note: due to floating point math we're going to get some very small + # polygons as $internal_polygons; they should be discarded, but a reliable + # way to detect them is needed, and they seem to be harmless so we keep them for now + + # assign resulting inner surfaces to layer + $self->layers->[$n]->surfaces([]); + foreach my $p (@$internal_polygons) { + push @{$self->layers->[$n]->surfaces}, Slic3r::Surface->new( + surface_type => 'internal', + contour => Slic3r::Polyline::Closed->cast($p->{outer}), + holes => [ + map Slic3r::Polyline::Closed->cast($_), @{$p->{holes}} + ], + ); + } + + # assign new internal-solid surfaces to layer + foreach my $p (@$intersections) { + push @{$self->layers->[$n]->surfaces}, Slic3r::Surface->new( + surface_type => 'internal-solid', + contour => Slic3r::Polyline::Closed->cast($p->{outer}), + holes => [ + map Slic3r::Polyline::Closed->cast($_), @{$p->{holes}} + ], + ); + } + } + } + } +} + sub extrude_perimeters { my $self = shift; diff --git a/lib/Slic3r/STL.pm b/lib/Slic3r/STL.pm index 590b0bb02..b6ce3f607 100644 --- a/lib/Slic3r/STL.pm +++ b/lib/Slic3r/STL.pm @@ -67,6 +67,9 @@ sub parse_file { $layer->merge_contiguous_surfaces; } + # detect which surfaces are near external layers + $print->discover_horizontal_shells; + return $print; } diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index b7c7e5199..67a4f9141 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -8,7 +8,7 @@ use constant X => 0; use constant Y => 1; sub factor { - return $Slic3r::resolution * 10; + return $Slic3r::resolution * 100; } sub svg { @@ -24,6 +24,7 @@ sub output_polygons { my $g = $svg->group( style => { 'stroke-width' => 2, + 'stroke' => 'black', }, ); foreach my $polygon (@$polygons) { diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 925c40fed..7e99f3f24 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -16,11 +16,9 @@ has 'holes' => ( default => sub { [] }, ); -# TODO: to allow for multiple solid skins to be filled near external -# surfaces, a new type should be defined: internal-solid has 'surface_type' => ( is => 'rw', - #isa => enum([qw(internal bottom top)]), + #isa => enum([qw(internal internal-solid bottom top)]), ); sub add_hole { @@ -78,6 +76,11 @@ sub clipper_polygon { }; } +sub p { + my $self = shift; + return ($self->contour->p, map $_->p, @{$self->holes}); +} + sub lines { my $self = shift; return @{ $self->contour->lines }, map @{ $_->lines }, @{ $self->holes }; diff --git a/slic3r.pl b/slic3r.pl index 8a816e592..65b25b655 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -39,6 +39,7 @@ GetOptions( # print options 'perimeters=i' => \$Slic3r::perimeter_offsets, + 'solid-layers=i' => \$Slic3r::solid_layers, 'fill-density=f' => \$Slic3r::fill_density, 'temperature=i' => \$Slic3r::temperature, @@ -75,6 +76,10 @@ GetOptions( die "Invalid value for --perimeters\n" if $Slic3r::perimeter_offsets < 1; + # --solid-layers + die "Invalid value for --solid-layers\n" + if $Slic3r::solid_layers < 1; + # --print-center die "Invalid value for --print-center\n" if !ref $Slic3r::print_center @@ -143,6 +148,8 @@ Usage: slic3r.pl [ OPTIONS ] file.stl Print options: --perimeters Number of perimeters/horizontal skins (range: 1+, default: $Slic3r::perimeter_offsets) + --solid-layers Number of solid layers to do for top/bottom surfaces + (range: 1+, default: $Slic3r::solid_layers) --fill-density Infill density (range: 0-1, default: $Slic3r::fill_density) --temperature Extrusion temperature (default: $Slic3r::temperature)