diff --git a/README.markdown b/README.markdown index ccbc26f1..646f262f 100644 --- a/README.markdown +++ b/README.markdown @@ -37,6 +37,7 @@ Slic3r current key features are: * multiple solid layers near horizontal external surfaces; * ability to scale, rotate and duplicate input object; * customizable initial and final GCODE; +* support material; * use different speed for bottom layer and perimeters. Experimental features include: @@ -150,6 +151,7 @@ The author is Alessandro Ranellucci (me). --end-gcode Load final gcode from the supplied file. This will overwrite the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). + --support-material Generate support material for overhangs Retraction options: --retract-length Length of retraction in mm when pausing extrusion diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index ad09842f..973537c7 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -95,6 +95,7 @@ our $fill_pattern = 'rectilinear'; our $solid_fill_pattern = 'rectilinear'; our $fill_density = 0.4; # 1 = 100% our $fill_angle = 45; +our $support_material = 0; our $start_gcode = "G28 ; home all axes"; our $end_gcode = <<"END"; M104 S0 ; turn off temperature diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b46bcd34..6e8579a9 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -215,6 +215,11 @@ our $Options = { cli => 'fill-angle=i', type => 'i', }, + 'support_material' => { + label => 'Generate support material', + cli => 'support-material', + type => 'bool', + }, 'start_gcode' => { label => 'Start GCODE', cli => 'start-gcode=s', diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 25381011..59efadf9 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -149,7 +149,7 @@ sub extrude_path { # extrude arc or line $self->print_feed_rate( - $path->role =~ /^(perimeter|skirt)$/o ? $self->perimeter_speed + $path->role =~ /^(perimeter|skirt|support-material)$/o ? $self->perimeter_speed : $path->role eq 'small-perimeter' ? $self->small_perimeter_speed : $path->role eq 'fill' ? $self->infill_speed : $path->role eq 'solid-fill' ? $self->solid_infill_speed diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index ed91651b..e0095474 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -44,4 +44,9 @@ sub split_at { ); } +sub split_at_first_point { + my $self = shift; + return $self->split_at($self->polygon->[0]); +} + 1; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 4f8a5b4c..fca81338 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -17,7 +17,7 @@ has 'depth_layers' => (is => 'ro', default => sub {1}); has 'flow_spacing' => (is => 'rw'); -# perimeter/fill/solid-fill/bridge/skirt +# perimeter/fill/solid-fill/bridge/skirt/support-material has 'role' => (is => 'rw', required => 1); sub BUILD { @@ -45,6 +45,22 @@ sub clip_end { } } +sub clip_with_expolygon { + my $self = shift; + my ($expolygon) = @_; + + my @paths = (); + foreach my $polyline ($self->polyline->clip_with_expolygon($expolygon)) { + push @paths, (ref $self)->new( + polyline => $polyline, + depth_layers => $self->depth_layers, + flow_spacing => $self->flow_spacing, + role => $self->role, + ); + } + return @paths; +} + sub points { my $self = shift; return $self->polyline; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 63c3174a..e03a5053 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -34,17 +34,16 @@ sub BUILD { my $self = shift; $self->fillers->{$_} ||= $FillTypes{$_}->new(print => $self->print) for ('rectilinear', $Slic3r::fill_pattern, $Slic3r::solid_fill_pattern); + + my $max_print_dimension = $self->print->max_length * sqrt(2); + $_->max_print_dimension($max_print_dimension) for values %{$self->fillers}; } sub make_fill { my $self = shift; my ($layer) = @_; - my $max_print_dimension = $self->print->max_length * sqrt(2); - for (values %{$self->fillers}) { - $_->layer($layer); - $_->max_print_dimension($max_print_dimension); - } + $_->layer($layer) for values %{$self->fillers}; printf "Filling layer %d:\n", $layer->id; diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 5dc47d8f..63e3cedd 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -18,13 +18,15 @@ sub infill_direction { $rotate[1] = [ $self->max_print_dimension * sqrt(2) / 2, $self->max_print_dimension * sqrt(2) / 2 ]; @shift = @{$rotate[1]}; - # alternate fill direction - if (($self->layer->id / $surface->depth_layers) % 2) { - $rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2; + if ($self->layer) { + # alternate fill direction + if (($self->layer->id / $surface->depth_layers) % 2) { + $rotate[0] = Slic3r::Geometry::deg2rad($Slic3r::fill_angle) + PI/2; + } } - + # use bridge angle - if ($surface->surface_type eq 'bottom' && $self->layer->id > 0 && defined $surface->bridge_angle) { + if (defined $surface->bridge_angle) { Slic3r::debugf "Filling bridge with angle %d\n", $surface->bridge_angle; $rotate[0] = Slic3r::Geometry::deg2rad($surface->bridge_angle); } diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 9b6fb560..7adce89a 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -66,10 +66,16 @@ has 'skirts' => ( default => sub { [] }, ); +# ordered collection of extrusion paths to fill surfaces for support material +has 'support_fills' => ( + is => 'rw', + #isa => 'Slic3r::ExtrusionPath::Collection', +); + # ordered collection of extrusion paths to fill surfaces has 'fills' => ( is => 'rw', - #isa => 'ArrayRef[Slic3r::ExtrusionPath]', + #isa => 'ArrayRef[Slic3r::ExtrusionPath::Collection]', default => sub { [] }, ); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8042b752..b1f8f228 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -342,6 +342,7 @@ sub extrude_skirt { my @points = ( (map @$_, map @{$_->expolygon}, map @{$_->slices}, @layers), (map @$_, map @{$_->thin_walls}, @layers), + (map @{$_->polyline}, map @{$_->support_fills->paths}, grep $_->support_fills, @layers), ); return if !@points; @@ -451,6 +452,74 @@ sub infill_every_layers { } } +sub generate_support_material { + my $self = shift; + + # generate paths for the pattern that we're going to use + my $support_pattern = []; + { + # get all surfaces needing support material + my @surfaces = grep $_->surface_type eq 'bottom' && !defined $_->bridge_angle, + map @{$_->slices}, grep $_->id > 0, @{$self->layers} or return; + + my @support_material_areas = @{union_ex([ map $_->p, @surfaces ])}; + + for (1..$Slic3r::perimeters+1) { + foreach my $expolygon (@support_material_areas) { + push @$support_pattern, + map Slic3r::ExtrusionLoop->new( + polygon => $_, + role => 'support-material', + )->split_at_first_point, @$expolygon; + } + @support_material_areas = map $_->offset_ex(- scale $Slic3r::flow_spacing), + @support_material_areas; + } + + my $fill = Slic3r::Fill->new(print => $self); + foreach my $expolygon (@support_material_areas) { + my @paths = $fill->fillers->{rectilinear}->fill_surface( + Slic3r::Surface->new( + expolygon => $expolygon, + bridge_angle => $Slic3r::fill_angle + 45, + ), + density => 0.15, + flow_spacing => $Slic3r::flow_spacing, + ); + my $params = shift @paths; + + push @$support_pattern, + map Slic3r::ExtrusionPath->new( + polyline => Slic3r::Polyline->new(@$_), + role => 'support-material', + depth_layers => 1, + flow_spacing => $params->{flow_spacing}, + ), @paths; + } + } + + # now apply the pattern to layers below unsupported surfaces + my (@a, @b) = (); + for (my $i = $#{$self->layers}; $i >=0; $i--) { + my $layer = $self->layers->[$i]; + my @c = (); + if (@b) { + @c = @{diff_ex( + [ map @$_, @b ], + [ map @$_, map $_->expolygon->offset_ex(scale $Slic3r::flow_width), @{$layer->slices} ], + )}; + $layer->support_fills(Slic3r::ExtrusionPath::Collection->new); + foreach my $expolygon (@c) { + push @{$layer->support_fills->paths}, map $_->clip_with_expolygon($expolygon), @$support_pattern; + } + } + @b = @{union_ex([ map @$_, @c, @a ])}; + @a = map $_->expolygon->offset_ex(scale 2), + grep $_->surface_type eq 'bottom' && !defined $_->bridge_angle, + @{$layer->slices}; + } +} + sub export_gcode { my $self = shift; my ($file) = @_; @@ -522,6 +591,12 @@ sub export_gcode { print $fh $extruder->extrude_path($_, 'fill') for $fill->shortest_path($extruder->last_pos); } + + # extrude support material + if ($layer->support_fills) { + print $fh $extruder->extrude_path($_, 'support material') + for $layer->support_fills->shortest_path($extruder->last_pos); + } } } diff --git a/lib/Slic3r/Skein.pm b/lib/Slic3r/Skein.pm index 7719274e..0afd87a8 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -35,10 +35,6 @@ sub go { $print = Slic3r::Print->new_from_mesh($mesh); } - # make skirt - $self->status_cb->(15, "Generating skirt..."); - $print->extrude_skirt; - # make perimeters # this will add a set of extrusion loops to each layer # as well as generate infill boundaries @@ -118,6 +114,16 @@ sub go { # free memory @{$_->fill_surfaces} = () for @{$print->layers}; + # generate support material + if ($Slic3r::support_material) { + $self->status_cb->(85, "Generating support material..."); + $print->generate_support_material; + } + + # make skirt + $self->status_cb->(88, "Generating skirt..."); + $print->extrude_skirt; + # output everything to a GCODE file $self->status_cb->(90, "Exporting GCODE..."); $print->export_gcode($self->expanded_output_filepath); diff --git a/slic3r.pl b/slic3r.pl index e1366e79..7d534c02 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -167,6 +167,7 @@ Usage: slic3r.pl [ OPTIONS ] file.stl --end-gcode Load final gcode from the supplied file. This will overwrite the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). + --support-material Generate support material for overhangs Retraction options: --retract-length Length of retraction in mm when pausing extrusion