From 0eadc5adbae29c1b5cf55c73e0e6970655d5702d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Aug 2012 15:42:58 +0200 Subject: [PATCH 001/114] Manual rebase of the avoid_crossing_perimeters feature --- MANIFEST | 1 + README.markdown | 1 + lib/Slic3r.pm | 1 + lib/Slic3r/Config.pm | 7 + lib/Slic3r/ExPolygon.pm | 3 + lib/Slic3r/GCode.pm | 43 ++++- lib/Slic3r/GCode/MotionPlanner.pm | 259 ++++++++++++++++++++++++++++++ lib/Slic3r/GUI/Tab.pm | 4 + lib/Slic3r/Point.pm | 2 + lib/Slic3r/Polygon.pm | 5 + lib/Slic3r/Print.pm | 46 +++++- slic3r.pl | 1 + 12 files changed, 362 insertions(+), 11 deletions(-) create mode 100644 lib/Slic3r/GCode/MotionPlanner.pm diff --git a/MANIFEST b/MANIFEST index 19de724981..b021f8d85f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -24,6 +24,7 @@ lib/Slic3r/Format/AMF/Parser.pm lib/Slic3r/Format/OBJ.pm lib/Slic3r/Format/STL.pm lib/Slic3r/GCode.pm +lib/Slic3r/GCode/MotionPlanner.pm lib/Slic3r/Geometry.pm lib/Slic3r/Geometry/Clipper.pm lib/Slic3r/GUI.pm diff --git a/README.markdown b/README.markdown index fc5ce7f864..0e1d72c728 100644 --- a/README.markdown +++ b/README.markdown @@ -170,6 +170,7 @@ The author of the Silk icon set is Mark James. --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) + --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) Support material options: --support-material Generate support material for overhangs diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index dc97761213..72a088702a 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -38,6 +38,7 @@ use Slic3r::Format::AMF; use Slic3r::Format::OBJ; use Slic3r::Format::STL; use Slic3r::GCode; +use Slic3r::GCode::MotionPlanner; use Slic3r::Geometry qw(PI); use Slic3r::Layer; use Slic3r::Line; diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 7af58122cd..de64fc2742 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -462,6 +462,13 @@ our $Options = { type => 'bool', default => 1, }, + 'avoid_crossing_perimeters' => { + label => 'Avoid crossing perimeters', + tooltip => 'Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down the processing times.', + cli => 'avoid-crossing-perimeters!', + type => 'bool', + default => 0, + }, 'support_material' => { label => 'Generate support material', tooltip => 'Enable support material generation.', diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 7037185519..717fc1d53c 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -155,16 +155,19 @@ sub clip_line { sub simplify { my $self = shift; $_->simplify(@_) for @$self; + $self; } sub translate { my $self = shift; $_->translate(@_) for @$self; + $self; } sub rotate { my $self = shift; $_->rotate(@_) for @$self; + $self; } sub area { diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b5e020ace0..273753d412 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -2,7 +2,8 @@ package Slic3r::GCode; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale); +use Slic3r::Geometry qw(PI X Y scale unscale points_coincide); +use Slic3r::Geometry::Clipper qw(union_ex); has 'layer' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); @@ -10,6 +11,8 @@ has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw', default => sub {0} ); has 'speed' => (is => 'rw'); +has 'motionplanner' => (is => 'rw'); +has 'straight_once' => (is => 'rw'); has 'extruder_idx' => (is => 'rw'); has 'extrusion_distance' => (is => 'rw', default => sub {0} ); has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds @@ -48,18 +51,35 @@ my %role_speeds = ( &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', ); -use Slic3r::Geometry qw(points_coincide PI X Y); - sub extruder { my $self = shift; return $Slic3r::extruders->[$self->extruder_idx]; } +sub set_shift { + my $self = shift; + my @shift = @_; + + # adjust last position + $self->last_pos($self->last_pos->clone->translate( + scale($self->shift_x - $shift[X]), + scale($self->shift_y - $shift[Y]), + )); + + $self->shift_x($shift[X]); + $self->shift_y($shift[Y]); +} + sub change_layer { my $self = shift; my ($layer) = @_; $self->layer($layer); + if ($Slic3r::Config->avoid_crossing_perimeters) { + $self->motionplanner(Slic3r::GCode::MotionPlanner->new( + islands => union_ex([ map @{$_->expolygon}, @{$layer->slices} ], undef, 1), + )); + } my $z = $Slic3r::Config->z_offset + $layer->print_z * &Slic3r::SCALING_FACTOR; my $gcode = ""; @@ -145,8 +165,7 @@ sub extrude_path { } # go to first point of extrusion path - $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") - if !points_coincide($self->last_pos, $path->points->[0]); + $gcode .= $self->travel_to($path->points->[0], "move to first $description point"); # compensate retraction $gcode .= $self->unretract if $self->extruder->retracted; @@ -192,6 +211,20 @@ sub extrude_path { return $gcode; } +sub travel_to { + my $self = shift; + my ($point, $comment) = @_; + + return "" if points_coincide($self->last_pos, $point); + if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { + return join '', map $self->G0($_->b, undef, 0, $comment || ""), + $self->motionplanner->shortest_path($self->last_pos, $point)->lines; + } else { + $self->straight_once(0); + return $self->G0($point, undef, 0, $comment || ""); + } +} + sub retract { my $self = shift; my %params = @_; diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm new file mode 100644 index 0000000000..cf0d97a2d0 --- /dev/null +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -0,0 +1,259 @@ +package Slic3r::GCode::MotionPlanner; +use Moo; + +has 'islands' => (is => 'ro', required => 1); +has 'no_internal' => (is => 'ro'); +has 'last_crossings'=> (is => 'rw'); +has '_inner' => (is => 'rw', default => sub { [] }); # arrayref of arrayrefs of expolygons +has '_outer' => (is => 'rw', default => sub { [] }); # arrayref of arrayrefs of polygons +has '_contours_ex' => (is => 'rw', default => sub { [] }); # arrayref of arrayrefs of expolygons +has '_pointmap' => (is => 'rw', default => sub { {} }); # { id => $point } +has '_edges' => (is => 'rw', default => sub { {} }); # node_idx => { node_idx => distance, ... } +has '_crossing_edges' => (is => 'rw', default => sub { {} }); # edge_idx => bool + +use List::Util qw(first); +use Slic3r::Geometry qw(scale epsilon nearest_point); +use Slic3r::Geometry::Clipper qw(diff_ex JT_MITER); + +# clearance (in mm) from the perimeters +has '_inner_margin' => (is => 'ro', default => sub { scale 0.5 }); +has '_outer_margin' => (is => 'ro', default => sub { scale 2 }); + +# this factor weigths the crossing of a perimeter +# vs. the alternative path. a value of 5 means that +# a perimeter will be crossed if the alternative path +# is >= 5x the length of the straight line we could +# follow if we decided to cross the perimeter. +# a nearly-infinite value for this will only permit +# perimeter crossing when there's no alternative path. +use constant CROSSING_FACTOR => 20; + +use constant INFINITY => 'inf'; + +# setup our configuration space +sub BUILD { + my $self = shift; + + my $edges = $self->_edges; + my $crossing_edges = $self->_crossing_edges; + my $tolerance = scale epsilon; + + my $add_expolygon = sub { + my ($expolygon, $crosses_perimeter) = @_; + my @points = map @$_, @$expolygon; + for my $i (0 .. $#points) { + for my $j (($i+1) .. $#points) { + my $line = Slic3r::Line->new($points[$i], $points[$j]); + if ($expolygon->encloses_line($line, scale Slic3r::Geometry::epsilon)) { + my $dist = $line->length * ($crosses_perimeter ? CROSSING_FACTOR : 1); + $edges->{$points[$i]}{$points[$j]} = $dist; + $edges->{$points[$j]}{$points[$i]} = $dist; + $crossing_edges->{$points[$i]}{$points[$j]} = 1; + $crossing_edges->{$points[$j]}{$points[$i]} = 1; + } + } + } + }; + + for my $i (0 .. $#{$self->islands}) { + $self->islands->[$i]->simplify($self->_inner_margin); + $self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ] + if !$self->no_internal; + $self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ]; + $_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]}; + + if (!$self->no_internal) { + $self->_contours_ex->[$i] = diff_ex( + $self->_outer->[$i], + [ map $_->contour, @{$self->_inner->[$i]} ], + ); + + # lines enclosed in inner expolygons are visible + $add_expolygon->($_) for @{ $self->_inner->[$i] }; + + # lines enclosed in expolygons covering perimeters are visible + # (but discouraged) + $add_expolygon->($_, 1) for @{ $self->_contours_ex->[$i] }; + } + } + + my $intersects = sub { + my ($polygon, $line) = @_; + @{Boost::Geometry::Utils::polygon_linestring_intersection( + $polygon->boost_polygon, + Boost::Geometry::Utils::linestring($line), + )} > 0; + }; + + # lines connecting outer polygons are visible + { + my @outer = (map @$_, @{$self->_outer}); + for my $i (0 .. $#outer) { + for my $j (($i+1) .. $#outer) { + for my $m (0 .. $#{$outer[$i]}) { + for my $n (0 .. $#{$outer[$j]}) { + my $line = Slic3r::Line->new($outer[$i][$m], $outer[$j][$n]); + if (!first { $intersects->($_, $line) } @outer) { + # this line does not cross any polygon + my $dist = $line->length; + $edges->{$outer[$i][$m]}{$outer[$j][$n]} = $dist; + $edges->{$outer[$j][$n]}{$outer[$i][$m]} = $dist; + } + } + } + } + } + } + + # lines connecting inner polygons contours are visible but discouraged + if (!$self->no_internal) { + my @inner = (map $_->contour, map @$_, @{$self->_inner}); + for my $i (0 .. $#inner) { + for my $j (($i+1) .. $#inner) { + for my $m (0 .. $#{$inner[$i]}) { + for my $n (0 .. $#{$inner[$j]}) { + my $line = Slic3r::Line->new($inner[$i][$m], $inner[$j][$n]); + if (!first { $intersects->($_, $line) } @inner) { + # this line does not cross any polygon + my $dist = $line->length * CROSSING_FACTOR; + $edges->{$inner[$i][$m]}{$inner[$j][$n]} = $dist; + $edges->{$inner[$j][$n]}{$inner[$i][$m]} = $dist; + $crossing_edges->{$inner[$i][$m]}{$inner[$j][$n]} = 1; + $crossing_edges->{$inner[$j][$n]}{$inner[$i][$m]} = 1; + } + } + } + } + } + } + + $self->_pointmap({ + map +("$_" => $_), + (map @$_, map @$_, map @$_, @{$self->_inner}), + (map @$_, map @$_, @{$self->_outer}), + (map @$_, map @$_, map @$_, @{$self->_contours_ex}), + }); + + if (0) { + my @lines = (); + my %lines = (); + for my $i (keys %{$self->_edges}) { + for my $j (keys %{$self->_edges->{$i}}) { + next if $lines{join '_', sort $i, $j}; + push @lines, [ map $self->_pointmap->{$_}, $i, $j ]; + $lines{join '_', sort $i, $j} = 1; + } + } + + require "Slic3r/SVG.pm"; + Slic3r::SVG::output(undef, "space.svg", + lines => \@lines, + no_arrows => 1, + polygons => [ map @$_, @{$self->islands} ], + red_polygons => [ map $_->holes, map @$_, @{$self->_inner} ], + white_polygons => [ map @$_, @{$self->_outer} ], + ); + printf "%d islands\n", scalar @{$self->islands}; + } +} + +sub find_node { + my $self = shift; + my ($point, $near_to) = @_; + + # for optimal pathing, we should check visibility from $point to all $candidates, and then + # choose the one that is nearest to $near_to among the visible ones; however this is probably too slow + + # if we're inside a hole, move to a point on hole; + { + my $polygon = first { $_->encloses_point($point) } (map $_->holes, map @$_, @{$self->_inner}); + return nearest_point($point, $polygon) if $polygon; + } + + # if we're inside an expolygon move to a point on contour or holes + { + my $expolygon = first { $_->encloses_point_quick($point) } (map @$_, @{$self->_inner}); + return nearest_point($point, [ map @$_, @$expolygon ]) if $expolygon; + } + + { + my $outer_polygon_idx; + if (!$self->no_internal) { + # look for an outer expolygon whose contour contains our point + $outer_polygon_idx = first { first { $_->contour->encloses_point($point) } @{$self->_contours_ex->[$_]} } + 0 .. $#{ $self->_contours_ex }; + } else { + # # look for an outer expolygon containing our point + $outer_polygon_idx = first { first { $_->encloses_point($point) } @{$self->_outer->[$_]} } + 0 .. $#{ $self->_outer }; + } + my $candidates = defined $outer_polygon_idx + ? [ map @{$_->contour}, @{$self->_inner->[$outer_polygon_idx]} ] + : [ map @$_, map @$_, @{$self->_outer} ]; + $candidates = [ map @$_, @{$self->_outer->[$outer_polygon_idx]} ] + if @$candidates == 0; + return nearest_point($point, $candidates); + } +} + +sub shortest_path { + my $self = shift; + my ($from, $to) = @_; + + # find nearest nodes + my $new_from = $self->find_node($from, $to); + my $new_to = $self->find_node($to, $from); + + my $root = "$new_from"; + my $target = "$new_to"; + my $edges = $self->_edges; + my %dist = map { $_ => INFINITY } keys %$edges; + $dist{$root} = 0; + my %prev = map { $_ => undef } keys %$edges; + my @unsolved = keys %$edges; + my %crossings = (); # node_idx => bool + + while (@unsolved) { + # sort unsolved by distance from root + # using a sorting option that accounts for infinity + @unsolved = sort { + $dist{$a} eq INFINITY ? +1 : + $dist{$b} eq INFINITY ? -1 : + $dist{$a} <=> $dist{$b}; + } @unsolved; + + # we'll solve the closest node + last if $dist{$unsolved[0]} eq INFINITY; + my $n = shift @unsolved; + + # stop search + last if $n eq $target; + + # now, look at all the nodes connected to n + foreach my $n2 (keys %{$edges->{$n}}) { + # .. and find out if any of their estimated distances + # can be improved if we go through n + if ( ($dist{$n2} eq INFINITY) || ($dist{$n2} > ($dist{$n} + $edges->{$n}{$n2})) ) { + $dist{$n2} = $dist{$n} + $edges->{$n}{$n2}; + $prev{$n2} = $n; + $crossings{$n} = 1 if $self->_crossing_edges->{$n}{$n2}; + } + } + } + + my @points = (); + my $crossings = 0; + { + my $pointmap = $self->_pointmap; + my $u = $target; + while (defined $prev{$u}) { + unshift @points, $pointmap->{$u}; + $crossings++ if $crossings{$u}; + $u = $prev{$u}; + } + } + $self->last_crossings($crossings); + return Slic3r::Polyline->new($from, $new_from, @points, $to); # @points already includes $new_to +} + +1; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 111800e0aa..1e152b9e31 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -392,6 +392,10 @@ sub build { title => 'Horizontal shells', options => [qw(solid_layers)], }, + { + title => 'Advanced', + options => [qw(avoid_crossing_perimeters)], + }, ]); $self->add_options_page('Infill', 'shading.png', optgroups => [ diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index ffa7fa1a87..52720b16af 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -58,12 +58,14 @@ sub rotate { my $self = shift; my ($angle, $center) = @_; @$self = @{ +(Slic3r::Geometry::rotate_points($angle, $center, $self))[0] }; + $self; } sub translate { my $self = shift; my ($x, $y) = @_; @$self = @{ +(Slic3r::Geometry::move_points([$x, $y], $self))[0] }; + $self; } sub x { $_[0]->[0] } diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index fa7ee712d8..224478905b 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -14,6 +14,11 @@ sub lines { return polygon_lines($self); } +sub boost_polygon { + my $self = shift; + return Boost::Geometry::Utils::polygon($self); +} + sub boost_linestring { my $self = shift; return Boost::Geometry::Utils::linestring([@$self, $self->[0]]); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index c5aec01a70..df1fc4c0a1 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -613,9 +613,30 @@ sub write_gcode { $Slic3r::Config->print_center->[Y] - (unscale ($print_bb[Y2] - $print_bb[Y1]) / 2) - unscale $print_bb[Y1], ); + # initialize a motion planner for object-to-object travel moves + my $external_motionplanner; + if ($Slic3r::Config->avoid_crossing_perimeters) { + my $distance_from_objects = 1; + # compute the offsetted convex hull for each object and repeat it for each copy. + my @islands = (); + foreach my $obj_idx (0 .. $#{$self->objects}) { + my @island = Slic3r::ExPolygon->new(convex_hull([ + map @{$_->expolygon->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers}, + ]))->translate(map -$_, @shift)->offset_ex(scale $distance_from_objects); + foreach my $copy (@{$self->copies->[$obj_idx]}) { + push @islands, map $_->clone->translate(@$copy), @island; + } + } + $external_motionplanner = Slic3r::GCode::MotionPlanner->new( + islands => union_ex([ map @$_, @islands ]), + no_internal => 1, + ); + } + # prepare the logic to print one layer my $skirt_done = 0; # count of skirt layers done my $brim_done = 0; + my $last_obj_copy = ""; my $extrude_layer = sub { my ($layer_id, $object_copies) = @_; my $gcode = ""; @@ -635,8 +656,7 @@ sub write_gcode { # extrude skirt if ($skirt_done < $Slic3r::Config->skirt_height) { - $gcodegen->shift_x($shift[X]); - $gcodegen->shift_y($shift[Y]); + $gcodegen->set_shift(@shift); $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); # skip skirt if we have a large brim if ($layer_id < $Slic3r::Config->skirt_height && ($layer_id != 0 || $Slic3r::Config->skirt_distance + (($Slic3r::Config->skirts - 1) * $Slic3r::flow->spacing) > $Slic3r::Config->brim_width)) { @@ -647,8 +667,7 @@ sub write_gcode { # extrude brim if ($layer_id == 0 && !$brim_done) { - $gcodegen->shift_x($shift[X]); - $gcodegen->shift_y($shift[Y]); + $gcodegen->set_shift(@shift); $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; $brim_done = 1; } @@ -661,8 +680,21 @@ sub write_gcode { # won't always trigger the automatic retraction $gcode .= $gcodegen->retract; - $gcodegen->shift_x($shift[X] + unscale $copy->[X]); - $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); + # travel to the first perimeter point using the external motion planner + if ($external_motionplanner && @{ $layer->perimeters } && !$gcodegen->straight_once && $last_obj_copy ne "${obj_idx}_${copy}") { + $gcodegen->set_shift(@shift); + my $layer_mp = $gcodegen->motionplanner; + $gcodegen->motionplanner($external_motionplanner); + my $first_perimeter = $layer->perimeters->[0]->unpack; + my $target = $first_perimeter->polygon->[0]->clone->translate(@$copy); + $gcode .= $gcodegen->travel_to($target, "move to first perimeter point"); + $gcodegen->motionplanner($layer_mp); + } + + $gcodegen->set_shift( + $shift[X] + unscale $copy->[X], + $shift[Y] + unscale $copy->[Y], + ); # extrude perimeters $gcode .= $gcodegen->set_tool($Slic3r::Config->perimeter_extruder-1); @@ -686,6 +718,8 @@ sub write_gcode { $gcode .= $gcodegen->extrude_path($_, 'support material') for $layer->support_fills->shortest_path($gcodegen->last_pos); } + + $last_obj_copy = "${obj_idx}_${copy}"; } return if !$gcode; diff --git a/slic3r.pl b/slic3r.pl index a4f5ca4d19..602ebbb658 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -213,6 +213,7 @@ $j --layer-gcode Load layer-change G-code from the supplied file (default: nothing). --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) + --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) Support material options: --support-material Generate support material for overhangs From 27090f83bde0bc45a6ff7a43fb9070125ecf6c45 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Aug 2012 17:02:50 +0200 Subject: [PATCH 002/114] Make the motionplanner work again after recent changes to approximation --- lib/Slic3r/ExPolygon.pm | 4 ++-- lib/Slic3r/GCode/MotionPlanner.pm | 22 ++++++++++++++++++---- lib/Slic3r/SVG.pm | 1 - 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 717fc1d53c..70e3f4f97b 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -6,7 +6,7 @@ use warnings; use Boost::Geometry::Utils; use Math::Geometry::Voronoi; -use Slic3r::Geometry qw(X Y A B point_in_polygon same_line); +use Slic3r::Geometry qw(X Y A B point_in_polygon same_line scale epsilon); use Slic3r::Geometry::Clipper qw(union_ex JT_MITER); # the constructor accepts an array of polygons @@ -113,7 +113,7 @@ sub encloses_line { my ($line) = @_; my $clip = $self->clip_line($line); - return @$clip == 1 && same_line($clip->[0], $line); + return @$clip == 1 && abs($line->length - Slic3r::Geometry::line_length($clip->[0])) < scale epsilon; } sub point_on_segment { diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index cf0d97a2d0..b95a38ec61 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -38,6 +38,7 @@ sub BUILD { my $crossing_edges = $self->_crossing_edges; my $tolerance = scale epsilon; + # given an expolygon, this subroutine connects all its visible points my $add_expolygon = sub { my ($expolygon, $crosses_perimeter) = @_; my @points = map @$_, @$expolygon; @@ -55,13 +56,25 @@ sub BUILD { } }; + # process individual islands for my $i (0 .. $#{$self->islands}) { + # simplify the island's contours $self->islands->[$i]->simplify($self->_inner_margin); + + # offset the island inwards to make the boundaries for internal movements + # so that no motion along external perimeters happens $self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ] if !$self->no_internal; + + # offset the island outwards to make the boundaries for external movements $self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ]; + + # further simplification (isn't this a duplication of the one above?) $_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]}; + # if internal motion is enabled, build a set of utility expolygons representing + # the outer boundaries (as contours) and the inner boundaries (as holes). whenever + # we jump from a hole to a contour or viceversa, we know we're crossing a perimeter if (!$self->no_internal) { $self->_contours_ex->[$i] = diff_ex( $self->_outer->[$i], @@ -81,7 +94,7 @@ sub BUILD { my ($polygon, $line) = @_; @{Boost::Geometry::Utils::polygon_linestring_intersection( $polygon->boost_polygon, - Boost::Geometry::Utils::linestring($line), + $line->boost_linestring, )} > 0; }; @@ -148,10 +161,11 @@ sub BUILD { require "Slic3r/SVG.pm"; Slic3r::SVG::output(undef, "space.svg", lines => \@lines, + points => [ values %{$self->_pointmap} ], no_arrows => 1, - polygons => [ map @$_, @{$self->islands} ], - red_polygons => [ map $_->holes, map @$_, @{$self->_inner} ], - white_polygons => [ map @$_, @{$self->_outer} ], + #polygons => [ map @$_, @{$self->islands} ], + #red_polygons => [ map $_->holes, map @$_, @{$self->_inner} ], + #white_polygons => [ map @$_, @{$self->_outer} ], ); printf "%d islands\n", scalar @{$self->islands}; } diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index 1bfa839f66..9b00001e0c 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -13,7 +13,6 @@ sub factor { sub svg { my ($print) = @_; - $print ||= Slic3r::Print->new(x_length => 200 / &Slic3r::SCALING_FACTOR, y_length => 200 / &Slic3r::SCALING_FACTOR); my $svg = SVG->new(width => 200 * 10, height => 200 * 10); my $marker_end = $svg->marker( From c98e9515ed1ec73d220d43e82a3a8cae130aa68d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Aug 2012 21:10:04 +0200 Subject: [PATCH 003/114] Better implementation of the external motion planner --- lib/Slic3r/ExPolygon.pm | 12 +++++++---- lib/Slic3r/GCode.pm | 33 +++++++++++++++++++++++++------ lib/Slic3r/GCode/MotionPlanner.pm | 2 +- lib/Slic3r/Print.pm | 27 ++++++++++--------------- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 70e3f4f97b..5d87f720f9 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -6,7 +6,7 @@ use warnings; use Boost::Geometry::Utils; use Math::Geometry::Voronoi; -use Slic3r::Geometry qw(X Y A B point_in_polygon same_line scale epsilon); +use Slic3r::Geometry qw(X Y A B point_in_polygon same_line line_length scale epsilon); use Slic3r::Geometry::Clipper qw(union_ex JT_MITER); # the constructor accepts an array of polygons @@ -110,10 +110,14 @@ sub encloses_point_quick { sub encloses_line { my $self = shift; - my ($line) = @_; - + my ($line, $tolerance) = @_; my $clip = $self->clip_line($line); - return @$clip == 1 && abs($line->length - Slic3r::Geometry::line_length($clip->[0])) < scale epsilon; + if (!defined $tolerance) { + # optimization + return @$clip == 1 && same_line($clip->[0], $line); + } else { + return @$clip == 1 && abs(line_length($clip->[0]) - $line->length) < $tolerance; + } } sub point_on_segment { diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 273753d412..842cf2b0fc 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -11,8 +11,10 @@ has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw', default => sub {0} ); has 'speed' => (is => 'rw'); -has 'motionplanner' => (is => 'rw'); -has 'straight_once' => (is => 'rw'); +has 'external_mp' => (is => 'rw'); +has 'layer_mp' => (is => 'rw'); +has 'new_object' => (is => 'rw', default => sub {0}); +has 'straight_once' => (is => 'rw', default => sub {1}); has 'extruder_idx' => (is => 'rw'); has 'extrusion_distance' => (is => 'rw', default => sub {0} ); has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds @@ -76,7 +78,7 @@ sub change_layer { $self->layer($layer); if ($Slic3r::Config->avoid_crossing_perimeters) { - $self->motionplanner(Slic3r::GCode::MotionPlanner->new( + $self->layer_mp(Slic3r::GCode::MotionPlanner->new( islands => union_ex([ map @{$_->expolygon}, @{$layer->slices} ], undef, 1), )); } @@ -216,13 +218,32 @@ sub travel_to { my ($point, $comment) = @_; return "" if points_coincide($self->last_pos, $point); + + my $gcode = ""; if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { - return join '', map $self->G0($_->b, undef, 0, $comment || ""), - $self->motionplanner->shortest_path($self->last_pos, $point)->lines; + my $plan = sub { + my $mp = shift; + return join '', + map $self->G0($_->b, undef, 0, $comment || ""), + $mp->shortest_path($self->last_pos, $point)->lines; + }; + + if ($self->new_object) { + my @shift = ($self->shift_x, $self->shift_y); + $self->set_shift(0,0); + $point->translate(map scale $_, @shift); + $gcode .= $plan->($self->external_mp); + $self->new_object(0); + $self->set_shift(@shift); + } else { + $gcode .= $plan->($self->layer_mp); + } } else { $self->straight_once(0); - return $self->G0($point, undef, 0, $comment || ""); + $gcode .= $self->G0($point, undef, 0, $comment || ""); } + + return $gcode; } sub retract { diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index b95a38ec61..ef55f16cc9 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -45,7 +45,7 @@ sub BUILD { for my $i (0 .. $#points) { for my $j (($i+1) .. $#points) { my $line = Slic3r::Line->new($points[$i], $points[$j]); - if ($expolygon->encloses_line($line, scale Slic3r::Geometry::epsilon)) { + if ($expolygon->encloses_line($line, $tolerance)) { my $dist = $line->length * ($crosses_perimeter ? CROSSING_FACTOR : 1); $edges->{$points[$i]}{$points[$j]} = $dist; $edges->{$points[$j]}{$points[$i]} = $dist; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index df1fc4c0a1..3f2b1769f8 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -614,7 +614,6 @@ sub write_gcode { ); # initialize a motion planner for object-to-object travel moves - my $external_motionplanner; if ($Slic3r::Config->avoid_crossing_perimeters) { my $distance_from_objects = 1; # compute the offsetted convex hull for each object and repeat it for each copy. @@ -622,15 +621,15 @@ sub write_gcode { foreach my $obj_idx (0 .. $#{$self->objects}) { my @island = Slic3r::ExPolygon->new(convex_hull([ map @{$_->expolygon->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers}, - ]))->translate(map -$_, @shift)->offset_ex(scale $distance_from_objects); + ]))->translate(scale $shift[X], scale $shift[Y])->offset_ex(scale $distance_from_objects, 1, JT_SQUARE); foreach my $copy (@{$self->copies->[$obj_idx]}) { push @islands, map $_->clone->translate(@$copy), @island; } } - $external_motionplanner = Slic3r::GCode::MotionPlanner->new( + $gcodegen->external_mp(Slic3r::GCode::MotionPlanner->new( islands => union_ex([ map @$_, @islands ]), no_internal => 1, - ); + )); } # prepare the logic to print one layer @@ -663,6 +662,7 @@ sub write_gcode { $gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt}; } $skirt_done++; + $gcodegen->straight_once(1); } # extrude brim @@ -670,27 +670,18 @@ sub write_gcode { $gcodegen->set_shift(@shift); $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; $brim_done = 1; + $gcodegen->straight_once(1); } for my $obj_copy (@$object_copies) { my ($obj_idx, $copy) = @$obj_copy; + $gcodegen->new_object(1) if $last_obj_copy && $last_obj_copy ne "${obj_idx}_${copy}"; my $layer = $self->objects->[$obj_idx]->layers->[$layer_id]; # retract explicitely because changing the shift_[xy] properties below # won't always trigger the automatic retraction $gcode .= $gcodegen->retract; - # travel to the first perimeter point using the external motion planner - if ($external_motionplanner && @{ $layer->perimeters } && !$gcodegen->straight_once && $last_obj_copy ne "${obj_idx}_${copy}") { - $gcodegen->set_shift(@shift); - my $layer_mp = $gcodegen->motionplanner; - $gcodegen->motionplanner($external_motionplanner); - my $first_perimeter = $layer->perimeters->[0]->unpack; - my $target = $first_perimeter->polygon->[0]->clone->translate(@$copy); - $gcode .= $gcodegen->travel_to($target, "move to first perimeter point"); - $gcodegen->motionplanner($layer_mp); - } - $gcodegen->set_shift( $shift[X] + unscale $copy->[X], $shift[Y] + unscale $copy->[Y], @@ -773,8 +764,10 @@ sub write_gcode { # this happens before Z goes down to layer 0 again, so that # no collision happens hopefully. if ($finished_objects > 0) { - $gcodegen->shift_x($shift[X] + unscale $copy->[X]); - $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); + $gcodegen->set_shift( + $shift[X] + unscale $copy->[X], + $shift[Y] + unscale $copy->[Y], + ); print $fh $gcodegen->retract; print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object'); } From 56a118986ff55fc5d3b68c10276edbc66b040e28 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 23 Aug 2012 21:19:10 +0200 Subject: [PATCH 004/114] External motion was crossing still too much --- lib/Slic3r/GCode/MotionPlanner.pm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index ef55f16cc9..4957f43450 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -12,7 +12,7 @@ has '_edges' => (is => 'rw', default => sub { {} }); # node_idx => { nod has '_crossing_edges' => (is => 'rw', default => sub { {} }); # edge_idx => bool use List::Util qw(first); -use Slic3r::Geometry qw(scale epsilon nearest_point); +use Slic3r::Geometry qw(A B scale epsilon nearest_point); use Slic3r::Geometry::Clipper qw(diff_ex JT_MITER); # clearance (in mm) from the perimeters @@ -98,9 +98,19 @@ sub BUILD { )} > 0; }; - # lines connecting outer polygons are visible { my @outer = (map @$_, @{$self->_outer}); + + # lines of outer polygons connect visible points + for my $i (0 .. $#outer) { + foreach my $line ($outer[$i]->lines) { + my $dist = $line->length; + $edges->{$line->[A]}{$line->[B]} = $dist; + $edges->{$line->[B]}{$line->[A]} = $dist; + } + } + + # lines connecting outer polygons are visible for my $i (0 .. $#outer) { for my $j (($i+1) .. $#outer) { for my $m (0 .. $#{$outer[$i]}) { @@ -163,7 +173,7 @@ sub BUILD { lines => \@lines, points => [ values %{$self->_pointmap} ], no_arrows => 1, - #polygons => [ map @$_, @{$self->islands} ], + polygons => [ map @$_, @{$self->islands} ], #red_polygons => [ map $_->holes, map @$_, @{$self->_inner} ], #white_polygons => [ map @$_, @{$self->_outer} ], ); From 2f1ae3f112430b852f91653992e5ce323b05471f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Sep 2012 15:46:40 +0200 Subject: [PATCH 005/114] Restore non-manifold warnings --- lib/Slic3r/GUI/Plater.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 5f83117c4c..ecf31e8f35 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -309,6 +309,7 @@ sub load_file { : [0,0], ], ); + $object->mesh->check_manifoldness; # we only consider the rotation of the first instance for now $object->set_rotation($model->objects->[$i]->instances->[0]->rotation) From e21fdf0b15b12423a789be6d8933b3c1448195c0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Sep 2012 15:47:38 +0200 Subject: [PATCH 006/114] Little cleanup --- lib/Slic3r/Model.pm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 2d7a331558..aba13f230c 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -83,11 +83,6 @@ sub add_instance { sub mesh { my $self = shift; - my $vertices = []; - my $facets = []; - - - return Slic3r::TriangleMesh->new( vertices => $self->vertices, facets => [ map @{$_->facets}, @{$self->volumes} ], From 91e9f651b51e6288e3f2678c00149d69ca3706a3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 22 Sep 2012 15:51:18 +0200 Subject: [PATCH 007/114] Bugfix: --avoid-crossing-perimeters was crashing when processing empty layers --- lib/Slic3r/GCode/MotionPlanner.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index 4957f43450..7bd84b4fc0 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -224,6 +224,8 @@ sub shortest_path { my $self = shift; my ($from, $to) = @_; + return Slic3r::Polyline->new($from, $to) if !@{$self->islands}; + # find nearest nodes my $new_from = $self->find_node($from, $to); my $new_to = $self->find_node($to, $from); From 32fd58d531de7835883a85e9bb9753a7af76da35 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 10 Jan 2013 15:29:40 +0100 Subject: [PATCH 008/114] Acceleration control. #185 --- README.markdown | 11 +++++++++++ lib/Slic3r/Config.pm | 18 +++++++++++------- lib/Slic3r/GCode.pm | 4 ++-- lib/Slic3r/GUI/Tab.pm | 4 ++++ lib/Slic3r/Print.pm | 6 +++++- slic3r.pl | 11 +++++++++++ 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/README.markdown b/README.markdown index f335450a8b..bf7ff336b3 100644 --- a/README.markdown +++ b/README.markdown @@ -153,6 +153,17 @@ The author of the Silk icon set is Mark James. --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute value or as a percentage over normal speeds (default: 30%) + Acceleration options: + --perimeter-acceleration + Overrides firmware's default acceleration for perimeters. (mm/s^2, set zero + to disable; default: 0) + --infill-acceleration + Overrides firmware's default acceleration for infill. (mm/s^2, set zero + to disable; default: 0) + --default-acceleration + Acceleration will be reset to this value after the specific settings above + have been applied. (mm/s^2, set zero to disable; default: 130) + Accuracy options: --layer-height Layer height in mm (default: 0.4) --first-layer-height Layer height for first layer (mm or %, default: 100%) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 90f9813646..a310361d58 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -6,7 +6,7 @@ use utf8; use List::Util qw(first); # cemetery of old config settings -our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool); +our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration); my $serialize_comma = sub { join ',', @{$_[0]} }; my $deserialize_comma = sub { [ split /,/, $_[0] ] }; @@ -319,25 +319,29 @@ our $Options = { }, # acceleration options - 'acceleration' => { - label => 'Enable acceleration control', - cli => 'acceleration!', - type => 'bool', + 'default_acceleration' => { + label => 'Default', + tooltip => 'This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all.', + sidetext => 'mm/s²', + cli => 'default-acceleration', + type => 'f', default => 0, }, 'perimeter_acceleration' => { label => 'Perimeters', + tooltip => 'This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters.', sidetext => 'mm/s²', cli => 'perimeter-acceleration', type => 'f', - default => 25, + default => 0, }, 'infill_acceleration' => { label => 'Infill', + tooltip => 'This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill.', sidetext => 'mm/s²', cli => 'infill-acceleration', type => 'f', - default => 50, + default => 0, }, # accuracy options diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 2bf1415564..0b9ff8bc6e 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -323,9 +323,9 @@ sub reset_e { sub set_acceleration { my $self = shift; my ($acceleration) = @_; - return "" unless $Slic3r::Config->acceleration; + return "" if !$acceleration; - return sprintf "M201 E%s%s\n", + return sprintf "M204 S%s%s\n", $acceleration, ($Slic3r::Config->gcode_comments ? ' ; adjust acceleration' : ''); } diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index d4479d6bd0..4bb5303509 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -430,6 +430,10 @@ sub build { title => 'Modifiers', options => [qw(first_layer_speed)], }, + { + title => 'Acceleration control (advanced)', + options => [qw(perimeter_acceleration infill_acceleration default_acceleration)], + }, ]); $self->add_options_page('Skirt and brim', 'box.png', optgroups => [ diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index fe9f164b7d..bf67efadcd 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -748,7 +748,6 @@ sub write_gcode { $gcodegen->set_shift(@shift); $gcode .= $gcodegen->set_extruder($self->extruders->[0]); # move_z requires extruder $gcode .= $gcodegen->move_z($gcodegen->layer->print_z); - $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); # skip skirt if we have a large brim if ($layer_id < $Slic3r::Config->skirt_height) { # distribute skirt loops across all extruders @@ -807,7 +806,10 @@ sub write_gcode { # extrude perimeters if (@{ $layerm->perimeters }) { $gcode .= $gcodegen->set_extruder($region->extruders->{perimeter}); + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters }; + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->default_acceleration) + if $Slic3r::Config->perimeter_acceleration; } # extrude fills @@ -822,6 +824,8 @@ sub write_gcode { $gcode .= $gcodegen->extrude($fill, 'fill') ; } } + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->default_acceleration) + if $Slic3r::Config->infill_acceleration; } } } diff --git a/slic3r.pl b/slic3r.pl index bafd98749c..d109f9ff45 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -201,6 +201,17 @@ $j --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute value or as a percentage over normal speeds (default: $config->{first_layer_speed}) + Acceleration options: + --perimeter-acceleration + Overrides firmware's default acceleration for perimeters. (mm/s^2, set zero + to disable; default: $config->{perimeter_acceleration}) + --infill-acceleration + Overrides firmware's default acceleration for infill. (mm/s^2, set zero + to disable; default: $config->{infill_acceleration}) + --default-acceleration + Acceleration will be reset to this value after the specific settings above + have been applied. (mm/s^2, set zero to disable; default: $config->{travel_speed}) + Accuracy options: --layer-height Layer height in mm (default: $config->{layer_height}) --first-layer-height Layer height for first layer (mm or %, default: $config->{first_layer_height}) From 3d03faf0b2db4ad7f5e8a2ce5555b4be16b72e72 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 10 Jan 2013 17:19:38 +0100 Subject: [PATCH 009/114] Remove leftover M501 --- lib/Slic3r/Print.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index bf67efadcd..e3f3c3adfb 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -916,7 +916,6 @@ sub write_gcode { # write end commands to file print $fh $gcodegen->retract; print $fh $gcodegen->set_fan(0); - print $fh "M501 ; reset acceleration\n" if $Slic3r::Config->acceleration; printf $fh "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->end_gcode); printf $fh "; filament used = %.1fmm (%.1fcm3)\n", From 73aae07e74cbe1a221e4de1acd7743f4271e21ca Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Fri, 11 Jan 2013 10:15:42 -0800 Subject: [PATCH 010/114] 842, 847 slightly enlarge a clip polygon to counteract integer truncation Geomery in referenced issues triggered Clipper problems, but also pointed to a situation where integer truncation (as coordinates pass into Clipper) might be shrinking a clip polygon in a way that leaves degenerate or unwanted thin clip results. Growing the clip polygon by 2 is expected to overcome any issues caused by truncation of floats. --- lib/Slic3r/Layer/Region.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 2a27367193..238a8e98e6 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -227,7 +227,9 @@ sub make_perimeters { # and we can extract the gap for later processing my $diff = diff_ex( [ map @$_, $expolygon->offset_ex(-0.5*$distance) ], - [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$distance) ], + # +2 on the offset here makes sure that Clipper float truncation + # won't shrink the clip polygon to be smaller than intended. + [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$distance + 2) ], ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } From ad9be0e4d703aab42b2721762a03373e98977515 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 13 Jan 2013 10:18:34 +0100 Subject: [PATCH 011/114] Bugfix: crash when reading/writing files to paths containing non-ASCII characters on Windows. #651 #865 --- Build.PL | 1 + lib/Slic3r.pm | 8 ++++++++ lib/Slic3r/Config.pm | 6 +++--- lib/Slic3r/Format/AMF.pm | 4 ++-- lib/Slic3r/Format/OBJ.pm | 2 +- lib/Slic3r/Format/STL.pm | 4 ++-- lib/Slic3r/Print.pm | 4 ++-- lib/Slic3r/SVG.pm | 2 +- 8 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Build.PL b/Build.PL index 53dccbbba8..e77e31fba6 100644 --- a/Build.PL +++ b/Build.PL @@ -8,6 +8,7 @@ my $build = Module::Build->new( license => 'perl', requires => { 'Boost::Geometry::Utils' => '0', + 'Encode::Locale' => '0', 'File::Basename' => '0', 'File::Spec' => '0', 'Getopt::Long' => '0', diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 4a0b1bb371..4ca5b43838 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -27,7 +27,10 @@ warn "Running Slic3r under Perl >= 5.16 is not supported nor recommended\n" use FindBin; our $var = "$FindBin::Bin/var"; +use Encode; +use Encode::Locale; use Moo 0.091009; + use Slic3r::Config; use Slic3r::ExPolygon; use Slic3r::Extruder; @@ -88,4 +91,9 @@ sub parallelize { } } +sub open { + my ($fh, $mode, $filename) = @_; + return CORE::open $$fh, $mode, encode('locale_fs', $filename); +} + 1; diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index a310361d58..d79fdf9c40 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -946,7 +946,7 @@ sub new_from_cli { if ($args{$opt_key}) { die "Invalid value for --${_}-gcode: file does not exist\n" if !-e $args{$opt_key}; - open my $fh, "<", $args{$opt_key} + Slic3r::open(\my $fh, "<", $args{$opt_key}) or die "Failed to open $args{$opt_key}\n"; binmode $fh, ':utf8'; $args{$opt_key} = do { local $/; <$fh> }; @@ -1251,7 +1251,7 @@ sub write_ini { my $class = shift; my ($file, $ini) = @_; - open my $fh, '>', $file; + Slic3r::open(\my $fh, '>', $file); binmode $fh, ':utf8'; my $localtime = localtime; printf $fh "# generated by Slic3r $Slic3r::VERSION on %s\n", "$localtime"; @@ -1269,7 +1269,7 @@ sub read_ini { my ($file) = @_; local $/ = "\n"; - open my $fh, '<', $file; + Slic3r::open(\my $fh, '<', $file); binmode $fh, ':utf8'; my $ini = { _ => {} }; diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index 0e44353cfd..0b3bbc1ed7 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -13,7 +13,7 @@ sub read_file { 1; } or die "AMF parsing requires XML::SAX\n"; - open my $fh, '<', $file or die "Failed to open $file\n"; + Slic3r::open(\my $fh, '<', $file) or die "Failed to open $file\n"; my $model = Slic3r::Model->new; XML::SAX::ParserFactory @@ -30,7 +30,7 @@ sub write_file { my %vertices_offset = (); - open my $fh, '>', $file; + Slic3r::open(\my $fh, '>', $file); binmode $fh, ':utf8'; printf $fh qq{\n}; printf $fh qq{\n}; diff --git a/lib/Slic3r/Format/OBJ.pm b/lib/Slic3r/Format/OBJ.pm index d663cfb5a7..c5cc085558 100644 --- a/lib/Slic3r/Format/OBJ.pm +++ b/lib/Slic3r/Format/OBJ.pm @@ -5,7 +5,7 @@ sub read_file { my $self = shift; my ($file) = @_; - open my $fh, '<', $file or die "Failed to open $file\n"; + Slic3r::open(\my $fh, '<', $file) or die "Failed to open $file\n"; my $vertices = []; my $facets = []; while (my $_ = <$fh>) { diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 6e96651854..5c992230e4 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -7,7 +7,7 @@ sub read_file { my $self = shift; my ($file) = @_; - open my $fh, '<', $file or die "Failed to open $file\n"; + Slic3r::open(\my $fh, '<', $file) or die "Failed to open $file\n"; # let's detect whether file is ASCII or binary my $mode; @@ -103,7 +103,7 @@ sub write_file { my $self = shift; my ($file, $model, %params) = @_; - open my $fh, '>', $file; + Slic3r::open(\my $fh, '>', $file); $params{binary} ? _write_binary($fh, $model->mesh) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e3f3c3adfb..5540632dbb 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -472,7 +472,7 @@ sub export_svg { my $output_file = $self->expanded_output_filepath($params{output_file}); $output_file =~ s/\.gcode$/.svg/i; - open my $fh, ">", $output_file or die "Failed to open $output_file for writing\n"; + Slic3r::open(\my $fh, ">", $output_file) or die "Failed to open $output_file for writing\n"; print "Exporting to $output_file..."; my $print_size = $self->size; print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]); @@ -647,7 +647,7 @@ sub write_gcode { if (ref $file eq 'IO::Scalar') { $fh = $file; } else { - open $fh, ">", $file + Slic3r::open(\$fh, ">", $file) or die "Failed to open $file for writing\n"; } diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index 8621614b55..0179ea7692 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -130,7 +130,7 @@ sub output_lines { sub write_svg { my ($svg, $filename) = @_; - open my $fh, '>', $filename; + Slic3r::open(\my $fh, '>', $filename); print $fh $svg->xmlify; close $fh; printf "SVG written to %s\n", $filename; From 68fc91d854ca7cc61b5b53af71574ec135f99916 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Sun, 13 Jan 2013 02:50:49 -0800 Subject: [PATCH 012/114] offset miter limit to 3 - 801 828 836 851 875 - spikes and pimples Was set to 10 to address 801. Setting to 3 has the same effect for 801, and avoids spike artifacts that are likely causing "spike and pimple" problems in the referenced issues. --- lib/Slic3r/Geometry/Clipper.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 9dedc7a96a..0ec5536535 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -21,7 +21,7 @@ sub offset { my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; $scale ||= 100000; $joinType //= JT_MITER; - $miterLimit //= 10; + $miterLimit //= 3; my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit); return @$offsets; From bce3e9621048f735bf96b2d6a16eea5d9518ab9f Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Mon, 14 Jan 2013 21:18:31 -0800 Subject: [PATCH 013/114] extraneous fill against sloped walls with fill every N layers #834 Polygons used for N x depth fill surfaces need to be offset by flow spacing before they are reused to clip lower fill surfaces. Otherwise thin fill sections remain on clipped layers against sloping walls, and the N x depth fill collides with them. --- lib/Slic3r/Print/Object.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index bf4f77aa07..f858249616 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -445,8 +445,8 @@ sub combine_infill { my $area_threshold = $Slic3r::flow->scaled_spacing ** 2; for my $region_id (0 .. ($self->print->regions_count-1)) { - # start from bottom, skip first layer - for (my $i = 1; $i < $self->layer_count; $i++) { + # start from top, skip lowest layer + for (my $i = $self->layer_count - 1; $i > 0; $i--) { my $layerm = $self->layers->[$i]->regions->[$region_id]; # skip layer if no internal fill surfaces @@ -506,6 +506,13 @@ sub combine_infill { { my @new_surfaces = (); push @new_surfaces, grep $_->surface_type != S_TYPE_INTERNAL, @{$lower_layerm->fill_surfaces}; + + # offset for the two different flow spacings + $intersection = [ map $_->offset_ex( + $lower_layerm->perimeter_flow->scaled_spacing / 2 + + $layerm->perimeter_flow->scaled_spacing / 2 + ), @$intersection]; + foreach my $depth (1..$Slic3r::Config->infill_every_layers) { push @new_surfaces, map Slic3r::Surface->new (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $depth), From 4bff4d0d508990845450144f54c71659d36c9402 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 15 Jan 2013 12:50:15 +0100 Subject: [PATCH 014/114] Ignore perimeter_acceleration and infill_acceleration if their values are 25 and 50 to handle legacy configs gracefully. --- lib/Slic3r/Config.pm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index d79fdf9c40..f691ae35b5 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -1024,6 +1024,17 @@ sub set { $value = 1; } + # For historical reasons, the world's full of configs having these very low values; + # to avoid unexpected behavior we need to ignore them. Banning these two hard-coded + # values is a dirty hack and will need to be removed sometime in the future, but it + # will avoid lots of complaints for now. + if ($opt_key eq 'perimeter_acceleration' && $value == '25') { + $value = 0; + } + if ($opt_key eq 'infill_acceleration' && $value == '50') { + $value = 0; + } + if (!exists $Options->{$opt_key}) { my @keys = grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options; if (!@keys) { From 85934e17383d2f326ec0e725c654816d9636762b Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Tue, 15 Jan 2013 22:13:11 -0800 Subject: [PATCH 015/114] use infill flow spacing, not perimeter, for combine_infill() clip offsets --- lib/Slic3r/Print/Object.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index f858249616..01c71af45e 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -509,8 +509,8 @@ sub combine_infill { # offset for the two different flow spacings $intersection = [ map $_->offset_ex( - $lower_layerm->perimeter_flow->scaled_spacing / 2 - + $layerm->perimeter_flow->scaled_spacing / 2 + $lower_layerm->infill_flow->scaled_spacing / 2 + + $layerm->infill_flow->scaled_spacing / 2 ), @$intersection]; foreach my $depth (1..$Slic3r::Config->infill_every_layers) { From f3a1221039392fe40e47a6641babed021a4df294 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Wed, 16 Jan 2013 05:52:26 -0800 Subject: [PATCH 016/114] per-thread clipper object for parallel support generation #888 --- lib/Slic3r/Print/Object.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 01c71af45e..cbafda2d50 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -674,6 +674,7 @@ sub generate_support_material { items => [ keys %layers ], thread_cb => sub { my $q = shift; + $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; my $result = {}; while (defined (my $layer_id = $q->dequeue)) { $result->{$layer_id} = [ $process_layer->($layer_id) ]; From ecc7e4560e661de3acbec2234dfa4d0b6f073f32 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 10:39:05 +0100 Subject: [PATCH 017/114] Don't fill gaps if fill density is 0. #915 --- lib/Slic3r/Layer/Region.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 238a8e98e6..37d9b7723b 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -252,7 +252,7 @@ sub make_perimeters { } # fill gaps - if ($Slic3r::Config->gap_fill_speed > 0) { + if ($Slic3r::Config->gap_fill_speed > 0 && $Slic3r::Config->fill_density > 0) { my $filler = Slic3r::Fill::Rectilinear->new(layer_id => $self->layer->id); my $w = $self->perimeter_flow->width; From 954520cba5e51832d668e9fc3c0837b370e2d6f7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 10:40:45 +0100 Subject: [PATCH 018/114] Enable cooling by default --- lib/Slic3r/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index f691ae35b5..98e6702c8e 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -715,7 +715,7 @@ END tooltip => 'This flag enables all the cooling features.', cli => 'cooling!', type => 'bool', - default => 0, + default => 1, }, 'min_fan_speed' => { label => 'Min', From 0ddd6d9e553507e99b3975f108e444611aa3a92a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 11:11:22 +0100 Subject: [PATCH 019/114] Require Math::Clipper 1.17 --- Build.PL | 2 +- lib/Slic3r/Geometry/Clipper.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build.PL b/Build.PL index e77e31fba6..89af11ed83 100644 --- a/Build.PL +++ b/Build.PL @@ -12,7 +12,7 @@ my $build = Module::Build->new( 'File::Basename' => '0', 'File::Spec' => '0', 'Getopt::Long' => '0', - 'Math::Clipper' => '1.15', + 'Math::Clipper' => '1.17', 'Math::ConvexHull::MonotoneChain' => '0.01', 'Math::Geometry::Voronoi' => '1.3', 'Math::PlanePath' => '53', diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 0ec5536535..292e88f27f 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -8,7 +8,7 @@ our @EXPORT_OK = qw(safety_offset offset offset_ex diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND JT_SQUARE is_counter_clockwise); -use Math::Clipper 1.15 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); +use Math::Clipper 1.17 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); use Slic3r::Geometry qw(scale); our $clipper = Math::Clipper->new; From 4fc6fc5ab6f3ed632f6aa6efcd745ab40e4f2d0b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 11:51:00 +0100 Subject: [PATCH 020/114] Increase default value for retract_length_toolchange --- lib/Slic3r/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 98e6702c8e..751462ef55 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -696,7 +696,7 @@ END type => 'f', serialize => $serialize_comma, deserialize => $deserialize_comma, - default => [3], + default => [10], }, 'retract_restart_extra_toolchange' => { label => 'Extra length on restart', From 4d8757bf5da269b35133c6d6be3f06c3465b9ec9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 11:59:14 +0100 Subject: [PATCH 021/114] Interface support layers were clipped badly. Also, reduce simplification of the clipping boundaries. #583 --- lib/Slic3r/Print/Object.pm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index cbafda2d50..e9ca00428f 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -561,9 +561,14 @@ sub generate_support_material { my $layer = $self->layers->[$i]; my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef; + my @current_layer_offsetted_slices = map $_->offset_ex($distance_from_object), @{$layer->slices}; + # $queue[-1] contains the overhangs of the upper layer, regardless of any empty interface layers # $queue[0] contains the overhangs of the first upper layer above the empty interface layers - $layers_interfaces{$i} = [@{ $queue[-1] || [] }]; + $layers_interfaces{$i} = diff_ex( + [ @{ $queue[-1] || [] } ], + [ map @$_, @current_layer_offsetted_slices ], + ); # step 1: generate support material in current layer (for upper layers) push @current_support_regions, @{ shift @queue } if @queue && $i < $#{$self->layers}; @@ -576,11 +581,11 @@ sub generate_support_material { $layers{$i} = diff_ex( [ map @$_, @current_support_regions ], [ - (map @$_, map $_->offset_ex($distance_from_object), @{$layer->slices}), + (map @$_, @current_layer_offsetted_slices), (map @$_, @{ $layers_interfaces{$i} }), ], ); - $_->simplify($flow->scaled_spacing * 2) for @{$layers{$i}}; + $_->simplify($flow->scaled_spacing) for @{$layers{$i}}; # step 2: get layer overhangs and put them into queue for adding support inside lower layers # we need an angle threshold for this From 97f0b7a3724e10f4c7729a8e4d968df06ab377cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 12:27:39 +0100 Subject: [PATCH 022/114] Update retraction.t to work with two-phase retractions (triggered by changing the default retract_length_toolchange to something greater than 3) --- t/retraction.t | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index 8ad1fbc793..3d10876567 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -21,6 +21,7 @@ my $test = sub { my $tool = 0; my @toolchange_count = (); # track first usages so that we don't expect retract_length_toolchange when extruders are used for the first time my @retracted = (1); # ignore the first travel move from home to first point + my @retracted_length = (0); my $lifted = 0; my $changed_tool = 0; my $wait_for_toolchange = 0; @@ -52,21 +53,22 @@ my $test = sub { } } if ($info->{retracting}) { - if (_eq(-$info->{dist_E}, $print->extruders->[$tool]->retract_length)) { + $retracted[$tool] = 1; + $retracted_length[$tool] += -$info->{dist_E}; + if (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length)) { # okay - } elsif (_eq(-$info->{dist_E}, $print->extruders->[$tool]->retract_length_toolchange)) { + } elsif (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length_toolchange)) { $wait_for_toolchange = 1; } else { fail 'retracted by the correct amount'; } fail 'combining retraction and travel with G0' if $cmd ne 'G0' && $conf->g0 && ($info->{dist_Z} || $info->{dist_XY}); - $retracted[$tool] = 1; } if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; if ($retracted[$tool]) { - my $expected_amount = $print->extruders->[$tool]->retract_length + $print->extruders->[$tool]->retract_restart_extra; + my $expected_amount = $retracted_length[$tool] + $print->extruders->[$tool]->retract_restart_extra; if ($changed_tool && $toolchange_count[$tool] > 1) { $expected_amount = $print->extruders->[$tool]->retract_length_toolchange + $print->extruders->[$tool]->retract_restart_extra_toolchange; $changed_tool = 0; @@ -74,6 +76,7 @@ my $test = sub { fail 'unretracted by the correct amount' if !_eq($info->{dist_E}, $expected_amount); $retracted[$tool] = 0; + $retracted_length[$tool] = 0; } } if ($info->{travel} && $info->{dist_XY} >= $print->extruders->[$tool]->retract_before_travel) { From 92feebf5fe474bb9c46f2033d6d26302186e496a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 12:32:57 +0100 Subject: [PATCH 023/114] Fixed regression introduced in 4d8757bf5da269b35133c6d6be3f06c3465b9ec9 causing crash during support material generation --- lib/Slic3r/Print/Object.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e9ca00428f..1457a66ec8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -566,7 +566,7 @@ sub generate_support_material { # $queue[-1] contains the overhangs of the upper layer, regardless of any empty interface layers # $queue[0] contains the overhangs of the first upper layer above the empty interface layers $layers_interfaces{$i} = diff_ex( - [ @{ $queue[-1] || [] } ], + [ map @$_, @{ $queue[-1] || [] } ], [ map @$_, @current_layer_offsetted_slices ], ); From 6dc055fa13e7bd3a196e0332c5fbb7a87fbe6561 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 12:54:48 +0100 Subject: [PATCH 024/114] Fix help text s/perimeters-/perimeter-/. #917 --- README.markdown | 4 ++-- slic3r.pl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index bf7ff336b3..a6aaaf952d 100644 --- a/README.markdown +++ b/README.markdown @@ -274,7 +274,7 @@ The author of the Silk icon set is Mark James. (like 0.65) or a percentage over layer height (like 200%) --first-layer-extrusion-width Set a different extrusion width for first layer - --perimeters-extrusion-width + --perimeter-extrusion-width Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill @@ -285,7 +285,7 @@ The author of the Silk icon set is Mark James. Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement (can be specified multiple times, default: 0x0) - --perimeters-extruder + --perimeter-extruder Extruder to use for perimeters (1+, default: 1) --infill-extruder Extruder to use for infill (1+, default: 1) --support-material-extruder diff --git a/slic3r.pl b/slic3r.pl index d109f9ff45..2a1ef7eb4b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -322,7 +322,7 @@ $j (like 0.65) or a percentage over layer height (like 200%) --first-layer-extrusion-width Set a different extrusion width for first layer - --perimeters-extrusion-width + --perimeter-extrusion-width Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill @@ -333,7 +333,7 @@ $j Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement (can be specified multiple times, default: 0x0) - --perimeters-extruder + --perimeter-extruder Extruder to use for perimeters (1+, default: 1) --infill-extruder Extruder to use for infill (1+, default: 1) --support-material-extruder From 8001059390b0653a27c2787341947dd708570b1d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 14:13:25 +0100 Subject: [PATCH 025/114] Add Mike Sheldrake in the About window --- lib/Slic3r/GUI/AboutDialog.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm index f8936338e1..f2a390bb2a 100644 --- a/lib/Slic3r/GUI/AboutDialog.pm +++ b/lib/Slic3r/GUI/AboutDialog.pm @@ -47,12 +47,12 @@ sub new { '' . '' . '' . - 'Copyright © 2011-2012 Alessandro Ranellucci. All rights reserved. ' . + 'Copyright © 2011-2013 Alessandro Ranellucci. All rights reserved. ' . 'Slic3r is licensed under the ' . 'GNU Affero General Public License, version 3.' . '


' . 'Slic3r logo designed by Corey Daniels, Silk Icon Set designed by Mark James. ' . - 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess and numerous others.' . + 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Mike Sheldrake and numerous others.' . '
' . '' . ''; From 177347137f73fc29dc6af7f82f800782dc5cc1e2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 14:56:31 +0100 Subject: [PATCH 026/114] New G-code flavor for Sailfish (thanks to fros1y for working on it). #826 --- README.markdown | 2 +- lib/Slic3r/Config.pm | 4 ++-- lib/Slic3r/GCode.pm | 40 +++++++++++++++++++++++++++++-------- lib/Slic3r/Print.pm | 13 +++++------- slic3r.pl | 2 +- utils/zsh/functions/_slic3r | 2 +- 6 files changed, 42 insertions(+), 21 deletions(-) diff --git a/README.markdown b/README.markdown index a6aaaf952d..c8a5e57e7d 100644 --- a/README.markdown +++ b/README.markdown @@ -108,7 +108,7 @@ The author of the Silk icon set is Mark James. (default: 100,100) --z-offset Additional height in mm to add to vertical coordinates (+/-, default: 0) - --gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/mach3/no-extrusion, + --gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/sailfish/mach3/no-extrusion, default: reprap) --use-relative-e-distances Enable this to get relative E values --gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 751462ef55..922206dea7 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -64,8 +64,8 @@ our $Options = { tooltip => 'Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer\'s firmware to get a compatible output. The "No extrusion" flavor prevents Slic3r from exporting any extrusion value at all.', cli => 'gcode-flavor=s', type => 'select', - values => [qw(reprap teacup makerbot mach3 no-extrusion)], - labels => ['RepRap (Marlin/Sprinter)', 'Teacup', 'MakerBot', 'Mach3/EMC', 'No extrusion'], + values => [qw(reprap teacup makerbot sailfish mach3 no-extrusion)], + labels => ['RepRap (Marlin/Sprinter)', 'Teacup', 'MakerBot', 'Sailfish', 'Mach3/EMC', 'No extrusion'], default => 'reprap', }, 'use_relative_e_distances' => { diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 0b9ff8bc6e..b60025df3d 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -6,6 +6,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y B); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); +has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); has 'move_z_callback' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); @@ -65,6 +66,21 @@ sub set_shift { $self->shift_y($shift[Y]); } +sub change_layer { + my $self = shift; + my ($layer) = @_; + + $self->layer($layer); + + my $gcode = ""; + if ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) { + $gcode .= sprintf "M73 P%s%s\n", + int(100 * ($layer->id / ($self->layer_count - 1))), + ($Slic3r::Config->gcode_comments ? ' ; update progress' : ''); + } + return $gcode; +} + # this method accepts Z in scaled coordinates sub move_z { my $self = shift; @@ -452,7 +468,10 @@ sub set_extruder { # set the new extruder $self->extruder($extruder); - $gcode .= sprintf "T%d%s\n", $extruder->id, ($Slic3r::Config->gcode_comments ? ' ; change extruder' : ''); + $gcode .= sprintf "%s%d%s\n", + ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ ? 'M108 T' : 'T'), + $extruder->id, + ($Slic3r::Config->gcode_comments ? ' ; change extruder' : ''); $gcode .= $self->reset_e; return $gcode; @@ -467,11 +486,17 @@ sub set_fan { if ($speed == 0) { my $code = $Slic3r::Config->gcode_flavor eq 'teacup' ? 'M106 S0' - : 'M107'; + : $Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ + ? 'M127' + : 'M107'; return sprintf "$code%s\n", ($Slic3r::Config->gcode_comments ? ' ; disable fan' : ''); } else { - return sprintf "M106 %s%d%s\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), - (255 * $speed / 100), ($Slic3r::Config->gcode_comments ? ' ; enable fan' : ''); + if ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) { + return sprintf "M126%s\n", ($Slic3r::Config->gcode_comments ? ' ; enable fan' : ''); + } else { + return sprintf "M106 %s%d%s\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), + (255 * $speed / 100), ($Slic3r::Config->gcode_comments ? ' ; enable fan' : ''); + } } } return ""; @@ -481,14 +506,14 @@ sub set_temperature { my $self = shift; my ($temperature, $wait, $tool) = @_; - return "" if $wait && $Slic3r::Config->gcode_flavor eq 'makerbot'; + return "" if $wait && $Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/; my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup') ? ('M109', 'wait for temperature to be reached') : ('M104', 'set temperature'); my $gcode = sprintf "$code %s%d %s; $comment\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, - (defined $tool && $self->multiple_extruders) ? "T$tool " : ""; + (defined $tool && ($self->multiple_extruders || $Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/)) ? "T$tool " : ""; $gcode .= "M116 ; wait for temperature to be reached\n" if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait; @@ -501,8 +526,7 @@ sub set_bed_temperature { my ($temperature, $wait) = @_; my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup') - ? (($Slic3r::Config->gcode_flavor eq 'makerbot' ? 'M109' - : 'M190'), 'wait for bed temperature to be reached') + ? (($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') : ('M140', 'set bed temperature'); my $gcode = sprintf "$code %s%d ; $comment\n", ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 5540632dbb..1cd95334fb 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -242,11 +242,7 @@ sub object_copies { sub layer_count { my $self = shift; - my $count = 0; - foreach my $object (@{$self->objects}) { - $count = @{$object->layers} if @{$object->layers} > $count; - } - return $count; + return max(map { scalar @{$_->layers} } @{$self->objects}); } sub regions_count { @@ -675,7 +671,8 @@ sub write_gcode { # set up our extruder object my $gcodegen = Slic3r::GCode->new( - multiple_extruders => (@{$self->extruders} > 1), + multiple_extruders => (@{$self->extruders} > 1), + layer_count => $self->layer_count, ); my $min_print_speed = 60 * $Slic3r::Config->min_print_speed; my $dec = $gcodegen->dec; @@ -700,7 +697,7 @@ sub write_gcode { print $fh "G21 ; set units to millimeters\n"; if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|teacup)$/) { printf $fh $gcodegen->reset_e; - if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|makerbot)$/) { + if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|makerbot|sailfish)$/) { if ($Slic3r::Config->use_relative_e_distances) { print $fh "M83 ; use relative distances for extrusion\n"; } else { @@ -733,7 +730,7 @@ sub write_gcode { } # set new layer, but don't move Z as support material interfaces may need an intermediate one - $gcodegen->layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); + $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); $gcodegen->elapsed_time(0); # prepare callback to call as soon as a Z command is generated diff --git a/slic3r.pl b/slic3r.pl index 2a1ef7eb4b..aa6d26b325 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -156,7 +156,7 @@ $j (default: $config->{print_center}->[0],$config->{print_center}->[1]) --z-offset Additional height in mm to add to vertical coordinates (+/-, default: $config->{z_offset}) - --gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/mach3/no-extrusion, + --gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/sailfish/mach3/no-extrusion, default: $config->{gcode_flavor}) --use-relative-e-distances Enable this to get relative E values --gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported diff --git a/utils/zsh/functions/_slic3r b/utils/zsh/functions/_slic3r index 384c812a5b..41b2594bd8 100644 --- a/utils/zsh/functions/_slic3r +++ b/utils/zsh/functions/_slic3r @@ -22,7 +22,7 @@ _arguments -S \ '*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \ '--print-center[specify print center coordinates]:print center coordinates in mm,mm' \ '--z-offset[specify Z-axis offset]:Z-axis offset in mm' \ - '--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerbot mach3 no-extrusion)' \ + '--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerbot sailfish mach3 no-extrusion)' \ '(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \ '--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \ '(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \ From 73c70021df2ee2f37ed538660552e9505500c149 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 15:02:40 +0100 Subject: [PATCH 027/114] Change toolchange and G92 E0 order for makerbot and sailfish G-code flavors. #855 --- lib/Slic3r/GCode.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b60025df3d..bad4db9188 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -468,11 +468,18 @@ sub set_extruder { # set the new extruder $self->extruder($extruder); - $gcode .= sprintf "%s%d%s\n", + my $toolchange_gcode = sprintf "%s%d%s\n", ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ ? 'M108 T' : 'T'), $extruder->id, ($Slic3r::Config->gcode_comments ? ' ; change extruder' : ''); - $gcode .= $self->reset_e; + + if ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) { + $gcode .= $self->reset_e; + $gcode .= $toolchange_gcode; + } else { + $gcode .= $toolchange_gcode; + $gcode .= $self->reset_e; + } return $gcode; } From 71052433de0ff1f3da04471ccbb572babafc3cae Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 15:50:45 +0100 Subject: [PATCH 028/114] Releasing 0.9.8 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 4ca5b43838..59df14307b 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.8-dev"; +our $VERSION = "0.9.8"; our $debug = 0; sub debugf { From 09bdd2ea5ddfef252a394e51402a7bb0932902cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 17 Jan 2013 17:44:56 +0100 Subject: [PATCH 029/114] Bump version number --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 59df14307b..3045fb1cfb 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.8"; +our $VERSION = "0.9.9-dev"; our $debug = 0; sub debugf { From 65ef3b35d9350a3a9843c1a40800f491c7ea9941 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Fri, 18 Jan 2013 08:36:01 -0800 Subject: [PATCH 030/114] limit layer requests to object->layer_count when preparing skirt #901 Each object on the plater was being queried for points for producing the skirt for all skirt layers, even when the object was shorter than the skirt. layers of the print --- lib/Slic3r/Print.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1cd95334fb..8c2d94e462 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -546,10 +546,10 @@ sub make_skirt { return unless $Slic3r::Config->skirts > 0; # collect points from all layers contained in skirt height - my $skirt_height = $Slic3r::Config->skirt_height; - $skirt_height = $self->layer_count if $skirt_height > $self->layer_count; my @points = (); foreach my $obj_idx (0 .. $#{$self->objects}) { + my $skirt_height = $Slic3r::Config->skirt_height; + $skirt_height = $self->objects->[$obj_idx]->layer_count if $skirt_height > $self->objects->[$obj_idx]->layer_count; my @layers = map $self->objects->[$obj_idx]->layers->[$_], 0..($skirt_height-1); my @layer_points = ( (map @$_, map @$_, map @{$_->slices}, @layers), From 94bb1e54bb4e39cdbc6dddcabba773f0eeddc76b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Jan 2013 11:03:01 +0100 Subject: [PATCH 031/114] =?UTF-8?q?Raise=20max=20temperatures=20(in=20GUI)?= =?UTF-8?q?=20to=20400=C2=B0C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Slic3r/Config.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 922206dea7..ac474d92f8 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -158,7 +158,7 @@ our $Options = { sidetext => '°C', cli => 'temperature=i@', type => 'i', - max => 300, + max => 400, serialize => $serialize_comma, deserialize => sub { $_[0] ? [ split /,/, $_[0] ] : [0] }, default => [200], @@ -171,7 +171,7 @@ our $Options = { type => 'i', serialize => $serialize_comma, deserialize => sub { $_[0] ? [ split /,/, $_[0] ] : [0] }, - max => 300, + max => 400, default => [200], }, From f555a1ecc2102df83cd8f25d078429ac0adac67e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Jan 2013 19:16:57 +0100 Subject: [PATCH 032/114] Replace 'Less' with 'Fewer' in plater. #944 --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index a8b30cba27..caa9dcb3fb 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -62,7 +62,7 @@ sub new { Wx::ToolTip::Enable(1); $self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL); $self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new("$Slic3r::var/add.png", wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LESS, "Less", Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LESS, "Fewer", Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_anticlockwise.png", wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new("$Slic3r::var/arrow_rotate_clockwise.png", wxBITMAP_TYPE_PNG), ''); From 18280da1b397823ad6017dbd72afe80d50411e54 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 25 Jan 2013 18:02:01 +0100 Subject: [PATCH 033/114] Bugfix: the non-manifold warning was spitting out wrong coordinates --- lib/Slic3r/TriangleMesh.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index d830ba54c9..0f5df9a4e7 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -152,9 +152,9 @@ sub check_manifoldness { my ($first_bad_edge_id) = grep { @{ $self->edges_facets->[$_] } != 2 } 0..$#{$self->edges_facets}; if (defined $first_bad_edge_id) { - warn sprintf "Warning: The input file contains a hole near edge %f-%f (not manifold). " + warn sprintf "Warning: The input file contains a hole near edge %f,%f,%f-%f,%f,%f (not manifold). " . "You might want to repair it and retry, or to check the resulting G-code before printing anyway.\n", - @{$self->edges->[$first_bad_edge_id]}; + map @{$self->vertices->[$_]}, @{$self->edges->[$first_bad_edge_id]}; return 0; } return 1; From 599d76b70b35f6e6f4c3ad93c280578dec909a2f Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Fri, 25 Jan 2013 12:29:44 -0800 Subject: [PATCH 034/114] avoid DBus related crash on Ubuntu #954 An undocumented feature of Net::DBus->session() is that it returns the same connection object it creates the first time it's called for all subsequent calls. Somehow this exposes us to a bug where unref() is called on that object too many times, causing a segfault. Undefining the cached object after we've used it once avoids this. --- lib/Slic3r/GUI.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7372049266..b431d307ba 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -413,6 +413,7 @@ sub notify { my $notifier = $serv->get_object('/org/freedesktop/Notifications', 'org.freedesktop.Notifications'); $notifier->Notify('Slic3r', 0, $self->{icon}, $title, $message, [], {}, -1); + undef $Net::DBus::bus_session; } }; } From bc61656057af4f93ba399f438ba38e941333fd76 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Sat, 26 Jan 2013 09:31:28 -0800 Subject: [PATCH 035/114] harmless misplaced semicolon --- lib/Slic3r/GUI.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index b431d307ba..fb53b5cb54 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -414,8 +414,8 @@ sub notify { 'org.freedesktop.Notifications'); $notifier->Notify('Slic3r', 0, $self->{icon}, $title, $message, [], {}, -1); undef $Net::DBus::bus_session; - } - }; + }; + } } 1; From 4c62c1b5707866e833bd1657bdbe02087f4f4afb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Jan 2013 21:36:54 +0100 Subject: [PATCH 036/114] Support .gco extension too. #957 --- lib/Slic3r/GUI/SkeinPanel.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 8cdc8d7b2f..edb47a9fe6 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -18,7 +18,7 @@ use constant FILE_WILDCARDS => { obj => 'OBJ files (*.obj)|*.obj;*.OBJ', amf => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML', ini => 'INI files *.ini|*.ini;*.INI', - gcode => 'G-code files *.gcode|*.gcode;*.GCODE|G-code files *.g|*.g;*.G', + gcode => 'G-code files (*.gcode, *.gco, *.g)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G', svg => 'SVG files *.svg|*.svg;*.SVG', }; use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(stl obj amf)}; From f1230312e3ee8a3eb5d157f8b6223829e2d56f7c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Jan 2013 23:55:47 +0100 Subject: [PATCH 037/114] Fixes for merge issues --- lib/Slic3r/GCode.pm | 12 +++++------- lib/Slic3r/GCode/MotionPlanner.pm | 2 +- lib/Slic3r/Print.pm | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 35a878ea2b..9f92b91298 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -163,8 +163,7 @@ sub extrude_loop { $point->rotate($angle, $extrusion_path->polyline->[0]); # generate the travel move - $self->speed('travel'); - $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); + $gcode .= $self->travel_to($point, "move inwards before travel"); } return $gcode; @@ -205,9 +204,7 @@ sub extrude_path { } # go to first point of extrusion path - $self->speed('travel'); - $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") - if !points_coincide($self->last_pos, $path->points->[0]); + $gcode .= $self->travel_to($path->points->[0], "move to first $description point"); # compensate retraction $gcode .= $self->unretract; @@ -265,13 +262,14 @@ sub travel_to { my ($point, $comment) = @_; return "" if points_coincide($self->last_pos, $point); - + $self->speed('travel'); my $gcode = ""; if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { + $point = $point->clone; my $plan = sub { my $mp = shift; return join '', - map $self->G0($_->b, undef, 0, $comment || ""), + map $self->G0($_->[B], undef, 0, $comment || ""), $mp->shortest_path($self->last_pos, $point)->lines; }; diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index 7bd84b4fc0..810a02ae2c 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -169,7 +169,7 @@ sub BUILD { } require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "space.svg", + Slic3r::SVG::output("space.svg", lines => \@lines, points => [ values %{$self->_pointmap} ], no_arrows => 1, diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0bfdeec073..5f06cf1dbe 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -794,6 +794,7 @@ sub write_gcode { for my $obj_copy (@$object_copies) { my ($obj_idx, $copy) = @$obj_copy; $gcodegen->new_object(1) if $last_obj_copy && $last_obj_copy ne "${obj_idx}_${copy}"; + $last_obj_copy = "${obj_idx}_${copy}"; my $layer = $self->objects->[$obj_idx]->layers->[$layer_id]; $gcodegen->set_shift(map $shift[$_] + unscale $copy->[$_], X,Y); From ad48fdc7f96f2bcf6e47055f9403799fe887f78b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Jan 2013 12:48:16 +0100 Subject: [PATCH 038/114] Fix external motionplanner too --- lib/Slic3r/GCode.pm | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 9f92b91298..d503db9919 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -63,8 +63,8 @@ sub set_shift { my @shift = @_; $self->last_pos->translate( - scale ($shift[X] - $self->shift_x), - scale ($shift[Y] - $self->shift_y), + scale ($self->shift_x - $shift[X]), + scale ($self->shift_y - $shift[Y]), ); $self->shift_x($shift[X]); @@ -265,7 +265,6 @@ sub travel_to { $self->speed('travel'); my $gcode = ""; if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { - $point = $point->clone; my $plan = sub { my $mp = shift; return join '', @@ -274,11 +273,16 @@ sub travel_to { }; if ($self->new_object) { - my @shift = ($self->shift_x, $self->shift_y); - $self->set_shift(0,0); - $point->translate(map scale $_, @shift); - $gcode .= $plan->($self->external_mp); $self->new_object(0); + + # represent $point in G-code coordinates + $point = $point->clone; + my @shift = ($self->shift_x, $self->shift_y); + $point->translate(map scale $_, @shift); + + # calculate path (external_mp uses G-code coordinates so we temporary need a null shift) + $self->set_shift(0,0); + $gcode .= $plan->($self->external_mp); $self->set_shift(@shift); } else { $gcode .= $plan->($self->layer_mp); From 50526dea5addba9b6193678a1cc404f4e5947796 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Jan 2013 13:06:45 +0100 Subject: [PATCH 039/114] Fix last_pos shifting and add unit test --- MANIFEST | 1 + lib/Slic3r/GCode.pm | 5 +++-- t/gcode.t | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 t/gcode.t diff --git a/MANIFEST b/MANIFEST index 45612a57bc..4f6b6fba36 100644 --- a/MANIFEST +++ b/MANIFEST @@ -58,6 +58,7 @@ t/collinear.t t/custom_gcode.t t/dynamic.t t/fill.t +t/gcode.t t/geometry.t t/layers.t t/loops.t diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index bad4db9188..30e54f734a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -57,9 +57,10 @@ sub set_shift { my $self = shift; my @shift = @_; + # if shift increases (goes towards right), last_pos decreases because it goes towards left $self->last_pos->translate( - scale ($shift[X] - $self->shift_x), - scale ($shift[Y] - $self->shift_y), + scale ($self->shift_x - $shift[X]), + scale ($self->shift_x - $shift[Y]), ); $self->shift_x($shift[X]); diff --git a/t/gcode.t b/t/gcode.t new file mode 100644 index 0000000000..c41307d4fc --- /dev/null +++ b/t/gcode.t @@ -0,0 +1,20 @@ +use Test::More tests => 1; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Geometry qw(scale); + +{ + local $Slic3r::Config = Slic3r::Config->new_from_defaults; + my $gcodegen = Slic3r::GCode->new(layer_count => 1); + $gcodegen->set_shift(10, 10); + is_deeply $gcodegen->last_pos, [scale -10, scale -10], 'last_pos is shifted correctly'; +} + +__END__ From ddaeaa7591a196a967d24c93eb32e8e1d088addb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Jan 2013 13:06:45 +0100 Subject: [PATCH 040/114] Fix last_pos shifting and add unit test --- MANIFEST | 1 + lib/Slic3r/GCode.pm | 1 + t/gcode.t | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 t/gcode.t diff --git a/MANIFEST b/MANIFEST index 6deab070bb..e48376de87 100644 --- a/MANIFEST +++ b/MANIFEST @@ -59,6 +59,7 @@ t/collinear.t t/custom_gcode.t t/dynamic.t t/fill.t +t/gcode.t t/geometry.t t/layers.t t/loops.t diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d503db9919..3633c3c5bb 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -62,6 +62,7 @@ sub set_shift { my $self = shift; my @shift = @_; + # if shift increases (goes towards right), last_pos decreases because it goes towards left $self->last_pos->translate( scale ($self->shift_x - $shift[X]), scale ($self->shift_y - $shift[Y]), diff --git a/t/gcode.t b/t/gcode.t new file mode 100644 index 0000000000..c41307d4fc --- /dev/null +++ b/t/gcode.t @@ -0,0 +1,20 @@ +use Test::More tests => 1; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Geometry qw(scale); + +{ + local $Slic3r::Config = Slic3r::Config->new_from_defaults; + my $gcodegen = Slic3r::GCode->new(layer_count => 1); + $gcodegen->set_shift(10, 10); + is_deeply $gcodegen->last_pos, [scale -10, scale -10], 'last_pos is shifted correctly'; +} + +__END__ From 4c8c725eece1e6952262cac55dccf832eed8d8bd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:02:34 +0100 Subject: [PATCH 041/114] Add a wkt() method to ExPolygon --- lib/Slic3r/ExPolygon.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index f19cfcd5d4..23ee8cc5c9 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -59,6 +59,12 @@ sub boost_polygon { return Boost::Geometry::Utils::polygon(@$self); } +sub wkt { + my $self = shift; + return sprintf "POLYGON(%s)", + join ',', map "($_)", map { join ',', map "$_->[0] $_->[1]", @$_ } @$self; +} + sub offset { my $self = shift; return Slic3r::Geometry::Clipper::offset($self, @_); From 6cb891f2db7788c0fc5755e48c1987299be12f3e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:09:19 +0100 Subject: [PATCH 042/114] Use Boost::Geometry::Utils for Douglas-Peucker --- lib/Slic3r/Polyline.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 6e3497544a..110472400a 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -68,7 +68,7 @@ sub simplify { my $self = shift; my $tolerance = shift || 10; - @$self = @{ Slic3r::Geometry::douglas_peucker($self, $tolerance) }; + @$self = @{ Boost::Geometry::Utils::linestring_simplify($self, $tolerance) }; bless $_, 'Slic3r::Point' for @$self; } From ae201c8f41a60b7de9776e94b088938362b821b5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:18:55 +0100 Subject: [PATCH 043/114] Optimization: avoid calculating square roots if not needed --- lib/Slic3r/Geometry.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index db3ecb6c81..7151895191 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -7,6 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw( PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel line_point_belongs_to_segment points_coincide distance_between_points + comparable_distance_between_points line_length midpoint point_in_polygon point_in_segment segment_in_segment point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_along_segment polygon_segment_having_point polygon_has_subsegment @@ -114,6 +115,11 @@ sub distance_between_points { return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2); } +sub comparable_distance_between_points { + my ($p1, $p2) = @_; + return (($p1->[X] - $p2->[X])**2) + (($p1->[Y] - $p2->[Y])**2); +} + sub point_line_distance { my ($point, $line) = @_; return distance_between_points($point, $line->[A]) @@ -251,7 +257,7 @@ sub nearest_point_index { my ($nearest_point_index, $distance) = (); for my $i (0..$#$points) { - my $d = distance_between_points($point, $points->[$i]); + my $d = comparable_distance_between_points($point, $points->[$i]); if (!defined $distance || $d < $distance) { $nearest_point_index = $i; $distance = $d; From 2963e54d21a19c90daaff32e2589defed6f5856b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:32:19 +0100 Subject: [PATCH 044/114] Use the faster polygon_multi_linestring_intersection() --- lib/Slic3r/ExPolygon.pm | 5 +---- lib/Slic3r/Fill/Rectilinear.pm | 6 +++--- lib/Slic3r/GCode/MotionPlanner.pm | 5 +---- lib/Slic3r/Polyline.pm | 5 +---- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 9a198c48d7..383a6d8209 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -153,10 +153,7 @@ sub clip_line { my $self = shift; my ($line) = @_; # line must be a Slic3r::Line object - return Boost::Geometry::Utils::polygon_linestring_intersection( - $self->boost_polygon, - $line->boost_linestring, - ); + return Boost::Geometry::Utils::polygon_multi_linestring_intersection($self, [$line]); } sub simplify { diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index ea9f1ddb06..eb633a74a1 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -48,9 +48,9 @@ sub fill_surface { # clip paths against a slightly offsetted expolygon, so that the first and last paths # are kept even if the expolygon has vertical sides - my @paths = @{ Boost::Geometry::Utils::polygon_linestring_intersection( - +($expolygon->offset_ex(scaled_epsilon))[0]->boost_polygon, # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object - Boost::Geometry::Utils::linestring(@vertical_lines), + my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection( + +($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object + [ @vertical_lines ], ) }; for (@paths) { $_->[0][Y] += $overlap_distance; diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index 810a02ae2c..d55faeb2a6 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -92,10 +92,7 @@ sub BUILD { my $intersects = sub { my ($polygon, $line) = @_; - @{Boost::Geometry::Utils::polygon_linestring_intersection( - $polygon->boost_polygon, - $line->boost_linestring, - )} > 0; + @{Boost::Geometry::Utils::polygon_multi_linestring_intersection([$polygon], [$line])} > 0; }; { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 110472400a..9d9926c42a 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -115,10 +115,7 @@ sub clip_with_expolygon { my $self = shift; my ($expolygon) = @_; - my $result = Boost::Geometry::Utils::polygon_linestring_intersection( - $expolygon->boost_polygon, - $self->boost_linestring, - ); + my $result = Boost::Geometry::Utils::polygon_multi_linestring_intersection($expolygon, [$self]); bless $_, 'Slic3r::Polyline' for @$result; bless $_, 'Slic3r::Point' for map @$_, @$result; return @$result; From 721b61e798623178a8ce3c30894e1640979c83f6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:34:09 +0100 Subject: [PATCH 045/114] Require Boost::Geometry::Utils 0.06 --- Build.PL | 2 +- lib/Slic3r.pm | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Build.PL b/Build.PL index 89af11ed83..b4bed05ce0 100644 --- a/Build.PL +++ b/Build.PL @@ -7,7 +7,7 @@ my $build = Module::Build->new( dist_version => '0.1', license => 'perl', requires => { - 'Boost::Geometry::Utils' => '0', + 'Boost::Geometry::Utils' => '0.06', 'Encode::Locale' => '0', 'File::Basename' => '0', 'File::Spec' => '0', diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index f6653ea4d8..f4e26b98a0 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -29,6 +29,7 @@ our $var = "$FindBin::Bin/var"; use Encode; use Encode::Locale; +use Boost::Geometry::Utils 0.06; use Moo 0.091009; use Slic3r::Config; From 4d70748723163a5330b3f46078c88a3e54eefc57 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 00:40:08 +0100 Subject: [PATCH 046/114] Make tests happy about the new Boost::Geometry::Utils working with integers --- t/clean_polylines.t | 5 +++-- t/polyclip.t | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/t/clean_polylines.t b/t/clean_polylines.t index bc7d864e6f..064b7bd3ac 100644 --- a/t/clean_polylines.t +++ b/t/clean_polylines.t @@ -32,8 +32,9 @@ use Slic3r; my $polyline = Slic3r::Polyline->new([ [0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5], ]); - $polyline->simplify(0.25); - is_deeply $polyline, [ [0, 0], [0.5, 0.5], [1.25, -0.25], [1.5, 0.5] ], 'Douglas-Peucker'; + $polyline->scale(100); + $polyline->simplify(25); + is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker'; } { diff --git a/t/polyclip.t b/t/polyclip.t index 285ef45beb..49e3be175a 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -92,7 +92,7 @@ is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved'; { my $intersections = $expolygon->clip_line(Slic3r::Line->new(reverse @$line)); is_deeply $intersections, [ - [ [20, 15], [16, 15] ], + [ [20, 15], [15, 15] ], [ [14, 15], [10, 15] ], ], 'reverse line is clipped to square with hole'; } @@ -144,8 +144,8 @@ is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved'; my $intersections = $expolygon->clip_line($line); is_deeply $intersections, [ - [ [152.742, 288.087], [152.742, 215.179], ], - [ [152.742, 108.088], [152.742, 35.1665] ], + [ [152, 287], [152, 214], ], + [ [152, 107], [152, 35] ], ], 'line is clipped to square with hole'; } From e8cb1f4528d3f0fa82ccd0c32ac8b86408ac3d2f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 10:42:24 +0100 Subject: [PATCH 047/114] Slight addition to avoid_crossing_perimeters tooltip --- lib/Slic3r/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index a8653ab8c0..f57eff5977 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -526,7 +526,7 @@ our $Options = { }, 'avoid_crossing_perimeters' => { label => 'Avoid crossing perimeters', - tooltip => 'Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down the processing times.', + tooltip => 'Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation.', cli => 'avoid-crossing-perimeters!', type => 'bool', default => 0, From 177f1a9bf08f4c30ce42b142b3f74edebd71ad12 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 28 Jan 2013 14:12:01 +0100 Subject: [PATCH 048/114] New raft_layers option. #123 --- README.markdown | 1 + lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/GUI/Tab.pm | 4 ++++ lib/Slic3r/Layer.pm | 14 ++++++++++---- lib/Slic3r/Print/Object.pm | 7 ++++--- slic3r.pl | 1 + 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index bc012abcfa..d0e38e3b91 100644 --- a/README.markdown +++ b/README.markdown @@ -209,6 +209,7 @@ The author of the Silk icon set is Mark James. Spacing between pattern lines (mm, default: 2.5) --support-material-angle Support material angle in degrees (range: 0-90, default: 0) + --raft-layers Number of layers to raise the printed objects by (range: 0+, default: 0) Retraction options: --retract-length Length of retraction in mm when pausing extrusion (default: 1) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index f57eff5977..f22e1fd9ee 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -578,6 +578,14 @@ our $Options = { type => 'i', default => 0, }, + 'raft_layers' => { + label => 'Raft layers', + tooltip => 'Number of total raft layers to insert below the object(s).', + sidetext => 'layers', + cli => 'raft-layers=i', + type => 'i', + default => 0, + }, 'start_gcode' => { label => 'Start G-code', tooltip => 'This start procedure is inserted at the beginning of the output file, right after the temperature control commands for extruder and bed. If Slic3r detects M104 or M190 in your custom codes, such commands will not be prepended automatically. Note that you can use placeholder variables for all Slic3r settings, so you can put a "M104 S[first_layer_temperature]" command wherever you want.', diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index a0ebc1a9af..f48c3782b2 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -456,6 +456,10 @@ sub build { title => 'Support material', options => [qw(support_material support_material_threshold support_material_pattern support_material_spacing support_material_angle)], }, + { + title => 'Raft', + options => [qw(raft_layers)], + }, ]); $self->add_options_page('Notes', 'note.png', optgroups => [ diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index d22188ab5d..e523df21c4 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -2,6 +2,7 @@ package Slic3r::Layer; use Moo; use List::Util qw(first); +use Slic3r::Geometry qw(scale); use Slic3r::Geometry::Clipper qw(union_ex); has 'id' => (is => 'rw', required => 1, trigger => 1); # sequential number of layer, 0-based @@ -32,11 +33,16 @@ sub _trigger_id { sub _build_slice_z { my $self = shift; - if ($self->id == 0) { - return $Slic3r::Config->get_value('first_layer_height') / 2 / &Slic3r::SCALING_FACTOR; + if ($Slic3r::Config->raft_layers == 0) { + if ($self->id == 0) { + return scale $Slic3r::Config->get_value('first_layer_height') / 2; + } + return scale($Slic3r::Config->get_value('first_layer_height') + ($self->id-1 + 0.5) * $Slic3r::Config->layer_height); + } else { + return -1 if $self->id < $Slic3r::Config->raft_layers; + my $object_layer_id = $self->id - $Slic3r::Config->raft_layers; + return scale ($object_layer_id + 0.5) * $Slic3r::Config->layer_height; } - return ($Slic3r::Config->get_value('first_layer_height') + (($self->id-1) * $Slic3r::Config->layer_height) + ($Slic3r::Config->layer_height/2)) - / &Slic3r::SCALING_FACTOR; #/ } # Z used for printing in scaled coordinates diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 1457a66ec8..b1c02bbffa 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -173,9 +173,10 @@ sub slice { } # remove empty layers from bottom - while (@{$self->layers} && !@{$self->layers->[0]->slices} && !map @{$_->thin_walls}, @{$self->layers->[0]->regions}) { - shift @{$self->layers}; - for (my $i = 0; $i <= $#{$self->layers}; $i++) { + my $first_object_layer_id = $Slic3r::Config->raft_layers; + while (@{$self->layers} && !@{$self->layers->[$first_object_layer_id]->slices} && !map @{$_->thin_walls}, @{$self->layers->[$first_object_layer_id]->regions}) { + splice @{$self->layers}, $first_object_layer_id, 1; + for (my $i = $first_object_layer_id; $i <= $#{$self->layers}; $i++) { $self->layers->[$i]->id($i); } } diff --git a/slic3r.pl b/slic3r.pl index c66277c58a..a8a6386b0e 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -257,6 +257,7 @@ $j Spacing between pattern lines (mm, default: $config->{support_material_spacing}) --support-material-angle Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) + --raft-layers Number of layers to raise the printed objects by (range: 0+, default: $config->{raft_layers}) Retraction options: --retract-length Length of retraction in mm when pausing extrusion (default: $config->{retract_length}[0]) From 1de016991071af13cd80b6011fce026669f41ec9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 31 Jan 2013 15:44:55 +0100 Subject: [PATCH 049/114] Multithreaded perimeter generation. #154 --- lib/Slic3r/Print/Object.pm | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 1457a66ec8..ab66f38510 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -247,7 +247,40 @@ sub make_perimeters { } } - $_->make_perimeters for @{$self->layers}; + Slic3r::parallelize( + items => sub { 0 .. ($self->layer_count-1) }, + thread_cb => sub { + my $q = shift; + $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; + my $result = {}; + while (defined (my $layer_id = $q->dequeue)) { + my $layer = $self->layers->[$layer_id]; + $layer->make_perimeters; + $result->{$layer_id} ||= {}; + foreach my $region_id (0 .. $#{$layer->regions}) { + my $layerm = $layer->regions->[$region_id]; + $result->{$layer_id}{$region_id} = { + perimeters => $layerm->perimeters, + fill_surfaces => $layerm->fill_surfaces, + thin_fills => $layerm->thin_fills, + }; + } + } + return $result; + }, + collect_cb => sub { + my $result = shift; + foreach my $layer_id (keys %$result) { + foreach my $region_id (keys %{$result->{$layer_id}}) { + $self->layers->[$layer_id]->regions->[$region_id]->$_($result->{$layer_id}{$region_id}{$_}) + for qw(perimeters fill_surfaces thin_fills); + } + } + }, + no_threads_cb => sub { + $_->make_perimeters for @{$self->layers}; + }, + ); } sub detect_surfaces_type { From 862900d9c1abde4a88e85a0fd0f4e574797a341f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 31 Jan 2013 16:05:51 +0100 Subject: [PATCH 050/114] Scale test coordinates to avoid truncation issues --- t/polyclip.t | 68 ++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/t/polyclip.t b/t/polyclip.t index 49e3be175a..8877d42296 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -24,90 +24,90 @@ is Slic3r::Geometry::point_in_segment([20, 15], [ [10, 10], [20, 20] ]), 0, 'poi #========================================================== my $square = [ # ccw - [10, 10], - [20, 10], - [20, 20], - [10, 20], + [100, 100], + [200, 100], + [200, 200], + [100, 200], ]; -my $line = Slic3r::Line->new([5, 15], [30, 15]); +my $line = Slic3r::Line->new([50, 150], [300, 150]); my $intersection = Slic3r::Geometry::clip_segment_polygon($line, $square); -is_deeply $intersection, [ [10, 15], [20, 15] ], 'line is clipped to square'; +is_deeply $intersection, [ [100, 150], [200, 150] ], 'line is clipped to square'; #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [0, 15], [8, 15] ], $square); +$intersection = Slic3r::Geometry::clip_segment_polygon([ [0, 150], [80, 150] ], $square); is $intersection, undef, 'external lines are ignored 1'; #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [30, 15], [40, 15] ], $square); +$intersection = Slic3r::Geometry::clip_segment_polygon([ [300, 150], [400, 150] ], $square); is $intersection, undef, 'external lines are ignored 2'; #========================================================== -$intersection = Slic3r::Geometry::clip_segment_polygon([ [12, 12], [18, 16] ], $square); -is_deeply $intersection, [ [12, 12], [18, 16] ], 'internal lines are preserved'; +$intersection = Slic3r::Geometry::clip_segment_polygon([ [120, 120], [180, 160] ], $square); +is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserved'; #========================================================== { my $hole_in_square = [ # cw - [14, 14], - [14, 16], - [16, 16], - [16, 14], + [140, 140], + [140, 160], + [160, 160], + [160, 140], ]; my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square); - is $expolygon->encloses_point([10, 10]), 1, 'corner point is recognized'; - is $expolygon->encloses_point([10, 18]), 1, 'point on contour is recognized'; - is $expolygon->encloses_point([14, 15]), 1, 'point on hole contour is recognized'; - is $expolygon->encloses_point([14, 14]), 1, 'point on hole corner is recognized'; + is $expolygon->encloses_point([100, 100]), 1, 'corner point is recognized'; + is $expolygon->encloses_point([100, 180]), 1, 'point on contour is recognized'; + is $expolygon->encloses_point([140, 150]), 1, 'point on hole contour is recognized'; + is $expolygon->encloses_point([140, 140]), 1, 'point on hole corner is recognized'; { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([15,18], [15,15])); + my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,180], [150,150])); is_deeply $intersections, [ - [ [15, 18], [15, 16] ], + [ [150, 180], [150, 160] ], ], 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([15,15], [15,12])); + my $intersections = $expolygon->clip_line(Slic3r::Line->new([150,150], [150,120])); is_deeply $intersections, [ - [ [15, 14], [15, 12] ], + [ [150, 140], [150, 120] ], ], 'line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([12,18], [18,18])); + my $intersections = $expolygon->clip_line(Slic3r::Line->new([120,180], [180,180])); is_deeply $intersections, [ - [ [12,18], [18,18] ], + [ [120,180], [180,180] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line($line); is_deeply $intersections, [ - [ [10, 15], [14, 15] ], - [ [16, 15], [20, 15] ], + [ [100, 150], [140, 150] ], + [ [160, 150], [200, 150] ], ], 'line is clipped to square with hole'; } { my $intersections = $expolygon->clip_line(Slic3r::Line->new(reverse @$line)); is_deeply $intersections, [ - [ [20, 15], [15, 15] ], - [ [14, 15], [10, 15] ], + [ [200, 150], [160, 150] ], + [ [140, 150], [100, 150] ], ], 'reverse line is clipped to square with hole'; } { - my $intersections = $expolygon->clip_line(Slic3r::Line->new([10,18], [20,18])); + my $intersections = $expolygon->clip_line(Slic3r::Line->new([100,180], [200,180])); is_deeply $intersections, [ - [ [10, 18], [20, 18] ], + [ [100, 180], [200, 180] ], ], 'tangent line is clipped to square with hole'; } { - my $polyline = Slic3r::Polyline->new([ [5, 18], [25, 18], [25, 15], [15, 15], [15, 12], [12, 12], [12, 5] ]); + my $polyline = Slic3r::Polyline->new([ [50, 180], [250, 180], [250, 150], [150, 150], [150, 120], [120, 120], [120, 50] ]); is_deeply [ map $_, $polyline->clip_with_expolygon($expolygon) ], [ - [ [10, 18], [20, 18] ], - [ [20, 15], [16, 15] ], - [ [15, 14], [15, 12], [12, 12], [12, 10] ], + [ [100, 180], [200, 180] ], + [ [200, 150], [160, 150] ], + [ [150, 140], [150, 120], [120, 120], [120, 100] ], ], 'polyline is clipped to square with hole'; } } From eae1e7adc2162e13c61a4136a6c7ede6bdc34102 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 1 Feb 2013 22:13:12 +0100 Subject: [PATCH 051/114] Make a solid base below support material on first layer --- lib/Slic3r/Print/Object.pm | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index ab66f38510..44a48be10e 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -701,11 +701,34 @@ sub generate_support_material { my %layer_islands = (); my $process_layer = sub { my ($layer_id) = @_; - my $layer = $self->layers->[$layer_id]; - my $paths = [ $clip_pattern->($layer_id, $layers{$layer_id}, $layer->height) ]; - my $interface_paths = [ $clip_pattern->($layer_id, $layers_interfaces{$layer_id}, $layer->support_material_interface_height) ]; - my $islands = union_ex([ map @$_, map @$_, $layers{$layer_id}, $layers_interfaces{$layer_id} ]); + + my ($paths, $interface_paths) = ([], []); + my $islands = union_ex([ map @$_, map @$_, $layers{$layer_id}, $layers_interfaces{$layer_id} ]); + + # make a solid base on bottom layer + if ($layer_id == 0) { + my $filler = Slic3r::Fill->filler('rectilinear'); + $filler->angle($Slic3r::Config->support_material_angle + 90); + foreach my $expolygon (@$islands) { + my @paths = $filler->fill_surface( + Slic3r::Surface->new(expolygon => $expolygon), + density => 1, + flow_spacing => $self->print->first_layer_support_material_flow->spacing, + ); + my $params = shift @paths; + + push @$paths, map Slic3r::ExtrusionPath->new( + polyline => Slic3r::Polyline->new(@$_), + role => EXTR_ROLE_SUPPORTMATERIAL, + height => undef, + flow_spacing => $params->{flow_spacing}, + ), @paths; + } + } else { + $paths = [ $clip_pattern->($layer_id, $layers{$layer_id}, $layer->height) ]; + $interface_paths = [ $clip_pattern->($layer_id, $layers_interfaces{$layer_id}, $layer->support_material_interface_height) ]; + } return ($paths, $interface_paths, $islands); }; Slic3r::parallelize( From 547dfb857d6249f20d7b297317b73f96c8bc6e5e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 1 Feb 2013 23:45:46 +0100 Subject: [PATCH 052/114] Workaround for Boost clipping not being idempotent and causing lack of support material --- lib/Slic3r/Print/Object.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 44a48be10e..248981c673 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -691,7 +691,7 @@ sub generate_support_material { $_; } map $_->clip_with_expolygon($expolygon), - map $_->clip_with_polygon($expolygon->bounding_box_polygon), + ###map $_->clip_with_polygon($expolygon->bounding_box_polygon), # currently disabled as a workaround for Boost failing at being idempotent @{$support_patterns->[ $layer_id % @$support_patterns ]}; }; return @paths; From d3727a16998ea0ac1fff63669fa92ac5fa13acf7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 2 Feb 2013 00:14:45 +0100 Subject: [PATCH 053/114] Add ability to have cross-hatched rectilinear support material. #509 --- lib/Slic3r/Config.pm | 4 ++-- lib/Slic3r/Print/Object.pm | 12 +++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index f57eff5977..7031dbd905 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -558,8 +558,8 @@ our $Options = { tooltip => 'Pattern used to generate support material.', cli => 'support-material-pattern=s', type => 'select', - values => [qw(rectilinear honeycomb)], - labels => [qw(rectilinear honeycomb)], + values => [qw(rectilinear rectilinear-grid honeycomb)], + labels => ['rectilinear', 'rectilinear grid', 'honeycomb'], default => 'rectilinear', }, 'support_material_spacing' => { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 248981c673..f628da2adc 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -643,9 +643,15 @@ sub generate_support_material { my @support_material_areas = map $_->offset_ex(- 0.5 * $flow->scaled_width), @{union_ex([ map $_->contour, map @$_, values %layers ])}; - my $filler = Slic3r::Fill->filler($Slic3r::Config->support_material_pattern); - $filler->angle($Slic3r::Config->support_material_angle); - { + my $pattern = $Slic3r::Config->support_material_pattern; + my @angles = ($Slic3r::Config->support_material_angle); + if ($pattern eq 'rectilinear-grid') { + $pattern = 'rectilinear'; + push @angles, $angles[0] + 90; + } + my $filler = Slic3r::Fill->filler($pattern); + foreach my $angle (@angles) { + $filler->angle($angle); my @patterns = (); foreach my $expolygon (@support_material_areas) { my @paths = $filler->fill_surface( From 02c0f3658fd8d405627af5f408409683f1e81f20 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 2 Feb 2013 15:37:09 +0100 Subject: [PATCH 054/114] Use 50% density for support base flange --- lib/Slic3r/Print/Object.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index f628da2adc..f5ea07a922 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -719,7 +719,7 @@ sub generate_support_material { foreach my $expolygon (@$islands) { my @paths = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon), - density => 1, + density => 0.5, flow_spacing => $self->print->first_layer_support_material_flow->spacing, ); my $params = shift @paths; From c7b4d99d7e418ce7d29d5e228835ede715b3e523 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 2 Feb 2013 16:16:43 +0100 Subject: [PATCH 055/114] Generate raft automatically even when support material is disabled --- lib/Slic3r/Print.pm | 8 ++++---- lib/Slic3r/Print/Object.pm | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 5f06cf1dbe..38e0a78ade 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -216,7 +216,7 @@ sub init_extruders { } # calculate support material flow - if ($self->config->support_material) { + if ($self->config->support_material || $self->config->raft_layers > 0) { my $extruder = $self->extruders->[$self->config->support_material_extruder-1]; $self->support_material_flow($extruder->make_flow( width => $self->config->support_material_extrusion_width || $self->config->extrusion_width, @@ -413,7 +413,7 @@ sub export_gcode { } # generate support material - if ($Slic3r::Config->support_material) { + if ($Slic3r::Config->support_material || $Slic3r::Config->raft_layers > 0) { $status_cb->(85, "Generating support material"); $_->generate_support_material for @{$self->objects}; } @@ -510,7 +510,7 @@ EOF } } # generate support material - if ($Slic3r::Config->support_material && $layer_id > 0) { + if (($Slic3r::Config->support_material || $self->config->raft_layers > 0) && $layer_id > 0) { my (@supported_slices, @unsupported_slices) = (); foreach my $expolygon (@current_layer_slices) { my $intersection = intersection_ex( @@ -801,7 +801,7 @@ sub write_gcode { # extrude support material before other things because it might use a lower Z # and also because we avoid travelling on other things when printing it - if ($Slic3r::Config->support_material) { + if ($Slic3r::Config->support_material || $self->config->raft_layers > 0) { $gcode .= $gcodegen->move_z($layer->support_material_interface_z) if ($layer->support_interface_fills && @{ $layer->support_interface_fills->paths }); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index b1c02bbffa..6f85752887 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -559,6 +559,8 @@ sub generate_support_material { my @current_support_regions = (); # expolygons we've started to support (i.e. below the empty interface layers) my @queue = (); # the number of items of this array determines the number of empty interface layers for my $i (reverse 0 .. $#{$self->layers}) { + next unless $Slic3r::Config->support_material || ($i <= $Slic3r::Config->raft_layers); # <= because we need to start from the first non-raft layer + my $layer = $self->layers->[$i]; my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef; From 9222526e030fb26e56a93dda4bc5bcfd180cdbda Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 3 Feb 2013 17:23:50 +0100 Subject: [PATCH 056/114] Customizable number of interface layers for support material and raft --- README.markdown | 4 ++ lib/Slic3r/Config.pm | 16 +++++ lib/Slic3r/GUI/Tab.pm | 7 +- lib/Slic3r/Layer.pm | 12 ++-- lib/Slic3r/Print.pm | 12 ++-- lib/Slic3r/Print/Object.pm | 135 +++++++++++++++++++++++++------------ slic3r.pl | 4 ++ 7 files changed, 133 insertions(+), 57 deletions(-) diff --git a/README.markdown b/README.markdown index d0e38e3b91..8a54b2faef 100644 --- a/README.markdown +++ b/README.markdown @@ -209,6 +209,10 @@ The author of the Silk icon set is Mark James. Spacing between pattern lines (mm, default: 2.5) --support-material-angle Support material angle in degrees (range: 0-90, default: 0) + --support-material-interface-layers + Number of perpendicular layers between support material and object (0+, default: 0) + --support-material-interface-spacing + Spacing between interface pattern lines (mm, set 0 to get a solid layer, default: 0) --raft-layers Number of layers to raise the printed objects by (range: 0+, default: 0) Retraction options: diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b8a046d832..2f4b881e5f 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -578,6 +578,22 @@ our $Options = { type => 'i', default => 0, }, + 'support_material_interface_layers' => { + label => 'Interface layers', + tooltip => 'Number of interface layers to insert between the object(s) and support material.', + sidetext => 'layers', + cli => 'support-material-interface-layers=i', + type => 'i', + default => 0, + }, + 'support_material_interface_spacing' => { + label => 'Interface pattern spacing', + tooltip => 'Spacing between interface lines. Set zero to get a solid interface.', + sidetext => 'mm', + cli => 'support-material-interface-spacing=f', + type => 'f', + default => 0, + }, 'raft_layers' => { label => 'Raft layers', tooltip => 'Number of total raft layers to insert below the object(s).', diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index f48c3782b2..aee600f881 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -454,12 +454,17 @@ sub build { $self->add_options_page('Support material', 'building.png', optgroups => [ { title => 'Support material', - options => [qw(support_material support_material_threshold support_material_pattern support_material_spacing support_material_angle)], + options => [qw(support_material support_material_threshold)], }, { title => 'Raft', options => [qw(raft_layers)], }, + { + title => 'Options for support material and raft', + options => [qw(support_material_pattern support_material_spacing support_material_angle + support_material_interface_layers support_material_interface_spacing)], + }, ]); $self->add_options_page('Notes', 'note.png', optgroups => [ diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index e523df21c4..cedd234c2c 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -22,7 +22,7 @@ has 'slices' => (is => 'rw'); # ordered collection of extrusion paths to fill surfaces for support material has 'support_islands' => (is => 'rw'); has 'support_fills' => (is => 'rw'); -has 'support_interface_fills' => (is => 'rw'); +has 'support_contact_fills' => (is => 'rw'); sub _trigger_id { my $self = shift; @@ -59,8 +59,8 @@ sub _build_height { sub _build_flow { $Slic3r::flow } -# layer height of interface paths in unscaled coordinates -sub support_material_interface_height { +# layer height of contact paths in unscaled coordinates +sub support_material_contact_height { my $self = shift; return $self->height if $self->id == 0; @@ -72,10 +72,10 @@ sub support_material_interface_height { return 2*$self->height - $self->flow->nozzle_diameter; } -# Z used for printing support material interface in scaled coordinates -sub support_material_interface_z { +# Z used for printing support material contact in scaled coordinates +sub support_material_contact_z { my $self = shift; - return $self->print_z - ($self->height - $self->support_material_interface_height) / &Slic3r::SCALING_FACTOR; + return $self->print_z - ($self->height - $self->support_material_contact_height) / &Slic3r::SCALING_FACTOR; } sub region { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 38e0a78ade..2c5f2acbe7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -749,7 +749,7 @@ sub write_gcode { if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; } - # set new layer, but don't move Z as support material interfaces may need an intermediate one + # set new layer, but don't move Z as support material contact areas may need an intermediate one $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); $gcodegen->elapsed_time(0); @@ -802,12 +802,12 @@ sub write_gcode { # extrude support material before other things because it might use a lower Z # and also because we avoid travelling on other things when printing it if ($Slic3r::Config->support_material || $self->config->raft_layers > 0) { - $gcode .= $gcodegen->move_z($layer->support_material_interface_z) - if ($layer->support_interface_fills && @{ $layer->support_interface_fills->paths }); + $gcode .= $gcodegen->move_z($layer->support_material_contact_z) + if ($layer->support_contact_fills && @{ $layer->support_contact_fills->paths }); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); - if ($layer->support_interface_fills) { - $gcode .= $gcodegen->extrude_path($_, 'support material interface') - for $layer->support_interface_fills->shortest_path($gcodegen->last_pos); + if ($layer->support_contact_fills) { + $gcode .= $gcodegen->extrude_path($_, 'support material contact area') + for $layer->support_contact_fills->shortest_path($gcodegen->last_pos); } $gcode .= $gcodegen->move_z($layer->print_z); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 640010a367..edac073d13 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -587,10 +587,11 @@ sub generate_support_material { # determine support regions in each layer (for upper layers) Slic3r::debugf "Detecting regions\n"; my %layers = (); # this represents the areas of each layer having to support upper layers (excluding interfaces) - my %layers_interfaces = (); # this represents the areas of each layer having an overhang in the immediately upper layer + my %layers_interfaces = (); # this represents the areas of each layer to be filled with interface pattern, excluding the contact areas which are stored separately + my %layers_contact_areas = (); # this represents the areas of each layer having an overhang in the immediately upper layer { my @current_support_regions = (); # expolygons we've started to support (i.e. below the empty interface layers) - my @queue = (); # the number of items of this array determines the number of empty interface layers + my @upper_layers_overhangs = (map [], 1..$Slic3r::Config->support_material_interface_layers); for my $i (reverse 0 .. $#{$self->layers}) { next unless $Slic3r::Config->support_material || ($i <= $Slic3r::Config->raft_layers); # <= because we need to start from the first non-raft layer @@ -599,31 +600,48 @@ sub generate_support_material { my @current_layer_offsetted_slices = map $_->offset_ex($distance_from_object), @{$layer->slices}; - # $queue[-1] contains the overhangs of the upper layer, regardless of any empty interface layers - # $queue[0] contains the overhangs of the first upper layer above the empty interface layers - $layers_interfaces{$i} = diff_ex( - [ map @$_, @{ $queue[-1] || [] } ], + # $upper_layers_overhangs[-1] contains the overhangs of the upper layer, regardless of any interface layers + # $upper_layers_overhangs[0] contains the overhangs of the first upper layer above the interface layers + + # we only consider the overhangs of the upper layer to define contact areas of the current one + $layers_contact_areas{$i} = diff_ex( + [ map @$_, @{ $upper_layers_overhangs[-1] || [] } ], [ map @$_, @current_layer_offsetted_slices ], ); + $_->simplify($flow->scaled_spacing) for @{$layers_contact_areas{$i}}; - # step 1: generate support material in current layer (for upper layers) - push @current_support_regions, @{ shift @queue } if @queue && $i < $#{$self->layers}; + # to define interface regions of this layer we consider the overhangs of all the upper layers + # minus the first one + $layers_interfaces{$i} = diff_ex( + [ map @$_, map @$_, @upper_layers_overhangs[0 .. $#upper_layers_overhangs-1] ], + [ + (map @$_, @current_layer_offsetted_slices), + (map @$_, @{ $layers_contact_areas{$i} }), + ], + ); + $_->simplify($flow->scaled_spacing) for @{$layers_interfaces{$i}}; + # generate support material in current layer (for upper layers) @current_support_regions = @{diff_ex( - [ map @$_, @current_support_regions ], + [ + (map @$_, @current_support_regions), + (map @$_, @{ $upper_layers_overhangs[-1] || [] }), # only considering -1 instead of the whole array contents is just an optimization + ], [ map @$_, @{$layer->slices} ], )}; + shift @upper_layers_overhangs; $layers{$i} = diff_ex( [ map @$_, @current_support_regions ], [ (map @$_, @current_layer_offsetted_slices), + (map @$_, @{ $layers_contact_areas{$i} }), (map @$_, @{ $layers_interfaces{$i} }), ], ); $_->simplify($flow->scaled_spacing) for @{$layers{$i}}; - # step 2: get layer overhangs and put them into queue for adding support inside lower layers + # get layer overhangs and put them into queue for adding support inside lower layers; # we need an angle threshold for this my @overhangs = (); if ($lower_layer) { @@ -633,17 +651,23 @@ sub generate_support_material { 1, )}; } - push @queue, [@overhangs]; + push @upper_layers_overhangs, [@overhangs]; + + if ($Slic3r::debug) { + printf "Layer %d has %d generic support areas, %d normal interface areas, %d contact areas\n", + $i, scalar(@{$layers{$i}}), scalar(@{$layers_interfaces{$i}}), scalar(@{$layers_contact_areas{$i}}); + } } } return if !map @$_, values %layers; # generate paths for the pattern that we're going to use Slic3r::debugf "Generating patterns\n"; - my $support_patterns = []; # in case we want cross-hatching + my $support_patterns = []; + my $support_interface_patterns = []; { - # 0.5 makes sure the paths don't get clipped externally when applying them to layers - my @support_material_areas = map $_->offset_ex(- 0.5 * $flow->scaled_width), + # 0.5 ensures the paths don't get clipped externally when applying them to layers + my @areas = map $_->offset_ex(- 0.5 * $flow->scaled_width), @{union_ex([ map $_->contour, map @$_, values %layers ])}; my $pattern = $Slic3r::Config->support_material_pattern; @@ -653,33 +677,46 @@ sub generate_support_material { push @angles, $angles[0] + 90; } my $filler = Slic3r::Fill->filler($pattern); + my $make_pattern = sub { + my ($expolygon, $density) = @_; + + my @paths = $filler->fill_surface( + Slic3r::Surface->new(expolygon => $expolygon), + density => $density, + flow_spacing => $flow->spacing, + ); + my $params = shift @paths; + + return map Slic3r::ExtrusionPath->new( + polyline => Slic3r::Polyline->new(@$_), + role => EXTR_ROLE_SUPPORTMATERIAL, + height => undef, + flow_spacing => $params->{flow_spacing}, + ), @paths; + }; foreach my $angle (@angles) { $filler->angle($angle); - my @patterns = (); - foreach my $expolygon (@support_material_areas) { - my @paths = $filler->fill_surface( - Slic3r::Surface->new(expolygon => $expolygon), - density => $flow->spacing / $pattern_spacing, - flow_spacing => $flow->spacing, - ); - my $params = shift @paths; - - push @patterns, - map Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polyline->new(@$_), - role => EXTR_ROLE_SUPPORTMATERIAL, - height => undef, - flow_spacing => $params->{flow_spacing}, - ), @paths; + { + my $density = $flow->spacing / $pattern_spacing; + push @$support_patterns, [ map $make_pattern->($_, $density), @areas ]; + } + + if ($Slic3r::Config->support_material_interface_layers > 0) { + # if pattern is not cross-hatched, rotate the interface pattern by 90° degrees + $filler->angle($angle + 90) if @angles == 1; + + my $spacing = $Slic3r::Config->support_material_interface_spacing; + my $density = $spacing == 0 ? 1 : $flow->spacing / $spacing; + push @$support_interface_patterns, [ map $make_pattern->($_, $density), @areas ]; } - push @$support_patterns, [@patterns]; } if (0) { require "Slic3r/SVG.pm"; Slic3r::SVG::output("support_$_.svg", polylines => [ map $_->polyline, map @$_, $support_patterns->[$_] ], - polygons => [ map @$_, @support_material_areas ], + red_polylines => [ map $_->polyline, map @$_, $support_interface_patterns->[$_] ], + polygons => [ map @$_, @areas ], ) for 0 .. $#$support_patterns; } } @@ -688,32 +725,39 @@ sub generate_support_material { Slic3r::debugf "Applying patterns\n"; { my $clip_pattern = sub { - my ($layer_id, $expolygons, $height) = @_; + my ($layer_id, $expolygons, $height, $is_interface) = @_; my @paths = (); foreach my $expolygon (@$expolygons) { push @paths, map $_->pack, map { $_->height($height); + + # useless line because this coderef isn't called for layer 0 anymore; + # let's keep it here just in case we want to make the base flange optional + # in the future $_->flow_spacing($self->print->first_layer_support_material_flow->spacing) if $layer_id == 0; + $_; } map $_->clip_with_expolygon($expolygon), ###map $_->clip_with_polygon($expolygon->bounding_box_polygon), # currently disabled as a workaround for Boost failing at being idempotent - @{$support_patterns->[ $layer_id % @$support_patterns ]}; + ($is_interface && @$support_interface_patterns) + ? @{$support_interface_patterns->[ $layer_id % @$support_interface_patterns ]} + : @{$support_patterns->[ $layer_id % @$support_patterns ]}; }; return @paths; }; my %layer_paths = (); - my %layer_interface_paths = (); + my %layer_contact_paths = (); my %layer_islands = (); my $process_layer = sub { my ($layer_id) = @_; my $layer = $self->layers->[$layer_id]; - my ($paths, $interface_paths) = ([], []); - my $islands = union_ex([ map @$_, map @$_, $layers{$layer_id}, $layers_interfaces{$layer_id} ]); + my ($paths, $contact_paths) = ([], []); + my $islands = union_ex([ map @$_, map @$_, $layers{$layer_id}, $layers_contact_areas{$layer_id} ]); # make a solid base on bottom layer if ($layer_id == 0) { @@ -735,10 +779,13 @@ sub generate_support_material { ), @paths; } } else { - $paths = [ $clip_pattern->($layer_id, $layers{$layer_id}, $layer->height) ]; - $interface_paths = [ $clip_pattern->($layer_id, $layers_interfaces{$layer_id}, $layer->support_material_interface_height) ]; + $paths = [ + $clip_pattern->($layer_id, $layers{$layer_id}, $layer->height), + $clip_pattern->($layer_id, $layers_interfaces{$layer_id}, $layer->height, 1), + ]; + $contact_paths = [ $clip_pattern->($layer_id, $layers_contact_areas{$layer_id}, $layer->support_material_contact_height, 1) ]; } - return ($paths, $interface_paths, $islands); + return ($paths, $contact_paths, $islands); }; Slic3r::parallelize( items => [ keys %layers ], @@ -753,10 +800,10 @@ sub generate_support_material { }, collect_cb => sub { my $result = shift; - ($layer_paths{$_}, $layer_interface_paths{$_}, $layer_islands{$_}) = @{$result->{$_}} for keys %$result; + ($layer_paths{$_}, $layer_contact_paths{$_}, $layer_islands{$_}) = @{$result->{$_}} for keys %$result; }, no_threads_cb => sub { - ($layer_paths{$_}, $layer_interface_paths{$_}, $layer_islands{$_}) = $process_layer->($_) for keys %layers; + ($layer_paths{$_}, $layer_contact_paths{$_}, $layer_islands{$_}) = $process_layer->($_) for keys %layers; }, ); @@ -764,9 +811,9 @@ sub generate_support_material { my $layer = $self->layers->[$layer_id]; $layer->support_islands($layer_islands{$layer_id}); $layer->support_fills(Slic3r::ExtrusionPath::Collection->new); - $layer->support_interface_fills(Slic3r::ExtrusionPath::Collection->new); + $layer->support_contact_fills(Slic3r::ExtrusionPath::Collection->new); push @{$layer->support_fills->paths}, @{$layer_paths{$layer_id}}; - push @{$layer->support_interface_fills->paths}, @{$layer_interface_paths{$layer_id}}; + push @{$layer->support_contact_fills->paths}, @{$layer_contact_paths{$layer_id}}; } } } diff --git a/slic3r.pl b/slic3r.pl index a8a6386b0e..fcd33eccd0 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -257,6 +257,10 @@ $j Spacing between pattern lines (mm, default: $config->{support_material_spacing}) --support-material-angle Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) + --support-material-interface-layers + Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) + --support-material-interface-spacing + Spacing between interface pattern lines (mm, set 0 to get a solid layer, default: $config->{support_material_interface_spacing}) --raft-layers Number of layers to raise the printed objects by (range: 0+, default: $config->{raft_layers}) Retraction options: From 3794d51cc72ecb178121104f786f5eab22e556ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Feb 2013 14:21:59 +0100 Subject: [PATCH 057/114] Add unit test for polyline_lines --- t/geometry.t | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/t/geometry.t b/t/geometry.t index 0ff61e3752..f5c2f412eb 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 20; +plan tests => 21; BEGIN { use FindBin; @@ -154,4 +154,14 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po is polygon_is_convex($convex1), 0, 'concave polygon'; } +#========================================================== + +{ + my $polyline = Slic3r::Polyline->new([0, 0], [10, 0], [20, 0]); + is_deeply [$polyline->lines], [ + [ [0, 0], [10, 0] ], + [ [10, 0], [20, 0] ], + ], 'polyline_lines'; +} + #========================================================== \ No newline at end of file From 7d08796b0a976ecddd71d860a5055dc7702e65d3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Feb 2013 14:27:23 +0100 Subject: [PATCH 058/114] Remove sporadic duplicate useless lines. #772 --- lib/Slic3r/Polygon.pm | 2 +- t/geometry.t | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 68f1002eb6..4cc51d6105 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -141,7 +141,7 @@ sub split_at_index { my $self = shift; my ($index) = @_; - return (ref $self)->new( + return Slic3r::Polyline->new( @$self[$index .. $#$self], @$self[0 .. $index], ); diff --git a/t/geometry.t b/t/geometry.t index f5c2f412eb..c0e1f753ba 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 21; +plan tests => 23; BEGIN { use FindBin; @@ -164,4 +164,13 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po ], 'polyline_lines'; } +#========================================================== + +{ + my $polyline = Slic3r::Polygon->new([0, 0], [10, 0], [5, 5]); + my $result = $polyline->split_at_index(1); + is ref($result), 'Slic3r::Polyline', 'split_at_index returns polyline'; + is_deeply $result, [ [10, 0], [5, 5], [0, 0], [10, 0] ], 'split_at_index'; +} + #========================================================== \ No newline at end of file From c8100ed0999e9816ba15a54c40040ecf3514bd34 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Feb 2013 15:05:56 +0100 Subject: [PATCH 059/114] Disable randomize starting points by default --- lib/Slic3r/Config.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 2f4b881e5f..7247d1894a 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -522,7 +522,7 @@ our $Options = { tooltip => 'Start each layer from a different vertex to prevent plastic build-up on the same corner.', cli => 'randomize-start!', type => 'bool', - default => 1, + default => 0, }, 'avoid_crossing_perimeters' => { label => 'Avoid crossing perimeters', From 8efe46e58de31bd505292d4b4b9a319c8c4d3faf Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Feb 2013 15:48:57 +0100 Subject: [PATCH 060/114] New option to enforce support material on a given number of layers to improve adhesion of objects with poor footprint --- README.markdown | 3 +++ lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/GUI/Tab.pm | 2 +- lib/Slic3r/Print.pm | 16 ++++++++++++---- lib/Slic3r/Print/Object.pm | 13 +++++++++---- slic3r.pl | 3 +++ 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.markdown b/README.markdown index 8a54b2faef..21498b0f58 100644 --- a/README.markdown +++ b/README.markdown @@ -214,6 +214,9 @@ The author of the Silk icon set is Mark James. --support-material-interface-spacing Spacing between interface pattern lines (mm, set 0 to get a solid layer, default: 0) --raft-layers Number of layers to raise the printed objects by (range: 0+, default: 0) + --support-material-enforce-layers + Enforce support material on the specified number of layers from bottom, + regardless of --support-material and threshold (0+, default: 0) Retraction options: --retract-length Length of retraction in mm when pausing extrusion (default: 1) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 7247d1894a..f693ba00ce 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -594,6 +594,14 @@ our $Options = { type => 'f', default => 0, }, + 'support_material_enforce_layers' => { + label => 'Enforce support for the first', + tooltip => 'Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate.', + sidetext => 'layers', + cli => 'support-material-enforce-layers=f', + type => 'i', + default => 0, + }, 'raft_layers' => { label => 'Raft layers', tooltip => 'Number of total raft layers to insert below the object(s).', diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index aee600f881..c9b292b725 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -454,7 +454,7 @@ sub build { $self->add_options_page('Support material', 'building.png', optgroups => [ { title => 'Support material', - options => [qw(support_material support_material_threshold)], + options => [qw(support_material support_material_threshold support_material_enforce_layers)], }, { title => 'Raft', diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 2c5f2acbe7..5cb6b42334 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -19,6 +19,7 @@ has 'extruders' => (is => 'rw', default => sub {[]}); has 'regions' => (is => 'rw', default => sub {[]}); has 'support_material_flow' => (is => 'rw'); has 'first_layer_support_material_flow' => (is => 'rw'); +has 'has_support_material' => (is => 'lazy'); # ordered collection of extrusion paths to build skirt loops has 'skirt' => ( @@ -62,6 +63,13 @@ sub _trigger_config { $self->config->set('extrusion_axis', '') if $self->config->gcode_flavor eq 'no-extrusion'; } +sub _build_has_support_material { + my $self = shift; + return $self->config->support_material + || $self->config->raft_layers > 0 + || $self->config->support_material_enforce_layers > 0; +} + sub add_model { my $self = shift; my ($model) = @_; @@ -216,7 +224,7 @@ sub init_extruders { } # calculate support material flow - if ($self->config->support_material || $self->config->raft_layers > 0) { + if ($self->has_support_material) { my $extruder = $self->extruders->[$self->config->support_material_extruder-1]; $self->support_material_flow($extruder->make_flow( width => $self->config->support_material_extrusion_width || $self->config->extrusion_width, @@ -413,7 +421,7 @@ sub export_gcode { } # generate support material - if ($Slic3r::Config->support_material || $Slic3r::Config->raft_layers > 0) { + if ($self->has_support_material) { $status_cb->(85, "Generating support material"); $_->generate_support_material for @{$self->objects}; } @@ -510,7 +518,7 @@ EOF } } # generate support material - if (($Slic3r::Config->support_material || $self->config->raft_layers > 0) && $layer_id > 0) { + if ($self->has_support_material && $layer_id > 0) { my (@supported_slices, @unsupported_slices) = (); foreach my $expolygon (@current_layer_slices) { my $intersection = intersection_ex( @@ -801,7 +809,7 @@ sub write_gcode { # extrude support material before other things because it might use a lower Z # and also because we avoid travelling on other things when printing it - if ($Slic3r::Config->support_material || $self->config->raft_layers > 0) { + if ($self->has_support_material) { $gcode .= $gcodegen->move_z($layer->support_material_contact_z) if ($layer->support_contact_fills && @{ $layer->support_contact_fills->paths }); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index edac073d13..e57620a876 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -593,7 +593,9 @@ sub generate_support_material { my @current_support_regions = (); # expolygons we've started to support (i.e. below the empty interface layers) my @upper_layers_overhangs = (map [], 1..$Slic3r::Config->support_material_interface_layers); for my $i (reverse 0 .. $#{$self->layers}) { - next unless $Slic3r::Config->support_material || ($i <= $Slic3r::Config->raft_layers); # <= because we need to start from the first non-raft layer + next unless $Slic3r::Config->support_material + || ($i <= $Slic3r::Config->raft_layers) # <= because we need to start from the first non-raft layer + || ($i <= $Slic3r::Config->support_material_enforce_layers + $Slic3r::Config->raft_layers); my $layer = $self->layers->[$i]; my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef; @@ -635,7 +637,6 @@ sub generate_support_material { [ map @$_, @current_support_regions ], [ (map @$_, @current_layer_offsetted_slices), - (map @$_, @{ $layers_contact_areas{$i} }), (map @$_, @{ $layers_interfaces{$i} }), ], ); @@ -645,6 +646,10 @@ sub generate_support_material { # we need an angle threshold for this my @overhangs = (); if ($lower_layer) { + # consider all overhangs regardless of their angle if we're told to enforce support on this layer + my $distance = $i <= ($Slic3r::Config->support_material_enforce_layers + $Slic3r::Config->raft_layers) + ? 0 + : $overhang_width; @overhangs = map $_->offset_ex(2 * $overhang_width), @{diff_ex( [ map @$_, map $_->offset_ex(-$overhang_width), @{$layer->slices} ], [ map @$_, @{$lower_layer->slices} ], @@ -654,8 +659,8 @@ sub generate_support_material { push @upper_layers_overhangs, [@overhangs]; if ($Slic3r::debug) { - printf "Layer %d has %d generic support areas, %d normal interface areas, %d contact areas\n", - $i, scalar(@{$layers{$i}}), scalar(@{$layers_interfaces{$i}}), scalar(@{$layers_contact_areas{$i}}); + printf "Layer %d (z = %.2f) has %d generic support areas, %d normal interface areas, %d contact areas\n", + $i, unscale($layer->print_z), scalar(@{$layers{$i}}), scalar(@{$layers_interfaces{$i}}), scalar(@{$layers_contact_areas{$i}}); } } } diff --git a/slic3r.pl b/slic3r.pl index fcd33eccd0..04aa1be9e6 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -262,6 +262,9 @@ $j --support-material-interface-spacing Spacing between interface pattern lines (mm, set 0 to get a solid layer, default: $config->{support_material_interface_spacing}) --raft-layers Number of layers to raise the printed objects by (range: 0+, default: $config->{raft_layers}) + --support-material-enforce-layers + Enforce support material on the specified number of layers from bottom, + regardless of --support-material and threshold (0+, default: $config->{support_material_enforce_layers}) Retraction options: --retract-length Length of retraction in mm when pausing extrusion (default: $config->{retract_length}[0]) From 11637d527a8dc35562e85933cedab3fe1100e14a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Feb 2013 19:33:30 +0100 Subject: [PATCH 061/114] Bugfix: avoid-crossing-perimeters was failing on layers only containing single walls. #978 --- lib/Slic3r/Print.pm | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 5cb6b42334..3c2ca71bbf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -727,11 +727,17 @@ sub write_gcode { # compute the offsetted convex hull for each object and repeat it for each copy. my @islands = (); foreach my $obj_idx (0 .. $#{$self->objects}) { - my @island = Slic3r::ExPolygon->new(convex_hull([ + my $convex_hull = convex_hull([ map @{$_->contour}, map @{$_->slices}, @{$self->objects->[$obj_idx]->layers}, - ]))->translate(scale $shift[X], scale $shift[Y])->offset_ex(scale $distance_from_objects, 1, JT_SQUARE); - foreach my $copy (@{ $self->objects->[$obj_idx]->copies }) { - push @islands, map $_->clone->translate(@$copy), @island; + ]); + # discard layers only containing thin walls (offset would fail on an empty polygon) + if (@$convex_hull) { + my @island = Slic3r::ExPolygon->new($convex_hull) + ->translate(scale $shift[X], scale $shift[Y]) + ->offset_ex(scale $distance_from_objects, 1, JT_SQUARE); + foreach my $copy (@{ $self->objects->[$obj_idx]->copies }) { + push @islands, map $_->clone->translate(@$copy), @island; + } } } $gcodegen->external_mp(Slic3r::GCode::MotionPlanner->new( From 090e3b1fe36f4de311dba2ba15836476ed79564d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Feb 2013 17:27:45 +0100 Subject: [PATCH 062/114] Rename shortest_path to chained_path, which reflects its algorithm more correctly --- lib/Slic3r/ExtrusionPath/Collection.pm | 6 +++--- lib/Slic3r/Fill.pm | 4 ++-- lib/Slic3r/Fill/Honeycomb.pm | 2 +- lib/Slic3r/Fill/Rectilinear.pm | 2 +- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/GCode/MotionPlanner.pm | 2 +- lib/Slic3r/Geometry.pm | 4 ++-- lib/Slic3r/Layer/Region.pm | 8 ++++---- lib/Slic3r/Polyline.pm | 2 +- lib/Slic3r/Print.pm | 6 +++--- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index f369081500..c6bfe37a9e 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -8,19 +8,19 @@ sub endpoints { return [ map $_->endpoints, @{$self->paths} ]; } -sub shortest_path { +sub chained_path { my $self = shift; my ($start_near) = @_; # make sure we pass the same path objects to the Collection constructor - # and the ->shortest_path() method because the latter will reverse the + # and the ->chained_path() method because the latter will reverse the # paths in-place when needed and we need to return them that way my @paths = map $_->unpack, @{$self->paths}; my $collection = Slic3r::Polyline::Collection->new( polylines => [ map $_->polyline, @paths ], ); - return $collection->shortest_path($start_near, \@paths); + return $collection->chained_path($start_near, \@paths); } sub cleanup { diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 7c6183083b..f3f3712706 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -12,7 +12,7 @@ use Slic3r::Fill::OctagramSpiral; use Slic3r::Fill::PlanePath; use Slic3r::Fill::Rectilinear; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(X Y PI scale shortest_path); +use Slic3r::Geometry qw(X Y PI scale chained_path); use Slic3r::Geometry::Clipper qw(union_ex diff_ex); use Slic3r::Surface ':types'; @@ -183,7 +183,7 @@ sub make_fill { push @fills_ordering_points, map $_->unpack->points->[0], @{$layer->thin_fills}; # organize infill paths using a shortest path search - @fills = @{shortest_path([ + @fills = @{chained_path([ map [ $fills_ordering_points[$_], $fills[$_] ], 0..$#fills, ])}; diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 5db50c3019..4d450370ed 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -93,7 +93,7 @@ sub fill_surface { )}; return { flow_spacing => $params{flow_spacing} }, - Slic3r::Polyline::Collection->new(polylines => \@paths)->shortest_path; + Slic3r::Polyline::Collection->new(polylines => \@paths)->chained_path; } 1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index eb633a74a1..c074d3b1c9 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -73,7 +73,7 @@ sub fill_surface { } : sub { abs($_[X] - $distance_between_lines) <= $tolerance && $_[Y] <= $diagonal_distance }; - foreach my $path ($collection->shortest_path) { + foreach my $path ($collection->chained_path) { if (@paths) { my @distance = map abs($path->[0][$_] - $paths[-1][-1][$_]), (X,Y); diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 3633c3c5bb..7e98a26e2b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -270,7 +270,7 @@ sub travel_to { my $mp = shift; return join '', map $self->G0($_->[B], undef, 0, $comment || ""), - $mp->shortest_path($self->last_pos, $point)->lines; + $mp->chained_path($self->last_pos, $point)->lines; }; if ($self->new_object) { diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index d55faeb2a6..f6ea5f77a0 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -217,7 +217,7 @@ sub find_node { } } -sub shortest_path { +sub chained_path { my $self = shift; my ($from, $to) = @_; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 7151895191..2132a978e3 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -18,7 +18,7 @@ our @EXPORT_OK = qw( longest_segment angle3points three_points_aligned line_direction polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges - shortest_path collinear scale unscale merge_collinear_lines + chained_path collinear scale unscale merge_collinear_lines rad2deg_dir bounding_box_center line_intersects_any douglas_peucker polyline_remove_short_segments normal triangle_normal polygon_is_convex scaled_epsilon bounding_box_3D size_3D size_2D @@ -803,7 +803,7 @@ sub polyline_remove_short_segments { # item is the point to be used for the shortest path, and the second # one is the value to be returned in output (if the second item # is not provided, the point will be returned) -sub shortest_path { +sub chained_path { my ($items, $start_near) = @_; my %values = map +($_->[0] => $_->[1] || $_->[0]), @$items; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 37d9b7723b..6a925ddbb9 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -2,7 +2,7 @@ package Slic3r::Layer::Region; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale shortest_path); +use Slic3r::Geometry qw(scale chained_path); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); use Slic3r::Surface ':types'; @@ -167,7 +167,7 @@ sub make_perimeters { my @perimeters = (); # one item per depth; each item # organize islands using a shortest path search - my @surfaces = @{shortest_path([ + my @surfaces = @{chained_path([ map [ $_->contour->[0], $_ ], @{$self->slices}, ])}; @@ -326,7 +326,7 @@ sub make_perimeters { my @hole_depths = map [ map $_->holes, @$_ ], @$island; # organize the outermost hole loops using a shortest path search - @{$hole_depths[0]} = @{shortest_path([ + @{$hole_depths[0]} = @{chained_path([ map [ $_->[0], $_ ], @{$hole_depths[0]}, ])}; @@ -391,7 +391,7 @@ sub make_perimeters { flow_spacing => $self->perimeter_flow->spacing, ); } @{ $self->thin_walls } - ])->shortest_path; + ])->chained_path; } sub _add_perimeter { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 9d9926c42a..078d3a1e9a 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -193,7 +193,7 @@ has 'polylines' => (is => 'ro', default => sub { [] }); # If the second argument is provided, this method will return its items sorted # instead of returning the actual sorted polylines. # Note that our polylines will be reversed in place when necessary. -sub shortest_path { +sub chained_path { my $self = shift; my ($start_near, $items) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 3c2ca71bbf..03fc77d883 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -821,13 +821,13 @@ sub write_gcode { $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); if ($layer->support_contact_fills) { $gcode .= $gcodegen->extrude_path($_, 'support material contact area') - for $layer->support_contact_fills->shortest_path($gcodegen->last_pos); + for $layer->support_contact_fills->chained_path($gcodegen->last_pos); } $gcode .= $gcodegen->move_z($layer->print_z); if ($layer->support_fills) { $gcode .= $gcodegen->extrude_path($_, 'support material') - for $layer->support_fills->shortest_path($gcodegen->last_pos); + for $layer->support_fills->chained_path($gcodegen->last_pos); } } @@ -854,7 +854,7 @@ sub write_gcode { for my $fill (@{ $layerm->fills }) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { $gcode .= $gcodegen->extrude($_, 'fill') - for $fill->shortest_path($gcodegen->last_pos); + for $fill->chained_path($gcodegen->last_pos); } else { $gcode .= $gcodegen->extrude($fill, 'fill') ; } From 73a661e6c159ac0e8310438b15733f3a676a65e5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Feb 2013 19:41:56 +0100 Subject: [PATCH 063/114] shortest_path() in MotionPlanner was actually correctly named shortest_path() --- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/GCode/MotionPlanner.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 7e98a26e2b..3633c3c5bb 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -270,7 +270,7 @@ sub travel_to { my $mp = shift; return join '', map $self->G0($_->[B], undef, 0, $comment || ""), - $mp->chained_path($self->last_pos, $point)->lines; + $mp->shortest_path($self->last_pos, $point)->lines; }; if ($self->new_object) { diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index f6ea5f77a0..d55faeb2a6 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -217,7 +217,7 @@ sub find_node { } } -sub chained_path { +sub shortest_path { my $self = shift; my ($from, $to) = @_; From 4f91bed904d525666858478a8dc0c01053a6ab14 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Feb 2013 21:59:18 +0100 Subject: [PATCH 064/114] Optimize cycling in get_layer_range() --- lib/Slic3r/Print/Object.pm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e57620a876..ceeda2646f 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -34,11 +34,18 @@ sub get_layer_range { my $self = shift; my ($min_z, $max_z) = @_; + # $min_layer is the uppermost layer having slice_z <= $min_z + # $max_layer is the lowermost layer having slice_z >= $max_z my ($min_layer, $max_layer) = (0, undef); - for my $layer (@{$self->layers}) { - $min_layer = $layer->id if $layer->slice_z <= $min_z; - if ($layer->slice_z >= $max_z) { - $max_layer = $layer->id; + for my $i (0 .. $#{$self->layers}) { + if ($self->layers->[$i]->slice_z >= $min_z) { + $min_layer = $i - 1; + for my $k ($i .. $#{$self->layers}) { + if ($self->layers->[$k]->slice_z >= $max_z) { + $max_layer = $k - 1; + last; + } + } last; } } From e7139c514dc920ed5d56bb97affc50b7990ce306 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 6 Feb 2013 10:21:23 +0100 Subject: [PATCH 065/114] Rename shortest_path() in tests too. #980 --- t/fill.t | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/t/fill.t b/t/fill.t index 5576374a8f..b5ebd5dcf2 100644 --- a/t/fill.t +++ b/t/fill.t @@ -48,9 +48,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } Slic3r::Polyline->new([0,10], [0,8], [0,5]), ]); is_deeply - [ map $_->[Y], map @$_, $collection->shortest_path(Slic3r::Point->new(0,30)) ], + [ map $_->[Y], map @$_, $collection->chained_path(Slic3r::Point->new(0,30)) ], [20, 18, 15, 10, 8, 5], - 'shortest path'; + 'chained path'; } { @@ -59,9 +59,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } Slic3r::Polyline->new([10,5], [15,5], [20,5]), ]); is_deeply - [ map $_->[X], map @$_, $collection->shortest_path(Slic3r::Point->new(30,0)) ], + [ map $_->[X], map @$_, $collection->chained_path(Slic3r::Point->new(30,0)) ], [reverse 4, 10, 15, 10, 15, 20], - 'shortest path'; + 'chained path'; } { @@ -71,9 +71,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } Slic3r::Polyline->new([0,10], [0,8], [0,5]), ]); is_deeply - [ map $_->[Y], map @{$_->unpack->polyline}, $collection->shortest_path(Slic3r::Point->new(0,30)) ], + [ map $_->[Y], map @{$_->unpack->polyline}, $collection->chained_path(Slic3r::Point->new(0,30)) ], [20, 18, 15, 10, 8, 5], - 'shortest path'; + 'chained path'; } { @@ -83,9 +83,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } Slic3r::Polyline->new([10,5], [15,5], [20,5]), ]); is_deeply - [ map $_->[X], map @{$_->unpack->polyline}, $collection->shortest_path(Slic3r::Point->new(30,0)) ], + [ map $_->[X], map @{$_->unpack->polyline}, $collection->chained_path(Slic3r::Point->new(30,0)) ], [reverse 4, 10, 15, 10, 15, 20], - 'shortest path'; + 'chained path'; } { From e593a30fc7f31a936803b4057b894f8e15e597cc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 6 Feb 2013 10:40:08 +0100 Subject: [PATCH 066/114] Skip support material for one-layer objects --- lib/Slic3r/Print/Object.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index ceeda2646f..592380e291 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -578,6 +578,7 @@ sub combine_infill { sub generate_support_material { my $self = shift; + return if $self->layer_count < 2; my $threshold_rad = $Slic3r::Config->support_material_threshold ? deg2rad($Slic3r::Config->support_material_threshold + 1) # +1 makes the threshold inclusive From 26a3cd554249354603aff6ee53fb41bb27051d26 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 6 Feb 2013 12:03:53 +0100 Subject: [PATCH 067/114] Refactor chained_path(), add chained_path_items() and chained_path_points() --- lib/Slic3r/Fill.pm | 6 ++---- lib/Slic3r/Geometry.pm | 36 +++++++++++++++++++++++++----------- lib/Slic3r/Layer/Region.pm | 14 ++++++++++---- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index f3f3712706..2c38824999 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -182,10 +182,8 @@ sub make_fill { push @fills, @{$layer->thin_fills}; push @fills_ordering_points, map $_->unpack->points->[0], @{$layer->thin_fills}; - # organize infill paths using a shortest path search - @fills = @{chained_path([ - map [ $fills_ordering_points[$_], $fills[$_] ], 0..$#fills, - ])}; + # organize infill paths using a nearest-neighbor search + @fills = @fills[ chained_path(\@fills_ordering_points) ]; return @fills; } diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 2132a978e3..723345906a 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -7,7 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw( PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel line_point_belongs_to_segment points_coincide distance_between_points - comparable_distance_between_points + comparable_distance_between_points chained_path_items chained_path_points line_length midpoint point_in_polygon point_in_segment segment_in_segment point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_along_segment polygon_segment_having_point polygon_has_subsegment @@ -799,29 +799,43 @@ sub polyline_remove_short_segments { } } -# accepts an arrayref; each item should be an arrayref whose first -# item is the point to be used for the shortest path, and the second -# one is the value to be returned in output (if the second item -# is not provided, the point will be returned) +# accepts an arrayref of points; it returns a list of indices +# according to a nearest-neighbor walk sub chained_path { my ($items, $start_near) = @_; - my %values = map +($_->[0] => $_->[1] || $_->[0]), @$items; - my @points = map $_->[0], @$items; + my @points = @$items; + my %indices = map { $points[$_] => $_ } 0 .. $#points; - my $result = []; + my @result = (); my $last_point; if (!$start_near) { $start_near = shift @points; - push @$result, $values{$start_near} if $start_near; + push @result, $indices{$start_near} if $start_near; } while (@points) { $start_near = nearest_point($start_near, [@points]); @points = grep $_ ne $start_near, @points; - push @$result, $values{$start_near}; + push @result, $indices{$start_near}; } - return $result; + return @result; +} + +# accepts an arrayref; each item should be an arrayref whose first +# item is the point to be used for the shortest path, and the second +# one is the value to be returned in output (if the second item +# is not provided, the point will be returned) +sub chained_path_items { + my ($items, $start_near) = @_; + + my @indices = chained_path([ map $_->[0], @$items ], $start_near); + return [ map $_->[1], @$items[@indices] ]; +} + +sub chained_path_points { + my ($points, $start_near) = @_; + return [ @$points[ chained_path($points, $start_near) ] ]; } sub douglas_peucker { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 6a925ddbb9..daab1a6d15 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -2,7 +2,7 @@ package Slic3r::Layer::Region; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale chained_path); +use Slic3r::Geometry qw(scale chained_path_items); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); use Slic3r::Surface ':types'; @@ -167,7 +167,7 @@ sub make_perimeters { my @perimeters = (); # one item per depth; each item # organize islands using a shortest path search - my @surfaces = @{chained_path([ + my @surfaces = @{chained_path_items([ map [ $_->contour->[0], $_ ], @{$self->slices}, ])}; @@ -319,18 +319,24 @@ sub make_perimeters { } # process one island (original surface) at time + # islands are already sorted with a nearest-neighbor search foreach my $island (@perimeters) { # do holes starting from innermost one my @holes = (); my %is_external = (); + + # each item of @$island contains the expolygons having the same depth; + # for each depth we build an arrayref containing all the holes my @hole_depths = map [ map $_->holes, @$_ ], @$island; - # organize the outermost hole loops using a shortest path search - @{$hole_depths[0]} = @{chained_path([ + # organize the outermost hole loops using a nearest-neighbor search + @{$hole_depths[0]} = @{chained_path_items([ map [ $_->[0], $_ ], @{$hole_depths[0]}, ])}; + # loop while we have spare holes CYCLE: while (map @$_, @hole_depths) { + # remove first depth container if it contains no holes anymore shift @hole_depths while !@{$hole_depths[0]}; # take first available hole From 6c12bd32adf2c57cba9f0c3b2f7b5f24bbc60100 Mon Sep 17 00:00:00 2001 From: Robert Giseburt Date: Wed, 6 Feb 2013 22:32:37 -0600 Subject: [PATCH 068/114] Fix for Replicator 1 not finishing builds. If the progress reaches 100% before last layer, the firmware erroneously stops reading the file. --- lib/Slic3r/GCode.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 3633c3c5bb..4eaf9846fe 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -86,7 +86,7 @@ sub change_layer { my $gcode = ""; if ($Slic3r::Config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) { $gcode .= sprintf "M73 P%s%s\n", - int(100 * ($layer->id / ($self->layer_count - 1))), + int(99 * ($layer->id / ($self->layer_count - 1))), ($Slic3r::Config->gcode_comments ? ' ; update progress' : ''); } return $gcode; From cf8cfc138036020b138735d9877637bab6e931bc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 9 Feb 2013 21:49:00 +0100 Subject: [PATCH 069/114] Bugfix: angle threshold was considered even when enforcing support --- lib/Slic3r/Print/Object.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 592380e291..87b7b47338 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -658,8 +658,8 @@ sub generate_support_material { my $distance = $i <= ($Slic3r::Config->support_material_enforce_layers + $Slic3r::Config->raft_layers) ? 0 : $overhang_width; - @overhangs = map $_->offset_ex(2 * $overhang_width), @{diff_ex( - [ map @$_, map $_->offset_ex(-$overhang_width), @{$layer->slices} ], + @overhangs = map $_->offset_ex(2 * $distance), @{diff_ex( + [ map @$_, map $_->offset_ex(-$distance), @{$layer->slices} ], [ map @$_, @{$lower_layer->slices} ], 1, )}; From cac79c057555d9c3baa497ec319db1869dcf2fd3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 9 Feb 2013 23:36:32 +0100 Subject: [PATCH 070/114] New option to only infill where strictly needed for supporting ceilings --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 7 +++++ lib/Slic3r/GUI/Tab.pm | 3 +- lib/Slic3r/Layer/Region.pm | 9 +++++- lib/Slic3r/Print.pm | 1 + lib/Slic3r/Print/Object.pm | 61 ++++++++++++++++++++++++++++++++++---- slic3r.pl | 2 ++ 7 files changed, 77 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 21498b0f58..76c1845d38 100644 --- a/README.markdown +++ b/README.markdown @@ -197,6 +197,8 @@ The author of the Silk icon set is Mark James. --solid-infill-below-area Force solid infill when a region has a smaller area than this threshold (mm^2, default: 70) + --infill-only-where-needed + Only infill under ceilings (default: no) Support material options: --support-material Generate support material for overhangs diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index f693ba00ce..54a82b3c32 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -380,6 +380,13 @@ our $Options = { min => 0, default => 0, }, + 'infill_only_where_needed' => { + label => 'Only infill where needed', + tooltip => 'This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).', + cli => 'infill-only-where-needed!', + type => 'bool', + default => 0, + }, # flow options 'extrusion_width' => { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index c9b292b725..d970da1bde 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -417,7 +417,8 @@ sub build { }, { title => 'Advanced', - options => [qw(infill_every_layers solid_infill_every_layers fill_angle solid_infill_below_area only_retract_when_crossing_perimeters)], + options => [qw(infill_every_layers infill_only_where_needed solid_infill_every_layers fill_angle + solid_infill_below_area only_retract_when_crossing_perimeters)], }, ]); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index daab1a6d15..aedb45d026 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -2,7 +2,7 @@ package Slic3r::Layer::Region; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale chained_path_items); +use Slic3r::Geometry qw(PI scale chained_path_items); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); use Slic3r::Surface ':types'; @@ -16,6 +16,7 @@ has 'layer' => ( has 'region' => (is => 'ro', required => 1); has 'perimeter_flow' => (is => 'rw'); has 'infill_flow' => (is => 'rw'); +has 'overhang_width' => (is => 'lazy'); # collection of spare segments generated by slicing the original geometry; # these need to be merged in continuos (closed) polylines @@ -64,6 +65,12 @@ sub _update_flows { : $self->region->flows->{infill}); } +sub _build_overhang_width { + my $self = shift; + my $threshold_rad = PI/2 - atan2($self->perimeter_flow->width / $self->height / 2, 1); + return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad))); +} + # build polylines from lines sub make_surfaces { my $self = shift; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 03fc77d883..d22bcb493b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -367,6 +367,7 @@ sub export_gcode { # they will be split in internal and internal-solid surfaces $status_cb->(60, "Generating horizontal shells"); $_->discover_horizontal_shells for @{$self->objects}; + $_->clip_fill_surfaces for @{$self->objects}; # combine fill surfaces to honor the "infill every N layers" option $status_cb->(70, "Combining infill"); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 87b7b47338..6a2ad15373 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -375,6 +375,53 @@ sub detect_surfaces_type { } } +sub clip_fill_surfaces { + my $self = shift; + return unless $Slic3r::Config->infill_only_where_needed; + + # We only want infill under ceilings; this is almost like an + # internal support material. + + my $additional_margin = scale 3; + + my @overhangs = (); + for my $layer_id (reverse 0..$#{$self->layers}) { + my $layer = $self->layers->[$layer_id]; + + # clip this layer's internal surfaces to @overhangs + foreach my $layerm (@{$layer->regions}) { + my @new_internal = map Slic3r::Surface->new( + expolygon => $_, + surface_type => S_TYPE_INTERNAL, + ), + @{intersection_ex( + [ map @$_, @overhangs ], + [ map @{$_->expolygon}, grep $_->surface_type == S_TYPE_INTERNAL, @{$layerm->fill_surfaces} ], + )}; + @{$layerm->fill_surfaces} = ( + @new_internal, + (grep $_->surface_type != S_TYPE_INTERNAL, @{$layerm->fill_surfaces}), + ); + } + + # get this layer's overhangs + if ($layer_id > 0) { + my $lower_layer = $self->layers->[$layer_id-1]; + # loop through layer regions so that we can use each region's + # specific overhang width + foreach my $layerm (@{$layer->regions}) { + my $overhang_width = $layerm->overhang_width; + # we want to support any solid surface, not just tops + # (internal solids might have been generated) + push @overhangs, map $_->offset_ex($additional_margin), @{intersection_ex( + [ map @{$_->expolygon}, grep $_->surface_type != S_TYPE_INTERNAL, @{$layerm->fill_surfaces} ], + [ map @$_, map $_->offset_ex(-$overhang_width), @{$lower_layer->slices} ], + )}; + } + } + } +} + sub discover_horizontal_shells { my $self = shift; @@ -580,13 +627,15 @@ sub generate_support_material { my $self = shift; return if $self->layer_count < 2; - my $threshold_rad = $Slic3r::Config->support_material_threshold - ? deg2rad($Slic3r::Config->support_material_threshold + 1) # +1 makes the threshold inclusive - : PI/2 - atan2($self->layers->[1]->regions->[0]->perimeter_flow->width/$Slic3r::Config->layer_height/2, 1); - Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); - + my $overhang_width; + if ($Slic3r::Config->support_material_threshold) { + my $threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive + Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); + $overhang_width = scale $Slic3r::Config->layer_height * ((cos $threshold_rad) / (sin $threshold_rad)); + } else { + $overhang_width = $self->layers->[1]->regions->[0]->overhang_width; + } my $flow = $self->print->support_material_flow; - my $overhang_width = $threshold_rad == 0 ? undef : scale $Slic3r::Config->layer_height * ((cos $threshold_rad) / (sin $threshold_rad)); my $distance_from_object = 1.5 * $flow->scaled_width; my $pattern_spacing = ($Slic3r::Config->support_material_spacing > $flow->spacing) ? $Slic3r::Config->support_material_spacing diff --git a/slic3r.pl b/slic3r.pl index 04aa1be9e6..741da1de8b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -245,6 +245,8 @@ $j --solid-infill-below-area Force solid infill when a region has a smaller area than this threshold (mm^2, default: $config->{solid_infill_below_area}) + --infill-only-where-needed + Only infill under ceilings (default: no) Support material options: --support-material Generate support material for overhangs From c9b506fa02bf037126e8e47e69b476c554ad2b70 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 10 Feb 2013 12:40:43 +0100 Subject: [PATCH 071/114] New simpler and more robust implementation of infill every 'n' layers --- lib/Slic3r/Print/Object.pm | 122 +++++++++++++------------------------ 1 file changed, 44 insertions(+), 78 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 6a2ad15373..800290a6d5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -530,93 +530,59 @@ sub combine_infill { my $self = shift; return unless $Slic3r::Config->infill_every_layers > 1 && $Slic3r::Config->fill_density > 0; + my $every = $Slic3r::Config->infill_every_layers; my $area_threshold = $Slic3r::flow->scaled_spacing ** 2; + my $layer_count = $self->layer_count; for my $region_id (0 .. ($self->print->regions_count-1)) { - # start from top, skip lowest layer - for (my $i = $self->layer_count - 1; $i > 0; $i--) { - my $layerm = $self->layers->[$i]->regions->[$region_id]; + # skip bottom layer + for (my $layer_id = $every; $layer_id <= $layer_count-1; $layer_id += $every) { + # get the layers whose infill we want to combine (bottom-up) + my @layerms = map $self->layers->[$_]->regions->[$region_id], + ($layer_id - ($every-1)) .. $layer_id; - # skip layer if no internal fill surfaces - next if !grep $_->surface_type == S_TYPE_INTERNAL, @{$layerm->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::Config->infill_every_layers - 1; $d >= 1; $d--) { - next if ($i - $d) <= 0; # do not combine infill for bottom layer - my $lower_layerm = $self->layers->[$i - 1]->regions->[$region_id]; + # process internal and internal-solid infill separately + for my $type (S_TYPE_INTERNAL, S_TYPE_INTERNALSOLID) { + # we need to perform a multi-layer intersection, so let's split it in pairs - # select surfaces of the lower layer having the depth we're looking for - my @lower_surfaces = grep $_->depth_layers == $d && $_->surface_type == S_TYPE_INTERNAL, - @{$lower_layerm->fill_surfaces}; - next if !@lower_surfaces; + # initialize the intersection with the candidates of the lowest layer + my $intersection = [ map $_->expolygon, grep $_->surface_type == $type, @{$layerms[0]->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 == S_TYPE_INTERNAL, @{$layerm->fill_surfaces} ], - undef, 1, - ); - - # purge intersections, skip tiny regions - @$intersection = grep $_->area > $area_threshold, @$intersection; - 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 != S_TYPE_INTERNAL, @{$layerm->fill_surfaces}; - push @new_surfaces, map Slic3r::Surface->new - (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $d + 1), @$intersection; - - foreach my $depth (reverse $d..$Slic3r::Config->infill_every_layers) { - push @new_surfaces, map Slic3r::Surface->new - (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $depth), - - # difference between our internal layers with depth == $depth - # and the intersection found - @{diff_ex( - [ - map $_->p, grep $_->surface_type == S_TYPE_INTERNAL && $_->depth_layers == $depth, - @{$layerm->fill_surfaces}, - ], - [ map @$_, @$intersection ], - 1, - )}; - } - @{$layerm->fill_surfaces} = @new_surfaces; + # 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}, grep $_->surface_type == $type, @{$layerm->fill_surfaces} ], + ); } - # now we remove the intersections from lower layer - { - my @new_surfaces = (); - push @new_surfaces, grep $_->surface_type != S_TYPE_INTERNAL, @{$lower_layerm->fill_surfaces}; - - # offset for the two different flow spacings - $intersection = [ map $_->offset_ex( - $lower_layerm->infill_flow->scaled_spacing / 2 - + $layerm->infill_flow->scaled_spacing / 2 - ), @$intersection]; - - foreach my $depth (1..$Slic3r::Config->infill_every_layers) { - push @new_surfaces, map Slic3r::Surface->new - (expolygon => $_, surface_type => S_TYPE_INTERNAL, depth_layers => $depth), - - # difference between internal layers with depth == $depth - # and the intersection found - @{diff_ex( - [ - map $_->p, grep $_->surface_type == S_TYPE_INTERNAL && $_->depth_layers == $depth, - @{$lower_layerm->fill_surfaces}, - ], - [ map @$_, @$intersection ], - 1, - )}; + @$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_id-($every-1), $layer_id; + + # $intersection now contains the regions that can be combined across the full amount of layers + # so let's remove those areas from all layers + foreach my $layerm (@layerms) { + my @this_type = grep $_->surface_type == $type, @{$layerm->fill_surfaces}; + my @other_types = grep $_->surface_type != $type, @{$layerm->fill_surfaces}; + + @this_type = map Slic3r::Surface->new(expolygon => $_, surface_type => $type), + @{diff_ex( + [ map @{$_->expolygon}, @this_type ], + [ map @$_, @$intersection ], + )}; + + # apply surfaces back with adjusted depth to the uppermost layer + if ($layerm->id == $layer_id) { + push @this_type, + map Slic3r::Surface->new(expolygon => $_, surface_type => $type, depth_layers => $every), + @$intersection; } - @{$lower_layerm->fill_surfaces} = @new_surfaces; + + @{$layerm->fill_surfaces} = (@this_type, @other_types); } } } From f46226b32fa860fced3952640abf157cd6642d20 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Sat, 16 Feb 2013 06:12:18 -0800 Subject: [PATCH 072/114] regression test for combined infill #834 Guards against issue of slightly off-vertical walls triggering extra infill on layers that should be empty when combining infill. --- t/combineinfill.t | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 t/combineinfill.t diff --git a/t/combineinfill.t b/t/combineinfill.t new file mode 100644 index 0000000000..8c1ba9e778 --- /dev/null +++ b/t/combineinfill.t @@ -0,0 +1,71 @@ +use Test::More tests => 3; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use List::Util qw(first); +use Slic3r; +use Slic3r::Test; + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('solid_layers', 0); + $config->set('bottom_solid_layers', 0); + $config->set('top_solid_layers', 0); + $config->set('infill_every_layers', 6); + $config->set('layer_height', 0.06); + + my $test = sub { + my ($shift) = @_; + + my $self = Slic3r::Test::init_print('20mm_cube', config => $config); + + $shift /= &Slic3r::SCALING_FACTOR; + + # Put a slope on the box's sides by shifting x and y coords by $tilt * (z / boxheight). + # The test here is to put such a slight slope on the walls that it should + # not trigger any extra fill on fill layers that should be empty when + # combine infill is enabled. + $_->[0] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)), for @{$self->objects->[0]->meshes->[0]->vertices}; + $_->[1] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)), for @{$self->objects->[0]->meshes->[0]->vertices}; + + # copy of Print::export_gcode() up to the point + # after fill surfaces are combined + $self->init_extruders; + $_->slice(keep_meshes => 1) for @{$self->objects}; + $_->make_perimeters for @{$self->objects}; + foreach my $layer (map @{$_->layers}, @{$self->objects}) { + $_->simplify(&Slic3r::SCALED_RESOLUTION) + for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); + } + $_->detect_surfaces_type for @{$self->objects}; + $_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; + $_->process_bridges for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; + $_->discover_horizontal_shells for @{$self->objects}; + $_->combine_infill for @{$self->objects}; + + # Only layers with id % 6 == 0 should have fill. + my $spurious_infill = 0; + foreach my $layer (map @{$_->layers}, @{$self->objects}) { + ++$spurious_infill if ($layer->id % 6 && grep @{$_->fill_surfaces} > 0, @{$layer->regions}); + } + + $spurious_infill -= scalar(@{$self->objects->[0]->layers} - 1) % 6; + + fail "spurious fill surfaces found on layers that should have none (walls " . sprintf("%.4f", Slic3r::Geometry::rad2deg(atan2($shift, 20/&Slic3r::SCALING_FACTOR))) . " degrees off vertical)" + unless $spurious_infill == 0; + 1; + }; + + # Test with mm skew offsets for the top of the 20mm-high box + for my $shift (0, 0.0001, 1) { + ok $test->($shift), "no spurious fill surfaces with box walls " . sprintf("%.4f",Slic3r::Geometry::rad2deg(atan2($shift, 20))) . " degrees off of vertical"; + } +} + +__END__ From 9a86edc70cae5635829578c81434e86560582c9f Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Sat, 16 Feb 2013 07:53:47 -0800 Subject: [PATCH 073/114] account for extrusion width when clearing layers for combined infill Otherwise you get extraneous fill up against sloped walls in regions of layers that should not have fill, like in #834. --- lib/Slic3r/Print/Object.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 800290a6d5..e312783402 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -565,6 +565,12 @@ sub combine_infill { # $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]->infill_flow->scaled_width / 2 + + $layerms[-1]->perimeter_flow->scaled_width / 2 + ), @$intersection; + foreach my $layerm (@layerms) { my @this_type = grep $_->surface_type == $type, @{$layerm->fill_surfaces}; my @other_types = grep $_->surface_type != $type, @{$layerm->fill_surfaces}; @@ -572,7 +578,7 @@ sub combine_infill { @this_type = map Slic3r::Surface->new(expolygon => $_, surface_type => $type), @{diff_ex( [ map @{$_->expolygon}, @this_type ], - [ map @$_, @$intersection ], + [ @intersection_with_clearance ], )}; # apply surfaces back with adjusted depth to the uppermost layer From c2f04f2861e44a2527dd853c3bcbb34844bc11d4 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Mon, 18 Feb 2013 07:26:19 -0800 Subject: [PATCH 074/114] avoid overlap of single layer and multilayer fill A single-layer rectilinear or honeycomb fill under a low slope wall/ceiling or extra perimeter will stick out from under the overhang, because those fills get extended beyond their fill_surface boundaries to purposely overlap perimeters. This causes interference with multil-layer fills. This commit clips the single layer fill boundaries a bit more where they interface with multi-layer fill boundaries, to avoid overlap. --- lib/Slic3r/Print/Object.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e312783402..42e27a05a5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -569,6 +569,11 @@ sub combine_infill { my @intersection_with_clearance = map $_->offset( $layerms[-1]->infill_flow->scaled_width / 2 + $layerms[-1]->perimeter_flow->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 || $Slic3r::Config->fill_pattern =~ /(rectilinear|honeycomb)/) + ? $layerms[-1]->infill_flow->scaled_width * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING + : 0) ), @$intersection; foreach my $layerm (@layerms) { From 4d6f8a4fd4094e49ed6b1eb339924f5b67d60bf4 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Mon, 18 Feb 2013 12:38:24 -0800 Subject: [PATCH 075/114] added file combineinfill.t --- MANIFEST | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST b/MANIFEST index e48376de87..08d04a8eb8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -56,6 +56,7 @@ t/arcs.t t/clean_polylines.t t/clipper.t t/collinear.t +t/combineinfill.t t/custom_gcode.t t/dynamic.t t/fill.t From 6dc62d3cf21ed41c6b4cfd56314d2c488a0bdb80 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 18 Feb 2013 11:37:34 +0100 Subject: [PATCH 076/114] Disable extra perimeters if fill density is zero as they wouldn't be supported by anything and would just float inside the object. infill_only_where_needed should be the method of choice for hollow objects --- lib/Slic3r/Print/Object.pm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 42e27a05a5..c4a02b1962 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -198,21 +198,24 @@ sub make_perimeters { # compare each layer to the one below, and mark those slices needing # one additional inner perimeter, like the top of domed objects- - # this algorithm makes sure that almost one perimeter is overlapping - if ($Slic3r::Config->extra_perimeters && $Slic3r::Config->perimeters > 0) { + # this algorithm makes sure that at least one perimeter is overlapping + # but we don't generate any extra perimeter if fill density is zero, as they would be floating + # inside the object - infill_only_where_needed should be the method of choice for printing + # hollow objects + if ($Slic3r::Config->extra_perimeters && $Slic3r::Config->perimeters > 0 && $Slic3r::Config->fill_density > 0) { for my $region_id (0 .. ($self->print->regions_count-1)) { for my $layer_id (0 .. $self->layer_count-2) { my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id]; my $perimeter_flow = $layerm->perimeter_flow; - my $overlap = $perimeter_flow->spacing; # one perimeter + my $overlap = $perimeter_flow->scaled_spacing; # one perimeter # compute polygons representing the thickness of the first external perimeter of # the upper layer slices my $upper = diff_ex( [ map @$_, map $_->expolygon->offset_ex(+ 0.5 * $perimeter_flow->scaled_spacing), @{$upper_layerm->slices} ], - [ map @$_, map $_->expolygon->offset_ex(- scale($overlap) + (0.5 * $perimeter_flow->scaled_spacing)), @{$upper_layerm->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- $overlap + (0.5 * $perimeter_flow->scaled_spacing)), @{$upper_layerm->slices} ], ); next if !@$upper; From b9c84490b80e9bb88e7566f54dfa5c48a41930f4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 18 Feb 2013 11:52:47 +0100 Subject: [PATCH 077/114] Get rid of most calls to $Slic3r::flow --- lib/Slic3r/Layer/Region.pm | 10 ++++++++-- lib/Slic3r/Polygon.pm | 4 ++-- lib/Slic3r/Print/Object.pm | 7 +++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index aedb45d026..027a9bd587 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -16,6 +16,7 @@ has 'layer' => ( has 'region' => (is => 'ro', required => 1); has 'perimeter_flow' => (is => 'rw'); has 'infill_flow' => (is => 'rw'); +has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); # collection of spare segments generated by slicing the original geometry; @@ -71,6 +72,11 @@ sub _build_overhang_width { return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad))); } +sub _build_infill_area_threshold { + my $self = shift; + return $self->infill_flow->scaled_spacing ** 2; +} + # build polylines from lines sub make_surfaces { my $self = shift; @@ -194,7 +200,7 @@ sub make_perimeters { # this compensation only works for circular holes, while it would # overcompensate for hexagons and other shapes having straight edges. # so we require a minimum number of vertices. - next unless $circumference / @$hole >= 3 * $Slic3r::flow->scaled_width; + next unless $circumference / @$hole >= 3 * $self->perimeter_flow->scaled_width; # revert the compensation done in make_surfaces() and get the actual radius # of the hole @@ -411,7 +417,7 @@ sub _add_perimeter { my $self = shift; my ($polygon, $role) = @_; - return unless $polygon->is_printable($self->perimeter_flow->width); + return unless $polygon->is_printable($self->perimeter_flow); push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack( polygon => $polygon, role => ($role // EXTR_ROLE_PERIMETER), diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 4cc51d6105..da23e823af 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -118,7 +118,7 @@ sub subdivide { # returns false if the polyline is too tight to be printed sub is_printable { my $self = shift; - my ($flow_width) = @_; + my ($flow) = @_; # try to get an inwards offset # for a distance equal to half of the extrusion width; @@ -129,7 +129,7 @@ sub is_printable { # detect them and we would be discarding them. my $p = $self->clone; $p->make_counter_clockwise; - return $p->offset(Slic3r::Geometry::scale($flow_width || $Slic3r::flow->width) / 2) ? 1 : 0; + return $p->offset($flow->scaled_width / 2) ? 1 : 0; } sub is_valid { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index c4a02b1962..a9fc25c345 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -306,7 +306,7 @@ sub detect_surfaces_type { [ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$clip_surfaces ], 1, ); - return grep $_->contour->is_printable($layerm->flow->width), + return grep $_->contour->is_printable($layerm->flow), map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @$expolygons; }; @@ -430,8 +430,6 @@ sub discover_horizontal_shells { Slic3r::debugf "==> DISCOVERING HORIZONTAL SHELLS\n"; - my $area_threshold = $Slic3r::flow->scaled_spacing ** 2; - for my $region_id (0 .. ($self->print->regions_count-1)) { for (my $i = 0; $i < $self->layer_count; $i++) { my $layerm = $self->layers->[$i]->regions->[$region_id]; @@ -514,6 +512,7 @@ sub discover_horizontal_shells { } } + my $area_threshold = $layerm->infill_area_threshold; @{$layerm->fill_surfaces} = grep $_->expolygon->area > $area_threshold, @{$layerm->fill_surfaces}; } @@ -534,7 +533,6 @@ sub combine_infill { return unless $Slic3r::Config->infill_every_layers > 1 && $Slic3r::Config->fill_density > 0; my $every = $Slic3r::Config->infill_every_layers; - my $area_threshold = $Slic3r::flow->scaled_spacing ** 2; my $layer_count = $self->layer_count; for my $region_id (0 .. ($self->print->regions_count-1)) { @@ -559,6 +557,7 @@ sub combine_infill { ); } + 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", From 91bcfc8a74425b1bfe3577460e5c3f48bcf17b9e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 22 Feb 2013 16:08:11 +0100 Subject: [PATCH 078/114] Remove $Slic3r::flow and $Slic3r::first_layer_flow --- lib/Slic3r.pm | 5 ----- lib/Slic3r/Layer.pm | 7 +++---- lib/Slic3r/Layer/Region.pm | 12 +++++------- lib/Slic3r/Print.pm | 36 +++++++++++++++--------------------- lib/Slic3r/Print/Object.pm | 2 +- 5 files changed, 24 insertions(+), 38 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index f4e26b98a0..75ea26ed38 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -69,12 +69,7 @@ use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; use constant PERIMETER_INFILL_OVERLAP_OVER_SPACING => 0.45; -# The following variables hold the objects used throughout the slicing -# process. They should belong to the Print object, but we are keeping -# them here because it makes accessing them slightly faster. our $Config; -our $flow; -our $first_layer_flow; sub parallelize { my %params = @_; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index cedd234c2c..9138aaf85a 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -13,7 +13,6 @@ has 'slicing_errors' => (is => 'rw'); has 'slice_z' => (is => 'lazy'); has 'print_z' => (is => 'lazy'); has 'height' => (is => 'lazy'); -has 'flow' => (is => 'lazy'); # collection of expolygons generated by slicing the original geometry; # also known as 'islands' (all regions are merged here) @@ -57,8 +56,6 @@ sub _build_height { return $self->id == 0 ? $Slic3r::Config->get_value('first_layer_height') : $Slic3r::Config->layer_height; } -sub _build_flow { $Slic3r::flow } - # layer height of contact paths in unscaled coordinates sub support_material_contact_height { my $self = shift; @@ -69,7 +66,8 @@ sub support_material_contact_height { # - we should sum our height with the actual upper layers height (which might be different) # - we should use the actual flow of the upper layer bridges, not the default one # ...but we're close enough for now - return 2*$self->height - $self->flow->nozzle_diameter; + my $upper_layer = $self->object->layers->[ $self->id + 1 ] // $self; + return 2*$self->height - $upper_layer->infill_flow->bridge_width; } # Z used for printing support material contact in scaled coordinates @@ -115,6 +113,7 @@ sub make_perimeters { sub support_islands_enclose_line { my $self = shift; my ($line) = @_; + return 0 if !$self->support_islands; # why can we arrive here if there are no support islands? return (first { $_->encloses_line($line) } @{$self->support_islands}) ? 1 : 0; } diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 027a9bd587..68a227846b 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -57,13 +57,11 @@ sub _update_flows { my $self = shift; return if !$self->region; - $self->perimeter_flow($self->id == 0 - ? $self->region->first_layer_flows->{perimeter} - : $self->region->flows->{perimeter}); + $self->perimeter_flow + ($self->region->first_layer_flows->{perimeter} || $self->region->flows->{perimeter}); - $self->infill_flow($self->id == 0 - ? $self->region->first_layer_flows->{infill} - : $self->region->flows->{infill}); + $self->infill_flow + ($self->region->first_layer_flows->{infill} || $self->region->flows->{infill}); } sub _build_overhang_width { @@ -471,7 +469,7 @@ sub process_bridges { # offset the contour and intersect it with the internal surfaces to discover # which of them has contact with our bridge my @supporting_surfaces = (); - my ($contour_offset) = $expolygon->contour->offset(scale $self->flow->spacing * sqrt(2)); + my ($contour_offset) = $expolygon->contour->offset(scale $self->infill_flow->spacing * sqrt(2)); foreach my $internal_surface (@internal_surfaces) { my $intersection = intersection_ex([$contour_offset], [$internal_surface->p]); if (@$intersection) { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d22bcb493b..4263c68b08 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -195,15 +195,6 @@ sub init_extruders { ); } - # calculate default flows - $Slic3r::flow = $self->extruders->[0]->make_flow( - width => $self->config->extrusion_width, - ); - $Slic3r::first_layer_flow = $self->extruders->[0]->make_flow( - layer_height => $self->config->get_value('first_layer_height'), - width => $self->config->first_layer_extrusion_width, - ); - # calculate regions' flows for my $region_id (0 .. $#{$self->regions}) { my $region = $self->regions->[$region_id]; @@ -219,7 +210,7 @@ sub init_extruders { $region->first_layer_flows->{$_} = $region->extruders->{$_}->make_flow( layer_height => $self->config->get_value('first_layer_height'), width => $self->config->first_layer_extrusion_width, - ); + ) if $self->config->first_layer_extrusion_width; } } @@ -234,9 +225,6 @@ sub init_extruders { width => $self->config->first_layer_extrusion_width, )); } - - Slic3r::debugf "Default flow width = %s (spacing = %s)\n", - $Slic3r::flow->width, $Slic3r::flow->spacing; } sub object_copies { @@ -573,7 +561,10 @@ sub make_skirt { my $convex_hull = convex_hull(\@points); my @extruded_length = (); # for each extruder - my $spacing = $Slic3r::first_layer_flow->spacing; + + # TODO: use each extruder's own flow + my $spacing = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow->spacing; + my $first_layer_height = $Slic3r::Config->get_value('first_layer_height'); my @extruders_e_per_mm = (); my $extruder_idx = 0; @@ -612,7 +603,9 @@ sub make_brim { my $self = shift; return unless $Slic3r::Config->brim_width > 0; - my $grow_distance = $Slic3r::first_layer_flow->scaled_width / 2; + my $flow = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow; + + my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons foreach my $obj_idx (0 .. $#{$self->objects}) { my $layer0 = $self->objects->[$obj_idx]->layers->[0]; @@ -627,18 +620,19 @@ sub make_brim { } # if brim touches skirt, make it around skirt too - if ($Slic3r::Config->skirt_distance + (($Slic3r::Config->skirts - 1) * $Slic3r::first_layer_flow->spacing) <= $Slic3r::Config->brim_width) { + # TODO: calculate actual skirt width (using each extruder's flow in multi-extruder setups) + if ($Slic3r::Config->skirt_distance + (($Slic3r::Config->skirts - 1) * $flow->spacing) <= $Slic3r::Config->brim_width) { push @islands, map $_->unpack->split_at_first_point->polyline->grow($grow_distance), @{$self->skirt}; } - my $num_loops = sprintf "%.0f", $Slic3r::Config->brim_width / $Slic3r::first_layer_flow->width; + my $num_loops = sprintf "%.0f", $Slic3r::Config->brim_width / $flow->width; for my $i (reverse 1 .. $num_loops) { # JT_SQUARE ensures no vertex is outside the given offset distance push @{$self->brim}, Slic3r::ExtrusionLoop->pack( polygon => Slic3r::Polygon->new($_), role => EXTR_ROLE_SKIRT, - flow_spacing => $Slic3r::first_layer_flow->spacing, - ) for Slic3r::Geometry::Clipper::offset(\@islands, $i * $Slic3r::first_layer_flow->scaled_spacing, undef, JT_SQUARE); + flow_spacing => $flow->spacing, + ) for Slic3r::Geometry::Clipper::offset(\@islands, $i * $flow->scaled_spacing, undef, JT_SQUARE); # TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions } } @@ -674,8 +668,8 @@ sub write_gcode { printf $fh "; infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{infill}->width; printf $fh "; support material extrusion width = %.2fmm\n", $self->support_material_flow->width if $self->support_material_flow; - printf $fh "; first layer extrusion width = %.2fmm\n", $Slic3r::first_layer_flow->width - if $Slic3r::first_layer_flow; + printf $fh "; first layer extrusion width = %.2fmm\n", $self->regions->[0]->first_layer_flows->{perimeter}->width + if $self->regions->[0]->first_layer_flows->{perimeter}; print $fh "\n"; # set up our extruder object diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a9fc25c345..567e37e554 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -306,7 +306,7 @@ sub detect_surfaces_type { [ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$clip_surfaces ], 1, ); - return grep $_->contour->is_printable($layerm->flow), + return grep $_->contour->is_printable($layerm->infill_flow), map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @$expolygons; }; From 5eade0f9e131d5f904ee9c0d63ce2e9e723664e3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 22 Feb 2013 16:24:24 +0100 Subject: [PATCH 079/114] Don't validate the number of layers for infill combination, just limit it automatically. #993 --- lib/Slic3r/Config.pm | 3 --- lib/Slic3r/Print/Object.pm | 10 ++++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 54a82b3c32..d98c301516 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -1213,9 +1213,6 @@ sub validate { # --infill-every-layers die "Invalid value for --infill-every-layers\n" if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1; - # TODO: this check should be limited to the extruder used for infill - die "Maximum infill thickness can't exceed nozzle diameter\n" - if grep $self->infill_every_layers * $self->layer_height > $_, @{$self->nozzle_diameter}; # --scale die "Invalid value for --scale\n" diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 567e37e554..5dae0482e4 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -1,6 +1,7 @@ package Slic3r::Print::Object; use Moo; +use List::Util qw(min); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); @@ -532,10 +533,15 @@ sub combine_infill { my $self = shift; return unless $Slic3r::Config->infill_every_layers > 1 && $Slic3r::Config->fill_density > 0; - my $every = $Slic3r::Config->infill_every_layers; - my $layer_count = $self->layer_count; for my $region_id (0 .. ($self->print->regions_count-1)) { + # limit the number of combined layers to the maximum height allowed by this regions' nozzle + my $every = min( + $Slic3r::Config->infill_every_layers, + int($self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter/$Slic3r::Config->layer_height), + ); + Slic3r::debugf "Infilling every %d layers\n", $every; + # skip bottom layer for (my $layer_id = $every; $layer_id <= $layer_count-1; $layer_id += $every) { # get the layers whose infill we want to combine (bottom-up) From bec2371604824df0680f962cea9e6051ee522e7b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 22 Feb 2013 16:56:43 +0100 Subject: [PATCH 080/114] Fix regression introduced in 91bcfc8a74425b1bfe3577460e5c3f48bcf17b9e --- lib/Slic3r/Layer/Region.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 68a227846b..5f44172319 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -57,11 +57,15 @@ sub _update_flows { my $self = shift; return if !$self->region; - $self->perimeter_flow - ($self->region->first_layer_flows->{perimeter} || $self->region->flows->{perimeter}); - - $self->infill_flow - ($self->region->first_layer_flows->{infill} || $self->region->flows->{infill}); + if ($self->id == 0) { + $self->perimeter_flow + ($self->region->first_layer_flows->{perimeter} || $self->region->flows->{perimeter}); + $self->infill_flow + ($self->region->first_layer_flows->{infill} || $self->region->flows->{infill}); + } else { + $self->perimeter_flow($self->region->flows->{perimeter}); + $self->infill_flow($self->region->flows->{infill}); + } } sub _build_overhang_width { From 28638019ae8c3735bb1a49c61736a08a5b662c40 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 22 Feb 2013 18:23:23 +0100 Subject: [PATCH 081/114] Bugfix: perimeter/infill overlap was calculated regardless of the infill extrusion width. #994 --- lib/Slic3r/Layer/Region.pm | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 5f44172319..718ac25c34 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -165,6 +165,8 @@ sub _merge_loops { sub make_perimeters { my $self = shift; + my $perimeter_spacing = $self->perimeter_flow->scaled_spacing; + my $infill_spacing = $self->infill_flow->scaled_spacing; my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2; # this array will hold one arrayref per original surface (island); @@ -217,7 +219,6 @@ sub make_perimeters { } } - my $distance = $self->perimeter_flow->scaled_spacing; my @gaps = (); # generate perimeters inwards (loop 0 is the external one) @@ -232,8 +233,8 @@ sub make_perimeters { foreach my $expolygon (@last_offsets) { my @offsets = @{union_ex([ Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$distance)], - +0.5*$distance, + [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$perimeter_spacing)], + +0.5*$perimeter_spacing, ), ])}; push @new_offsets, @offsets; @@ -241,10 +242,10 @@ sub make_perimeters { # where the above check collapses the expolygon, then there's no room for an inner loop # and we can extract the gap for later processing my $diff = diff_ex( - [ map @$_, $expolygon->offset_ex(-0.5*$distance) ], + [ map @$_, $expolygon->offset_ex(-0.5*$perimeter_spacing) ], # +2 on the offset here makes sure that Clipper float truncation # won't shrink the clip polygon to be smaller than intended. - [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$distance + 2) ], + [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$perimeter_spacing + 2) ], ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } @@ -258,8 +259,8 @@ sub make_perimeters { { my @fill_boundaries = @{union_ex([ Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -1.5*$distance)], - +0.5*$distance, + [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))], + +0.5*$infill_spacing, ), ])}; $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; From 72df7961855e8f581947941c3c4d99a4507dedf5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 22 Feb 2013 18:40:00 +0100 Subject: [PATCH 082/114] Bugfix: failure in launching on Windows with non-ASCII characters in username. #1011 --- lib/Slic3r.pm | 7 ++++++- lib/Slic3r/GUI.pm | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 75ea26ed38..f9fb3319d3 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -88,9 +88,14 @@ sub parallelize { } } +sub encode_path { + my ($filename) = @_; + return encode('locale_fs', $filename); +} + sub open { my ($fh, $mode, $filename) = @_; - return CORE::open $$fh, $mode, encode('locale_fs', $filename); + return CORE::open $$fh, $mode, encode_path($filename); } 1; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index fb53b5cb54..a4e9f6d909 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -55,14 +55,15 @@ sub OnInit { # locate or create data directory $datadir ||= Wx::StandardPaths::Get->GetUserDataDir; Slic3r::debugf "Data directory: %s\n", $datadir; - my $run_wizard = (-d $datadir) ? 0 : 1; - for ($datadir, "$datadir/print", "$datadir/filament", "$datadir/printer") { + my $encoded_datadir = Slic3r::encode_path($datadir); + my $run_wizard = (-d $encoded_datadir) ? 0 : 1; + for ($encoded_datadir, "$encoded_datadir/print", "$encoded_datadir/filament", "$encoded_datadir/printer") { mkdir or $self->fatal_error("Slic3r was unable to create its data directory at $_ (errno: $!).") unless -d $_; } # load settings - if (-f "$datadir/slic3r.ini") { + if (-f "$encoded_datadir/slic3r.ini") { my $ini = eval { Slic3r::Config->read_ini("$datadir/slic3r.ini") }; $Settings = $ini if $ini; } From 3eedd4bbed12648562c8b87ca9f8d7caefa62fde Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 16:08:29 +0100 Subject: [PATCH 083/114] Fixed regression in support material after recent flow refactoring. #1012 --- lib/Slic3r/Layer.pm | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 9138aaf85a..aa21c1977b 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -62,12 +62,9 @@ sub support_material_contact_height { return $self->height if $self->id == 0; - # this is not very correct because: - # - we should sum our height with the actual upper layers height (which might be different) - # - we should use the actual flow of the upper layer bridges, not the default one - # ...but we're close enough for now + # TODO: check what upper region applies instead of considering the first one my $upper_layer = $self->object->layers->[ $self->id + 1 ] // $self; - return 2*$self->height - $upper_layer->infill_flow->bridge_width; + return 2*$self->height - $upper_layer->regions->[0]->infill_flow->bridge_width; } # Z used for printing support material contact in scaled coordinates From 504962712b25379cf06a1623f055ed543ef2b621 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 17:40:38 +0100 Subject: [PATCH 084/114] Bugfix: when using low layer heights and support material, the contact regions were generated with a negative height. #1013 --- lib/Slic3r/Layer.pm | 11 ++++++++++- lib/Slic3r/Test.pm | 36 ++++++++++++++++++++---------------- t/layers.t | 13 ++++++++++++- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index aa21c1977b..ec350e1d13 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -64,7 +64,16 @@ sub support_material_contact_height { # TODO: check what upper region applies instead of considering the first one my $upper_layer = $self->object->layers->[ $self->id + 1 ] // $self; - return 2*$self->height - $upper_layer->regions->[0]->infill_flow->bridge_width; + my $h = ($self->height + $upper_layer->height) - $upper_layer->regions->[0]->infill_flow->bridge_width; + + # If layer height is less than half the bridge width then we'll get a negative height for contact area. + # The optimal solution would be to skip some layers during support material generation, but for now + # we'll apply a (dirty) workaround that should still work. + if ($h <= 0) { + $h = $self->height; + } + + return $h; } # Z used for printing support material contact in scaled coordinates diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 1e4531cd43..672c8af4bd 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -15,30 +15,34 @@ my %cuboids = ( '2x20x10' => [2, 20,10], ); -sub init_print { - my ($model_name, %params) = @_; +sub model { + my ($model_name) = @_; + + my ($vertices, $facets); + if ($cuboids{$model_name}) { + my ($x, $y, $z) = @{ $cuboids{$model_name} }; + $vertices = [ + [$x,$y,0], [$x,0,0], [0,0,0], [0,$y,0], [$x,$y,$z], [0,$y,$z], [0,0,$z], [$x,0,$z], + ]; + $facets = [ + [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], + ], + } my $model = Slic3r::Model->new; - { - my ($vertices, $facets); - if ($cuboids{$model_name}) { - my ($x, $y, $z) = @{ $cuboids{$model_name} }; - $vertices = [ - [$x,$y,0], [$x,0,0], [0,0,0], [0,$y,0], [$x,$y,$z], [0,$y,$z], [0,0,$z], [$x,0,$z], - ]; - $facets = [ - [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], - ], - } - $model->add_object(vertices => $vertices)->add_volume(facets => $facets); - } + $model->add_object(vertices => $vertices)->add_volume(facets => $facets); + return $model; +} + +sub init_print { + my ($model_name, %params) = @_; my $config = Slic3r::Config->new_from_defaults; $config->apply($params{config}) if $params{config}; $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE}; my $print = Slic3r::Print->new(config => $config); - $print->add_model($model); + $print->add_model(model($model_name)); $print->validate; return $print; diff --git a/t/layers.t b/t/layers.t index 3d381788a9..06688b3369 100644 --- a/t/layers.t +++ b/t/layers.t @@ -1,4 +1,4 @@ -use Test::More tests => 4; +use Test::More tests => 5; use strict; use warnings; @@ -55,4 +55,15 @@ ok $test->(), "positive Z offset"; $config->set('z_offset', -0.8); ok $test->(), "negative Z offset"; +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('nozzle_diameter', [0.35]); + $config->set('layer_height', 0.1333); + + my $print = Slic3r::Test::init_print('2x20x10', config => $config); + $print->init_extruders; + $_->region(0) for @{$print->objects->[0]->layers}; # init layer regions + ok $print->objects->[0]->layers->[1]->support_material_contact_height > 0, 'support_material_contact_height is positive'; +} + __END__ From 063443736427b6040dca146212fe81f55eb500f8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 18:21:29 +0100 Subject: [PATCH 085/114] Keep the full geometries in $layer->slices, including thin walls --- lib/Slic3r/Layer/Region.pm | 45 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 718ac25c34..c9792c60b3 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -87,28 +87,18 @@ sub make_surfaces { return if !@$loops; $self->slices([ _merge_loops($loops) ]); - # the contours must be offsetted by half extrusion width inwards + # detect thin walls by offsetting slices by half extrusion inwards { - my $distance = $self->perimeter_flow->scaled_width / 2; - my @surfaces = @{$self->slices}; - @{$self->slices} = (); - foreach my $surface (@surfaces) { - push @{$self->slices}, map Slic3r::Surface->new - (expolygon => $_, surface_type => S_TYPE_INTERNAL), - @{union_ex([ - Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset($surface->expolygon, -2*$distance)], - +$distance, - ), - ])}; - } - - # now detect thin walls by re-outgrowing offsetted surfaces and subtracting - # them from the original slices - my $outgrown = [ Slic3r::Geometry::Clipper::offset([ map $_->p, @{$self->slices} ], $distance) ]; + my $width = $self->perimeter_flow->scaled_width; + my $outgrown = union_ex([ + Slic3r::Geometry::Clipper::offset( + [Slic3r::Geometry::Clipper::offset([ map @$_, map $_->expolygon, @{$self->slices} ], -$width)], + +$width, + ), + ]); my $diff = diff_ex( - [ map $_->p, @surfaces ], - $outgrown, + [ map $_->p, @{$self->slices} ], + [ map @$_, @$outgrown ], 1, ); @@ -223,18 +213,21 @@ sub make_perimeters { # generate perimeters inwards (loop 0 is the external one) my $loop_number = $Slic3r::Config->perimeters + ($surface->additional_inner_perimeters || 0); - push @perimeters, [[@last_offsets]] if $loop_number > 0; + push @perimeters, [] if $loop_number > 0; # do one more loop (<= instead of <) so that we can detect gaps even after the desired # number of perimeters has been generated - for (my $loop = 1; $loop <= $loop_number; $loop++) { + for (my $loop = 0; $loop <= $loop_number; $loop++) { + my $spacing = $perimeter_spacing; + $spacing /= 2 if $loop == 0; + # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); foreach my $expolygon (@last_offsets) { my @offsets = @{union_ex([ Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$perimeter_spacing)], - +0.5*$perimeter_spacing, + [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$spacing)], + +0.5*$spacing, ), ])}; push @new_offsets, @offsets; @@ -242,10 +235,10 @@ sub make_perimeters { # where the above check collapses the expolygon, then there's no room for an inner loop # and we can extract the gap for later processing my $diff = diff_ex( - [ map @$_, $expolygon->offset_ex(-0.5*$perimeter_spacing) ], + [ map @$_, $expolygon->offset_ex(-0.5*$spacing) ], # +2 on the offset here makes sure that Clipper float truncation # won't shrink the clip polygon to be smaller than intended. - [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$perimeter_spacing + 2) ], + [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$spacing + 2) ], ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } From 3b1e92117140d31cd96d1e36ced25d1a7b83ec46 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 19:14:41 +0100 Subject: [PATCH 086/114] Slight changes to the defaults --- README.markdown | 4 ++-- lib/Slic3r/Config.pm | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 76c1845d38..c580c6387f 100644 --- a/README.markdown +++ b/README.markdown @@ -140,7 +140,7 @@ The author of the Silk icon set is Mark James. (default: 30) --external-perimeter-speed Speed of print moves for the external perimeter in mm/s or % over perimeter speed - (default: 100%) + (default: 70%) --infill-speed Speed of print moves in mm/s (default: 60) --solid-infill-speed Speed of print moves for solid surfaces in mm/s or % over infill speed (default: 60) @@ -245,7 +245,7 @@ The author of the Silk icon set is Mark James. --fan-below-layer-time Enable fan if layer print time is below this approximate number of seconds (default: 60) --slowdown-below-layer-time Slow down if layer print time is below this approximate number - of seconds (default: 15) + of seconds (default: 30) --min-print-speed Minimum print speed (mm/s, default: 10) --disable-fan-first-layers Disable fan for the first N layers (default: 1) --fan-always-on Keep fan always on at min fan speed, even for layers that don't need diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index d98c301516..b46b4e006c 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -254,7 +254,7 @@ our $Options = { cli => 'external-perimeter-speed=s', type => 'f', ratio_over => 'perimeter_speed', - default => '100%', + default => '70%', }, 'infill_speed' => { label => 'Infill', @@ -808,7 +808,7 @@ END type => 'i', max => 1000, width => 60, - default => 15, + default => 30, }, 'min_print_speed' => { label => 'Min print speed', From 08a72c08249e8e1f3234d5ecd4d313debbf1eed0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 21:39:13 +0100 Subject: [PATCH 087/114] Use bridge math for the first solid infill layer above sparse infill. #240 --- lib/Slic3r/Fill.pm | 4 +-- lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print.pm | 3 ++ lib/Slic3r/Print/Object.pm | 66 ++++++++++++++++++++++++++++++++++++++ lib/Slic3r/Surface.pm | 29 +++++++++++++++-- 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 2c38824999..1031b6e86d 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -126,8 +126,8 @@ sub make_fill { my $filler = $Slic3r::Config->fill_pattern; my $density = $Slic3r::Config->fill_density; my $flow_spacing = $layer->infill_flow->spacing; - my $is_bridge = $layer->id > 0 && $surface->surface_type == S_TYPE_BOTTOM; - my $is_solid = (grep { $surface->surface_type == $_ } S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_INTERNALSOLID) ? 1 : 0; + my $is_bridge = $layer->id > 0 && $surface->is_bridge; + my $is_solid = $surface->is_solid; # force 100% density and rectilinear fill for external surfaces if ($surface->surface_type != S_TYPE_INTERNAL) { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index c9792c60b3..cb23f719aa 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -458,7 +458,7 @@ sub process_bridges { ($_->surface_type == S_TYPE_BOTTOM && $self->id > 0) || $_->surface_type == S_TYPE_TOP } @{$self->fill_surfaces} or return; - my @internal_surfaces = grep { $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALSOLID } @{$self->slices}; + my @internal_surfaces = grep $_->is_internal, @{$self->slices}; SURFACE: foreach my $surface (@solid_surfaces) { my $expolygon = $surface->expolygon->safety_offset; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 4263c68b08..322b8fa48f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -356,6 +356,9 @@ sub export_gcode { $status_cb->(60, "Generating horizontal shells"); $_->discover_horizontal_shells for @{$self->objects}; $_->clip_fill_surfaces for @{$self->objects}; + # the following step needs to be done before combination because it may need + # to remove only half of the combined infill + $_->bridge_over_infill for @{$self->objects}; # combine fill surfaces to honor the "infill every N layers" option $status_cb->(70, "Combining infill"); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 5dae0482e4..5f0e51ee01 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -426,6 +426,72 @@ sub clip_fill_surfaces { } } +sub bridge_over_infill { + my $self = shift; + + # calculate the number of layers to remove below each bridged one + my $skip = int(); + + for my $layer_id (1..$#{$self->layers}) { + my $layer = $self->layers->[$layer_id]; + my $lower_layer = $self->layers->[$layer_id-1]; + + foreach my $layerm (@{$layer->regions}) { + # compute the areas needing bridge math + my @internal_solid = grep $_->surface_type == S_TYPE_INTERNALSOLID, @{$layerm->fill_surfaces}; + my @lower_internal = grep $_->surface_type == S_TYPE_INTERNAL, map @{$_->fill_surfaces}, @{$lower_layer->regions}; + my $to_bridge = intersection_ex( + [ map $_->p, @internal_solid ], + [ map $_->p, @lower_internal ], + ); + next unless @$to_bridge; + Slic3r::debugf "Bridging %d internal areas at layer %d\n", scalar(@$to_bridge), $layer_id; + + # build the new collection of fill_surfaces + { + my @new_surfaces = grep $_->surface_type != S_TYPE_INTERNALSOLID, @{$layerm->fill_surfaces}; + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => S_TYPE_INTERNALBRIDGE, + ), @$to_bridge; + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => S_TYPE_INTERNALSOLID, + ), @{diff_ex( + [ map $_->p, @internal_solid ], + [ map @$_, @$to_bridge ], + )}; + @{$layerm->fill_surfaces} = @new_surfaces; + } + + # exclude infill from the layers below if needed + # see discussion at https://github.com/alexrj/Slic3r/issues/240 + { + my $excess = $layerm->infill_flow->bridge_width - $layerm->height; + for (my $i = $layer_id-1; $excess >= $self->layers->[$i]->height; $i--) { + Slic3r::debugf " skipping infill below those areas at layer %d\n", $i; + foreach my $lower_layerm (@{$self->layers->[$i]->regions}) { + my @new_surfaces = (); + # subtract the area from all types of surfaces + foreach my $group (Slic3r::Surface->group(@{$lower_layerm->fill_surfaces})) { + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => $group->[0]->surface_type, + ), @{diff_ex( + [ map $_->p, @$group ], + [ map @$_, @$to_bridge ], + )}; + } + @{$lower_layerm->fill_surfaces} = @new_surfaces; + } + + $excess -= $self->layers->[$i]->height; + } + } + } + } +} + sub discover_horizontal_shells { my $self = shift; diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 6912a15af0..8ebddaa146 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -4,7 +4,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(S_TYPE_TOP S_TYPE_BOTTOM S_TYPE_INTERNAL S_TYPE_INTERNALSOLID); +our @EXPORT_OK = qw(S_TYPE_TOP S_TYPE_BOTTOM S_TYPE_INTERNAL S_TYPE_INTERNALSOLID S_TYPE_INTERNALBRIDGE); our %EXPORT_TAGS = (types => \@EXPORT_OK); use constant S_EXPOLYGON => 0; @@ -17,6 +17,7 @@ use constant S_TYPE_TOP => 0; use constant S_TYPE_BOTTOM => 1; use constant S_TYPE_INTERNAL => 2; use constant S_TYPE_INTERNALSOLID => 3; +use constant S_TYPE_INTERNALBRIDGE => 4; sub new { my $class = shift; @@ -51,7 +52,7 @@ sub group { my %unique_types = (); foreach my $surface (@surfaces) { - my $type = ($params->{merge_solid} && grep { $surface->surface_type == $_ } S_TYPE_TOP, S_TYPE_BOTTOM, S_TYPE_INTERNALSOLID) + my $type = ($params->{merge_solid} && $surface->is_solid) ? 'solid' : $surface->surface_type; $type .= "_" . ($surface->bridge_angle // ''); #/ @@ -89,4 +90,28 @@ sub p { return @{$self->expolygon}; } +sub is_solid { + my $self = shift; + my $type = $self->surface_type; + return $type == S_TYPE_TOP + || $type == S_TYPE_BOTTOM + || $type == S_TYPE_INTERNALSOLID + || $type == S_TYPE_INTERNALBRIDGE; +} + +sub is_internal { + my $self = shift; + my $type = $self->surface_type; + return $type == S_TYPE_INTERNAL + || $type == S_TYPE_INTERNALSOLID + || $type == S_TYPE_INTERNALBRIDGE; +} + +sub is_bridge { + my $self = shift; + my $type = $self->surface_type; + return $type == S_TYPE_BOTTOM + || $type == S_TYPE_INTERNALBRIDGE; +} + 1; From de0c2febd99042096215416016d7b30e22ab52cd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 23 Feb 2013 23:50:46 +0100 Subject: [PATCH 088/114] Remove useless lines --- lib/Slic3r/Print/Object.pm | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 5f0e51ee01..638482fbc7 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -429,9 +429,6 @@ sub clip_fill_surfaces { sub bridge_over_infill { my $self = shift; - # calculate the number of layers to remove below each bridged one - my $skip = int(); - for my $layer_id (1..$#{$self->layers}) { my $layer = $self->layers->[$layer_id]; my $lower_layer = $self->layers->[$layer_id-1]; From c3dde7477614faeff7dbc6995c458634f8ac840e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 24 Feb 2013 16:40:14 +0100 Subject: [PATCH 089/114] Fix brim and extra perimeters after recent changes to ->slices --- lib/Slic3r/Print.pm | 2 +- lib/Slic3r/Print/Object.pm | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 322b8fa48f..8676cd2197 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -635,7 +635,7 @@ sub make_brim { polygon => Slic3r::Polygon->new($_), role => EXTR_ROLE_SKIRT, flow_spacing => $flow->spacing, - ) for Slic3r::Geometry::Clipper::offset(\@islands, $i * $flow->scaled_spacing, undef, JT_SQUARE); + ) for Slic3r::Geometry::Clipper::offset(\@islands, ($i - 0.5) * $flow->scaled_spacing, undef, JT_SQUARE); # -0.5 because islands are not represented by their centerlines # TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions } } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 638482fbc7..c33660230a 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -208,15 +208,15 @@ sub make_perimeters { for my $layer_id (0 .. $self->layer_count-2) { my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id]; - my $perimeter_flow = $layerm->perimeter_flow; + my $perimeter_spacing = $layerm->perimeter_flow->scaled_spacing; - my $overlap = $perimeter_flow->scaled_spacing; # one perimeter + my $overlap = $perimeter_spacing; # one perimeter # compute polygons representing the thickness of the first external perimeter of # the upper layer slices my $upper = diff_ex( - [ map @$_, map $_->expolygon->offset_ex(+ 0.5 * $perimeter_flow->scaled_spacing), @{$upper_layerm->slices} ], - [ map @$_, map $_->expolygon->offset_ex(- $overlap + (0.5 * $perimeter_flow->scaled_spacing)), @{$upper_layerm->slices} ], + [ map @{$_->expolygon}, @{$upper_layerm->slices} ], + [ map @$_, map $_->expolygon->offset_ex(-$overlap), @{$upper_layerm->slices} ], ); next if !@$upper; @@ -225,10 +225,10 @@ sub make_perimeters { my $ignore = []; { my $diff = diff_ex( - [ map @$_, map $_->expolygon->offset_ex(- ($Slic3r::Config->perimeters-0.5) * $perimeter_flow->scaled_spacing), @{$layerm->slices} ], - [ map @{$_->expolygon}, @{$upper_layerm->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- $Slic3r::Config->perimeters * $perimeter_spacing), @{$layerm->slices} ], + [ map @$_, map $_->expolygon->offset_ex(- 0.5*$perimeter_spacing), @{$upper_layerm->slices} ], ); - $ignore = [ map @$_, map $_->offset_ex($perimeter_flow->scaled_spacing), @$diff ]; + $ignore = [ map @$_, map $_->offset_ex($perimeter_spacing), @$diff ]; } foreach my $slice (@{$layerm->slices}) { @@ -238,9 +238,9 @@ sub make_perimeters { # of our slice my $hypothetical_perimeter; { - my $outer = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-1.5) * $perimeter_flow->scaled_spacing - scaled_epsilon) ]; + my $outer = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-1) * $perimeter_spacing - scaled_epsilon) ]; last CYCLE if !@$outer; - my $inner = [ map @$_, $slice->expolygon->offset_ex(- ($hypothetical_perimeter_num-0.5) * $perimeter_flow->scaled_spacing) ]; + my $inner = [ map @$_, $slice->expolygon->offset_ex(- $hypothetical_perimeter_num * $perimeter_spacing) ]; last CYCLE if !@$inner; $hypothetical_perimeter = diff_ex($outer, $inner); } From bd76e7677c30ebd22099fe0b7c0b431b2114b582 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 24 Feb 2013 17:04:19 +0100 Subject: [PATCH 090/114] Adjust extra perimeters --- lib/Slic3r/Print/Object.pm | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index c33660230a..cf41fae51d 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -212,25 +212,16 @@ sub make_perimeters { my $overlap = $perimeter_spacing; # one perimeter - # compute polygons representing the thickness of the first external perimeter of - # the upper layer slices + # compute the polygon used to trigger the additional perimeters: the hole represents + # the required overlap, while the contour represents how different should the slices be + # (thus how horizontal should the slope be) before extra perimeters are not generated, and + # normal solid infill is used my $upper = diff_ex( - [ map @{$_->expolygon}, @{$upper_layerm->slices} ], + [ map @$_, map $_->expolygon->offset_ex($overlap), @{$upper_layerm->slices} ], [ map @$_, map $_->expolygon->offset_ex(-$overlap), @{$upper_layerm->slices} ], ); next if !@$upper; - # we need to limit our detection to the areas which would actually benefit from - # more perimeters. so, let's compute the area we want to ignore - my $ignore = []; - { - my $diff = diff_ex( - [ map @$_, map $_->expolygon->offset_ex(- $Slic3r::Config->perimeters * $perimeter_spacing), @{$layerm->slices} ], - [ map @$_, map $_->expolygon->offset_ex(- 0.5*$perimeter_spacing), @{$upper_layerm->slices} ], - ); - $ignore = [ map @$_, map $_->offset_ex($perimeter_spacing), @$diff ]; - } - foreach my $slice (@{$layerm->slices}) { my $hypothetical_perimeter_num = $Slic3r::Config->perimeters + 1; CYCLE: while (1) { @@ -246,9 +237,7 @@ sub make_perimeters { } last CYCLE if !@$hypothetical_perimeter; - my $intersection = intersection_ex([ map @$_, @$upper ], [ map @$_, @$hypothetical_perimeter ]); - $intersection = diff_ex([ map @$_, @$intersection ], $ignore) if @$ignore; last CYCLE if !@{ $intersection }; Slic3r::debugf " adding one more perimeter at layer %d\n", $layer_id; $slice->additional_inner_perimeters(($slice->additional_inner_perimeters || 0) + 1); From 7412d4a6872d4cec05ef64db2ff13e05ff45ad4e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 24 Feb 2013 18:17:08 +0100 Subject: [PATCH 091/114] Bugfix: manifoldness error triggered with multi-volume AMF files. #1019 --- lib/Slic3r/GUI/Plater.pm | 2 +- lib/Slic3r/Model.pm | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index caa9dcb3fb..f4e1f32196 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1088,7 +1088,7 @@ sub _trigger_model_object { sub check_manifoldness { my $self = shift; - $self->is_manifold($self->get_model_object->mesh->check_manifoldness); + $self->is_manifold($self->get_model_object->check_manifoldness); return $self->is_manifold; } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index c05ee8ee24..67793dd14b 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -73,6 +73,7 @@ has 'attributes' => (is => 'rw', default => sub { {} }); package Slic3r::Model::Object; use Moo; +use List::Util qw(first); use Slic3r::Geometry qw(X Y Z); has 'input_file' => (is => 'rw'); @@ -112,6 +113,8 @@ sub add_instance { sub mesh { my $self = shift; + # this mesh won't be suitable for check_manifoldness as multiple + # facets from different volumes may use the same vertices return Slic3r::TriangleMesh->new( vertices => $self->vertices, facets => [ map @{$_->facets}, @{$self->volumes} ], @@ -136,6 +139,11 @@ sub materials_count { return scalar keys %materials; } +sub check_manifoldness { + my $self = shift; + return (first { !$_->mesh->check_manifoldness } @{$self->volumes}) ? 0 : 1; +} + package Slic3r::Model::Volume; use Moo; From 038737abe67e383b374426e27b9f6b06d8f305ec Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 26 Feb 2013 14:15:05 +0100 Subject: [PATCH 092/114] Bugfix: minimum flow for low layer heights was still too low --- lib/Slic3r/Flow.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index dbce589fbe..66a8213dc8 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -39,10 +39,10 @@ sub _build_width { } my $min = max( - ((($self->nozzle_diameter/2) ** 2) / $self->layer_height * 0.8), + ($volume / $self->layer_height), ($self->nozzle_diameter * 1.05), ); - my $max = $self->nozzle_diameter * 1.6; + my $max = $self->nozzle_diameter * 2; $width = $max if $width > $max; $width = $min if $width < $min; From f4e8cdbf368169a17adc1ddea39cd78b0e7b9b4f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 26 Feb 2013 20:00:05 +0100 Subject: [PATCH 093/114] Better logic for extra perimeters --- lib/Slic3r/Print/Object.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index cf41fae51d..19a715f6a0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -1,7 +1,7 @@ package Slic3r::Print::Object; use Moo; -use List::Util qw(min); +use List::Util qw(min sum); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); @@ -237,8 +237,12 @@ sub make_perimeters { } last CYCLE if !@$hypothetical_perimeter; + # compute the area of the hypothetical perimeter + my $hp_area = sum(map $_->area, @$hypothetical_perimeter); + + # only add the perimeter if the intersection is at least 20%, otherwise we'd get no benefit my $intersection = intersection_ex([ map @$_, @$upper ], [ map @$_, @$hypothetical_perimeter ]); - last CYCLE if !@{ $intersection }; + last CYCLE if (sum(map $_->area, @{ $intersection }) // 0) < $hp_area * 0.2; Slic3r::debugf " adding one more perimeter at layer %d\n", $layer_id; $slice->additional_inner_perimeters(($slice->additional_inner_perimeters || 0) + 1); $hypothetical_perimeter_num++; From 37be4f7319a1e4b7f86ed83226106e3b50b04833 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 26 Feb 2013 20:52:13 +0100 Subject: [PATCH 094/114] Some cleanup for detect_surfaces_type() --- lib/Slic3r/Print/Object.pm | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 19a715f6a0..1418b3c145 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -294,19 +294,19 @@ sub detect_surfaces_type { # prepare a reusable subroutine to make surface differences my $surface_difference = sub { - my ($subject_surfaces, $clip_surfaces, $result_type, $layerm) = @_; + my ($subject_surfaces, $clip_surfaces, $result_type, $layerm) = @_;use XXX; ZZZ "here" if grep ref eq 'Slic3r::Surface', @$subject_surfaces, @$clip_surfaces; my $expolygons = diff_ex( - [ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$subject_surfaces ], - [ map { ref $_ eq 'ARRAY' ? $_ : ref $_ eq 'Slic3r::ExPolygon' ? @$_ : $_->p } @$clip_surfaces ], + [ map @$_, @$subject_surfaces ], + [ map @$_, @$clip_surfaces ], 1, ); - return grep $_->contour->is_printable($layerm->infill_flow), + return grep $_->contour->is_printable($layerm->perimeter_flow), map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @$expolygons; }; for my $region_id (0 .. ($self->print->regions_count-1)) { - for (my $i = 0; $i < $self->layer_count; $i++) { + for my $i (0 .. ($self->layer_count-1)) { my $layerm = $self->layers->[$i]->regions->[$region_id]; # comparison happens against the *full* slices (considering all regions) @@ -318,7 +318,12 @@ sub detect_surfaces_type { # find top surfaces (difference between current surfaces # of current layer and upper one) if ($upper_layer) { - @top = $surface_difference->($layerm->slices, $upper_layer->slices, S_TYPE_TOP, $layerm); + @top = $surface_difference->( + [ map $_->expolygon, @{$layerm->slices} ], + $upper_layer->slices, + S_TYPE_TOP, + $layerm, + ); } else { # if no upper layer, all surfaces of this one are solid @top = @{$layerm->slices}; @@ -328,7 +333,13 @@ sub detect_surfaces_type { # find bottom surfaces (difference between current surfaces # of current layer and lower one) if ($lower_layer) { - @bottom = $surface_difference->($layerm->slices, $lower_layer->slices, S_TYPE_BOTTOM, $layerm); + # lower layer's slices are already Surface objects + @bottom = $surface_difference->( + [ map $_->expolygon, @{$layerm->slices} ], + $lower_layer->slices, + S_TYPE_BOTTOM, + $layerm, + ); } else { # if no lower layer, all surfaces of this one are solid @bottom = @{$layerm->slices}; @@ -341,11 +352,16 @@ sub detect_surfaces_type { if (@top && @bottom) { my $overlapping = intersection_ex([ map $_->p, @top ], [ map $_->p, @bottom ]); Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->id, scalar(@$overlapping); - @top = $surface_difference->([@top], $overlapping, S_TYPE_TOP, $layerm); + @top = $surface_difference->([map $_->expolygon, @top], $overlapping, S_TYPE_TOP, $layerm); } # find internal surfaces (difference between top/bottom surfaces and others) - @internal = $surface_difference->($layerm->slices, [@top, @bottom], S_TYPE_INTERNAL, $layerm); + @internal = $surface_difference->( + [ map $_->expolygon, @{$layerm->slices} ], + [ map $_->expolygon, @top, @bottom ], + S_TYPE_INTERNAL, + $layerm, + ); # save surfaces to layer @{$layerm->slices} = (@bottom, @top, @internal); From 8ce7b13db525b0d247d46c7eee587f654c9e22d0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 26 Feb 2013 20:54:40 +0100 Subject: [PATCH 095/114] Fixed regression in is_printable() - needs unit test --- lib/Slic3r/Polygon.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index da23e823af..0f38273817 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -129,7 +129,7 @@ sub is_printable { # detect them and we would be discarding them. my $p = $self->clone; $p->make_counter_clockwise; - return $p->offset($flow->scaled_width / 2) ? 1 : 0; + return $p->offset(-$flow->scaled_width / 2) ? 1 : 0; } sub is_valid { From bf9fe1b5058bba2764bfe5270b1e9e7c39020617 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 01:30:32 +0100 Subject: [PATCH 096/114] Bugfix: internal bridge surfaces were incorrectly merged with other internal solid surfaces, causing suboptimal results --- lib/Slic3r/Surface.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 8ebddaa146..a3cbe4b085 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -93,10 +93,10 @@ sub p { sub is_solid { my $self = shift; my $type = $self->surface_type; + # S_TYPE_INTERNALBRIDGE is not solid because we can't merge it with other solid types return $type == S_TYPE_TOP || $type == S_TYPE_BOTTOM - || $type == S_TYPE_INTERNALSOLID - || $type == S_TYPE_INTERNALBRIDGE; + || $type == S_TYPE_INTERNALSOLID; } sub is_internal { From d00c2882c42507a49bf0eafcfb0bafc055d2fcd8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 10:30:05 +0100 Subject: [PATCH 097/114] Remove debug line slipped into last commit --- lib/Slic3r/Print/Object.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 1418b3c145..dc270a9dbf 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -294,7 +294,7 @@ sub detect_surfaces_type { # prepare a reusable subroutine to make surface differences my $surface_difference = sub { - my ($subject_surfaces, $clip_surfaces, $result_type, $layerm) = @_;use XXX; ZZZ "here" if grep ref eq 'Slic3r::Surface', @$subject_surfaces, @$clip_surfaces; + my ($subject_surfaces, $clip_surfaces, $result_type, $layerm) = @_; my $expolygons = diff_ex( [ map @$_, @$subject_surfaces ], [ map @$_, @$clip_surfaces ], From a9090688f95a61897156a9a9d4cc7b8847dd6e77 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 10:43:50 +0100 Subject: [PATCH 098/114] Refactor bridge flow to Slic3r::Flow::Bridge class --- lib/Slic3r/Extruder.pm | 6 ++++++ lib/Slic3r/Fill.pm | 4 ++-- lib/Slic3r/Flow.pm | 31 ++++++++++++++++++------------- lib/Slic3r/Layer.pm | 2 +- lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print/Object.pm | 2 +- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 9cfc4dcf76..ece4be0178 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -13,12 +13,18 @@ use constant OPTIONS => [qw( has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; +has 'bridge_flow' => (is => 'lazy'); has 'retracted' => (is => 'rw', default => sub {0} ); has 'restart_extra' => (is => 'rw', default => sub {0} ); has 'e_per_mm3' => (is => 'lazy'); has 'retract_speed_mm_min' => (is => 'lazy'); has '_mm3_per_mm_cache' => (is => 'ro', default => sub {{}}); +sub _build_bridge_flow { + my $self = shift; + return Slic3r::Flow::Bridge->new(nozzle_diameter => $self->nozzle_diameter); +} + sub _build_e_per_mm3 { my $self = shift; return $self->extrusion_multiplier * (4 / (($self->filament_diameter ** 2) * PI)); diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 1031b6e86d..809fcfd613 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -135,7 +135,7 @@ sub make_fill { $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = $layer->infill_flow->bridge_spacing; + $flow_spacing = $layer->extruders->{infill}->bridge_flow->spacing; } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -157,7 +157,7 @@ sub make_fill { my $params = shift @paths; # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) - $params->{flow_spacing} = $layer->infill_flow->bridge_width if $is_bridge; + $params->{flow_spacing} = $layer->extruders->{infill}->bridge_flow->width if $is_bridge; # save into layer next unless @paths; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 66a8213dc8..e2e780921f 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -9,8 +9,6 @@ has 'layer_height' => (is => 'ro', default => sub { $Slic3r::Config->layer_ has 'width' => (is => 'rwp', builder => 1); has 'spacing' => (is => 'lazy'); -has 'bridge_width' => (is => 'lazy'); -has 'bridge_spacing' => (is => 'lazy'); has 'scaled_width' => (is => 'lazy'); has 'scaled_spacing' => (is => 'lazy'); @@ -73,17 +71,6 @@ sub clone { ); } -sub _build_bridge_width { - my $self = shift; - return sqrt($Slic3r::Config->bridge_flow_ratio * ($self->nozzle_diameter**2)); -} - -sub _build_bridge_spacing { - my $self = shift; - my $width = $self->bridge_width; - return $width + &Slic3r::OVERLAP_FACTOR * ($width * PI / 4 - $width); -} - sub _build_scaled_width { my $self = shift; return scale $self->width; @@ -94,4 +81,22 @@ sub _build_scaled_spacing { return scale $self->spacing; } + +package Slic3r::Flow::Bridge; +use Moo; +extends 'Slic3r::Flow'; + +use Slic3r::Geometry qw(PI); + +sub _build_width { + my $self = shift; + return sqrt($Slic3r::Config->bridge_flow_ratio * ($self->nozzle_diameter**2)); +} + +sub _build_spacing { + my $self = shift; + my $width = $self->width; + return $width + &Slic3r::OVERLAP_FACTOR * ($width * PI / 4 - $width); +} + 1; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index ec350e1d13..951805c8af 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -64,7 +64,7 @@ sub support_material_contact_height { # TODO: check what upper region applies instead of considering the first one my $upper_layer = $self->object->layers->[ $self->id + 1 ] // $self; - my $h = ($self->height + $upper_layer->height) - $upper_layer->regions->[0]->infill_flow->bridge_width; + my $h = ($self->height + $upper_layer->height) - $upper_layer->regions->[0]->extruders->{infill}->bridge_flow->width; # If layer height is less than half the bridge width then we'll get a negative height for contact area. # The optimal solution would be to skip some layers during support material generation, but for now diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index cb23f719aa..eb55fcc557 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -13,7 +13,7 @@ has 'layer' => ( trigger => 1, handles => [qw(id slice_z print_z height flow)], ); -has 'region' => (is => 'ro', required => 1); +has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'perimeter_flow' => (is => 'rw'); has 'infill_flow' => (is => 'rw'); has 'infill_area_threshold' => (is => 'lazy'); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index dc270a9dbf..e502fa83d5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -473,7 +473,7 @@ sub bridge_over_infill { # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 { - my $excess = $layerm->infill_flow->bridge_width - $layerm->height; + my $excess = $layerm->extruders->{infill}->bridge_flow->width - $layerm->height; for (my $i = $layer_id-1; $excess >= $self->layers->[$i]->height; $i--) { Slic3r::debugf " skipping infill below those areas at layer %d\n", $i; foreach my $lower_layerm (@{$self->layers->[$i]->regions}) { From c3d90a1ff8155eda20cba1080fbfb51e8b9e5d56 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 10:44:42 +0100 Subject: [PATCH 099/114] Rename $layer to $layerm in Slic3r::Fill too for consistency --- lib/Slic3r/Fill.pm | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 809fcfd613..27b0434f0f 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -48,17 +48,17 @@ sub filler { sub make_fill { my $self = shift; - my ($layer) = @_; + my ($layerm) = @_; - Slic3r::debugf "Filling layer %d:\n", $layer->id; + Slic3r::debugf "Filling layer %d:\n", $layerm->id; # merge overlapping surfaces my @surfaces = (); { - my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layer->fill_surfaces}; + my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces}; # give priority to bridges - my @groups = Slic3r::Surface->group({merge_solid => 1}, @{$layer->fill_surfaces}); + my @groups = Slic3r::Surface->group({merge_solid => 1}, @{$layerm->fill_surfaces}); @groups = sort { defined $a->[0]->bridge_angle ? -1 : 0 } @groups; foreach my $group (@groups) { @@ -91,7 +91,7 @@ sub make_fill { # add spacing between adjacent surfaces { - my $distance = $layer->infill_flow->scaled_spacing / 2; + my $distance = $layerm->infill_flow->scaled_spacing / 2; my @offsets = (); foreach my $surface (@surfaces) { my $expolygon = $surface->expolygon; @@ -125,8 +125,8 @@ sub make_fill { SURFACE: foreach my $surface (@surfaces) { my $filler = $Slic3r::Config->fill_pattern; my $density = $Slic3r::Config->fill_density; - my $flow_spacing = $layer->infill_flow->spacing; - my $is_bridge = $layer->id > 0 && $surface->is_bridge; + my $flow_spacing = $layerm->infill_flow->spacing; + my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; # force 100% density and rectilinear fill for external surfaces @@ -135,7 +135,7 @@ sub make_fill { $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = $layer->extruders->{infill}->bridge_flow->spacing; + $flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing; } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -146,7 +146,7 @@ sub make_fill { my @paths; { my $f = $self->filler($filler); - $f->layer_id($layer->id); + $f->layer_id($layerm->id); @paths = $f->fill_surface( $surface, density => $density, @@ -157,7 +157,7 @@ sub make_fill { my $params = shift @paths; # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) - $params->{flow_spacing} = $layer->extruders->{infill}->bridge_flow->width if $is_bridge; + $params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge; # save into layer next unless @paths; @@ -170,7 +170,7 @@ sub make_fill { : $is_solid ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) : EXTR_ROLE_FILL), - height => $surface->depth_layers * $layer->height, + height => $surface->depth_layers * $layerm->height, flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"), ), @paths, ], @@ -179,8 +179,8 @@ sub make_fill { } # add thin fill regions - push @fills, @{$layer->thin_fills}; - push @fills_ordering_points, map $_->unpack->points->[0], @{$layer->thin_fills}; + push @fills, @{$layerm->thin_fills}; + push @fills_ordering_points, map $_->unpack->points->[0], @{$layerm->thin_fills}; # organize infill paths using a nearest-neighbor search @fills = @fills[ chained_path(\@fills_ordering_points) ]; From 919d1131f80fb2ac5de0b3a66022225197ce7355 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 11:04:14 +0100 Subject: [PATCH 100/114] Use nearest-neighbor search for internal perimeters too. #1025 --- lib/Slic3r/Layer/Region.pm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index eb55fcc557..d1fd5b9c62 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -173,7 +173,7 @@ sub make_perimeters { # ) my @perimeters = (); # one item per depth; each item - # organize islands using a shortest path search + # organize islands using a nearest-neighbor search my @surfaces = @{chained_path_items([ map [ $_->contour->[0], $_ ], @{$self->slices}, ])}; @@ -245,6 +245,12 @@ sub make_perimeters { last if !@new_offsets || $loop == $loop_number; @last_offsets = @new_offsets; + + # sort loops before storing them + @last_offsets = @{chained_path_items([ + map [ $_->contour->[0], $_ ], @last_offsets, + ])}; + push @{ $perimeters[-1] }, [@last_offsets]; } From bb83f070ae7ea041c03c2b497c4fd884c2b0717b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 11:08:08 +0100 Subject: [PATCH 101/114] Load threads::shared before Wx. #1018 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index f9fb3319d3..7d1c4dd672 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -18,7 +18,7 @@ sub debugf { our $have_threads; BEGIN { use Config; - $have_threads = $Config{useithreads} && eval "use threads; use Thread::Queue; 1"; + $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1"; } warn "Running Slic3r under Perl >= 5.16 is not supported nor recommended\n" From fafad0fd81c579125bec453ec9797c5d45214eab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 27 Feb 2013 11:26:52 +0100 Subject: [PATCH 102/114] Code for memory usage statistics --- lib/Slic3r/Print.pm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8676cd2197..1aad0ae390 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -426,6 +426,18 @@ sub export_gcode { $self->make_skirt; $self->make_brim; # must come after make_skirt + # time to make some statistics + if (0) { + eval "use Devel::Size"; + print "MEMORY USAGE:\n"; + printf " meshes = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->meshes), @{$self->objects})/1024/1024; + printf " layer slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->layers}, @{$self->objects})/1024/1024; + printf " region slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024; + printf " perimeters = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->perimeters), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024; + printf " fills = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->fills), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024; + printf " print object = %.1fMb\n", Devel::Size::total_size($self)/1024/1024; + } + # output everything to a G-code file my $output_file = $self->expanded_output_filepath($params{output_file}); $status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); From 28467b68e06600baaf3f9daf18d6b1a658d0dd00 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Wed, 27 Feb 2013 15:16:07 -0800 Subject: [PATCH 103/114] typo - fix wrong bridge direction #621 fixes two out of three reported in #621 (but not the first one) likely the same issue in #706 and #1003 --- lib/Slic3r/Layer/Region.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index d1fd5b9c62..ee30232777 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -538,7 +538,7 @@ sub process_bridges { } elsif (@edges) { my $center = Slic3r::Geometry::bounding_box_center([ map @$_, @edges ]); my $x = my $y = 0; - foreach my $point (map @$, @edges) { + foreach my $point (map @$_, @edges) { my $line = Slic3r::Line->new($center, $point); my $dir = $line->direction; my $len = $line->length; From b36ed0538d4e5f59bd60f0bd04017507d213ee60 Mon Sep 17 00:00:00 2001 From: Mike Sheldrake Date: Thu, 28 Feb 2013 00:55:31 -0800 Subject: [PATCH 104/114] make sure combine infill test has infill to combine #1023 --- t/combineinfill.t | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/t/combineinfill.t b/t/combineinfill.t index 8c1ba9e778..aa4ed91a11 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -19,6 +19,7 @@ use Slic3r::Test; $config->set('top_solid_layers', 0); $config->set('infill_every_layers', 6); $config->set('layer_height', 0.06); + $config->set('perimeters', 1); my $test = sub { my ($shift) = @_; @@ -26,14 +27,16 @@ use Slic3r::Test; my $self = Slic3r::Test::init_print('20mm_cube', config => $config); $shift /= &Slic3r::SCALING_FACTOR; + my $scale = 4; # make room for fat infill lines with low layer height # Put a slope on the box's sides by shifting x and y coords by $tilt * (z / boxheight). # The test here is to put such a slight slope on the walls that it should # not trigger any extra fill on fill layers that should be empty when # combine infill is enabled. - $_->[0] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)), for @{$self->objects->[0]->meshes->[0]->vertices}; - $_->[1] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)), for @{$self->objects->[0]->meshes->[0]->vertices}; - + $_->[0] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)) for @{$self->objects->[0]->meshes->[0]->vertices}; + $_->[1] += $shift * ($_->[2] / (20 / &Slic3r::SCALING_FACTOR)) for @{$self->objects->[0]->meshes->[0]->vertices}; + $_ = [$_->[0]*$scale, $_->[1]*$scale, $_->[2]] for @{$self->objects->[0]->meshes->[0]->vertices}; + # copy of Print::export_gcode() up to the point # after fill surfaces are combined $self->init_extruders; From 70092a9cf5ee43c7bfbe0b20969f67a0a5908ff8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 4 Mar 2013 23:37:58 +0100 Subject: [PATCH 105/114] Apply only_retract_when_crossing_perimeters to all infills --- lib/Slic3r/ExtrusionPath.pm | 7 +++++++ lib/Slic3r/GCode.pm | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index e52dc66edf..212a1bb711 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -93,6 +93,13 @@ sub endpoints { sub is_printable { 1 } +sub is_fill { + my $self = shift; + return $self->role == EXTR_ROLE_FILL + || $self->role == EXTR_ROLE_SOLIDFILL + || $self->role == EXTR_ROLE_TOPSOLIDFILL; +} + sub split_at_acute_angles { my $self = shift; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 4eaf9846fe..55b3dcbf94 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -198,7 +198,7 @@ sub extrude_path { # note that we're only considering the current object's islands, while we should # build a more complete configuration space $travel->translate(-$self->shift_x, -$self->shift_y); - if (!$Slic3r::Config->only_retract_when_crossing_perimeters || $path->role != EXTR_ROLE_FILL || !first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { + if (!$Slic3r::Config->only_retract_when_crossing_perimeters || !$path->is_fill || !first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { $gcode .= $self->retract(travel_to => $path->points->[0]); } } From 72613ae2b4368c53f046140b7cec1ad31ef8f5f9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Mar 2013 15:01:45 +0100 Subject: [PATCH 106/114] Enable only_retract_when_crossing_perimeters by default --- README.markdown | 2 +- lib/Slic3r/Config.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index c580c6387f..e6ec81eba8 100644 --- a/README.markdown +++ b/README.markdown @@ -193,7 +193,7 @@ The author of the Silk icon set is Mark James. --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) --only-retract-when-crossing-perimeters Disable retraction when travelling between infill paths inside the same island. - (default: no) + (default: yes) --solid-infill-below-area Force solid infill when a region has a smaller area than this threshold (mm^2, default: 70) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b46b4e006c..4ea040401b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -543,7 +543,7 @@ our $Options = { tooltip => 'Disables retraction when travelling between infill paths inside the same island.', cli => 'only-retract-when-crossing-perimeters!', type => 'bool', - default => 0, + default => 1, }, 'support_material' => { label => 'Generate support material', From 37810f777fec3aecc889c363bdb665c19a9ba970 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Mar 2013 17:30:27 +0100 Subject: [PATCH 107/114] Make only_retract_when_crossing_perimeters work with avoid_crossing_perimeters --- lib/Slic3r/GCode.pm | 73 ++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 55b3dcbf94..a27508be2c 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -186,26 +186,9 @@ sub extrude_path { return $gcode; } - my $gcode = ""; - - # skip retract for support material - { - # retract if distance from previous position is greater or equal to the one specified by the user - my $travel = Slic3r::Line->new($self->last_pos->clone, $path->points->[0]->clone); - if ($travel->length >= scale $self->extruder->retract_before_travel - && ($path->role != EXTR_ROLE_SUPPORTMATERIAL || !$self->layer->support_islands_enclose_line($travel))) { - # move travel back to original layer coordinates. - # note that we're only considering the current object's islands, while we should - # build a more complete configuration space - $travel->translate(-$self->shift_x, -$self->shift_y); - if (!$Slic3r::Config->only_retract_when_crossing_perimeters || !$path->is_fill || !first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { - $gcode .= $self->retract(travel_to => $path->points->[0]); - } - } - } - # go to first point of extrusion path - $gcode .= $self->travel_to($path->points->[0], "move to first $description point"); + my $gcode = ""; + $gcode .= $self->travel_to($path->points->[0], $path->role, "move to first $description point"); # compensate retraction $gcode .= $self->unretract; @@ -260,17 +243,54 @@ sub extrude_path { sub travel_to { my $self = shift; - my ($point, $comment) = @_; + my ($point, $role, $comment) = @_; - return "" if points_coincide($self->last_pos, $point); $self->speed('travel'); my $gcode = ""; - if ($Slic3r::Config->avoid_crossing_perimeters && $self->last_pos->distance_to($point) > scale 5 && !$self->straight_once) { + + my $travel = Slic3r::Line->new($self->last_pos->clone, $point->clone); + + # move travel back to original layer coordinates for the island check. + # note that we're only considering the current object's islands, while we should + # build a more complete configuration space + $travel->translate(-$self->shift_x, -$self->shift_y); + + if ($travel->length < scale $self->extruder->retract_before_travel + || ($Slic3r::Config->only_retract_when_crossing_perimeters && first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) + || ($role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands_enclose_line($travel)) + ) { + $self->straight_once(0); + $gcode .= $self->G0($point, undef, 0, $comment || ""); + } elsif (!$Slic3r::Config->avoid_crossing_perimeters || $self->straight_once) { + $self->straight_once(0); + $gcode .= $self->retract(travel_to => $point); + $gcode .= $self->G0($point, undef, 0, $comment || ""); + } else { my $plan = sub { my $mp = shift; - return join '', - map $self->G0($_->[B], undef, 0, $comment || ""), - $mp->shortest_path($self->last_pos, $point)->lines; + + my $gcode = ""; + my @travel = $mp->shortest_path($self->last_pos, $point)->lines; + + # if the path is not contained in a single island we need to retract + my $need_retract = !$Slic3r::Config->only_retract_when_crossing_perimeters; + if (!$need_retract) { + $need_retract = 1; + foreach my $slice (@{$self->layer->slices}) { + # discard the island if at any line is not enclosed in it + next if first { !$slice->encloses_line($_, scaled_epsilon) } @travel; + # okay, this island encloses the full travel path + $need_retract = 0; + last; + } + } + + # do the retract (the travel_to argument is broken) + $gcode .= $self->retract(travel_to => $point) if $need_retract; + + # append the actual path and return + $gcode .= join '', map $self->G0($_->[B], undef, 0, $comment || ""), @travel; + return $gcode; }; if ($self->new_object) { @@ -288,9 +308,6 @@ sub travel_to { } else { $gcode .= $plan->($self->layer_mp); } - } else { - $self->straight_once(0); - $gcode .= $self->G0($point, undef, 0, $comment || ""); } return $gcode; From a9df56670f2ffa5b35d4d41fdb973570dc386006 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Mar 2013 19:33:06 +0100 Subject: [PATCH 108/114] Bugfix: internal-bridge regions were not touching perimeters --- lib/Slic3r/Print/Object.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e502fa83d5..08be023887 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -466,6 +466,7 @@ sub bridge_over_infill { ), @{diff_ex( [ map $_->p, @internal_solid ], [ map @$_, @$to_bridge ], + 1, )}; @{$layerm->fill_surfaces} = @new_surfaces; } From d928f005e6ee531421210842ea4a4483137ecc79 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 5 Mar 2013 20:03:46 +0100 Subject: [PATCH 109/114] Some incomplete work for cleaner infill logic --- lib/Slic3r/Fill.pm | 33 +++++---------------------------- lib/Slic3r/Layer/Region.pm | 5 ++++- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 27b0434f0f..025a1cc006 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -52,7 +52,9 @@ sub make_fill { Slic3r::debugf "Filling layer %d:\n", $layerm->id; - # merge overlapping surfaces + # merge adjacent surfaces + # in case of bridge surfaces, the ones with defined angle will be attached to the ones + # without any angle (shouldn't this logic be moved to process_bridges()?) my @surfaces = (); { my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces}; @@ -89,35 +91,10 @@ sub make_fill { } } - # add spacing between adjacent surfaces + # add spacing between surfaces { my $distance = $layerm->infill_flow->scaled_spacing / 2; - my @offsets = (); - foreach my $surface (@surfaces) { - my $expolygon = $surface->expolygon; - my $diff = diff_ex( - [ $expolygon->offset($distance) ], - $expolygon, - 1, - ); - push @offsets, map @$_, @$diff; - } - - my @new_surfaces = (); - foreach my $surface (@surfaces) { - my $diff = diff_ex( - $surface->expolygon, - [ @offsets ], - ); - - push @new_surfaces, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $surface->surface_type, - bridge_angle => $surface->bridge_angle, - depth_layers => $surface->depth_layers, - ), @$diff; - } - @surfaces = @new_surfaces; + @surfaces = map $_->offset(-$distance), @surfaces; } my @fills = (); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index ee30232777..80f5aaa965 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -256,10 +256,13 @@ sub make_perimeters { # create one more offset to be used as boundary for fill { + # we offset by half the perimeter spacing (to get to the actual infill boundary) + # and then we offset back and forth by the infill spacing to only consider the + # non-collapsing regions my @fill_boundaries = @{union_ex([ Slic3r::Geometry::Clipper::offset( [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))], - +0.5*$infill_spacing, + +$infill_spacing, ), ])}; $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; From 36d24ccb0be83ec56b63072ee57b10735416bbc3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 7 Mar 2013 15:47:32 +0100 Subject: [PATCH 110/114] Infill refactoring and cleanup complete --- lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/Layer/Region.pm | 228 +++++++++++++++---------------------- lib/Slic3r/Polygon.pm | 4 +- lib/Slic3r/Print.pm | 5 +- lib/Slic3r/Print/Object.pm | 25 ++-- lib/Slic3r/Surface.pm | 4 +- t/combineinfill.t | 2 +- 7 files changed, 108 insertions(+), 162 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 025a1cc006..6e9dcaf450 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -54,7 +54,7 @@ sub make_fill { # merge adjacent surfaces # in case of bridge surfaces, the ones with defined angle will be attached to the ones - # without any angle (shouldn't this logic be moved to process_bridges()?) + # without any angle (shouldn't this logic be moved to process_external_surfaces()?) my @surfaces = (); { my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces}; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 80f5aaa965..f98bfc9a75 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -2,7 +2,7 @@ package Slic3r::Layer::Region; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(PI scale chained_path_items); +use Slic3r::Geometry qw(PI scale chained_path_items points_coincide); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); use Slic3r::Surface ':types'; @@ -422,7 +422,7 @@ sub _add_perimeter { my $self = shift; my ($polygon, $role) = @_; - return unless $polygon->is_printable($self->perimeter_flow); + return unless $polygon->is_printable($self->perimeter_flow->scaled_width); push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack( polygon => $polygon, role => ($role // EXTR_ROLE_PERIMETER), @@ -433,6 +433,11 @@ sub _add_perimeter { sub prepare_fill_surfaces { my $self = shift; + # if hollow object is requested, remove internal surfaces + if ($Slic3r::Config->fill_density == 0) { + @{$self->fill_surfaces} = grep $_->surface_type != S_TYPE_INTERNAL, @{$self->fill_surfaces}; + } + # if no solid layers are requested, turn top/bottom surfaces to internal if ($Slic3r::Config->top_solid_layers == 0) { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @{$self->fill_surfaces}; @@ -441,7 +446,7 @@ sub prepare_fill_surfaces { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}; } - # turn too small internal regions into solid regions + # turn too small internal regions into solid regions according to the user setting { my $min_area = scale scale $Slic3r::Config->solid_infill_below_area; # scaling an area requires two calls! my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @{$self->fill_surfaces}; @@ -450,76 +455,99 @@ sub prepare_fill_surfaces { } } -# make bridges printable -sub process_bridges { +sub process_external_surfaces { my $self = shift; - # no bridges are possible if we have no internal surfaces - return if $Slic3r::Config->fill_density == 0; - - my @bridges = (); - - # a bottom surface on a layer > 0 is either a bridge or a overhang - # or a combination of both; any top surface is a candidate for - # reverse bridge processing - - my @solid_surfaces = grep { - ($_->surface_type == S_TYPE_BOTTOM && $self->id > 0) || $_->surface_type == S_TYPE_TOP - } @{$self->fill_surfaces} or return; - - my @internal_surfaces = grep $_->is_internal, @{$self->slices}; - - SURFACE: foreach my $surface (@solid_surfaces) { - my $expolygon = $surface->expolygon->safety_offset; - my $description = $surface->surface_type == S_TYPE_BOTTOM ? 'bridge/overhang' : 'reverse bridge'; + # enlarge top and bottom surfaces + { + # get all external surfaces + my @top = grep $_->surface_type == S_TYPE_TOP, @{$self->fill_surfaces}; + my @bottom = grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}; - # offset the contour and intersect it with the internal surfaces to discover - # which of them has contact with our bridge - my @supporting_surfaces = (); - my ($contour_offset) = $expolygon->contour->offset(scale $self->infill_flow->spacing * sqrt(2)); - foreach my $internal_surface (@internal_surfaces) { - my $intersection = intersection_ex([$contour_offset], [$internal_surface->p]); - if (@$intersection) { - push @supporting_surfaces, $internal_surface; - } + # offset them and intersect the results with the actual fill boundaries + my $margin = scale 3; # TODO: ensure this is greater than the total thickness of the perimeters + @top = @{intersection_ex( + [ Slic3r::Geometry::Clipper::offset([ map $_->p, @top ], +$margin) ], + [ map $_->p, @{$self->fill_surfaces} ], + undef, + 1, # to ensure adjacent expolygons are unified + )}; + @bottom = @{intersection_ex( + [ Slic3r::Geometry::Clipper::offset([ map $_->p, @bottom ], +$margin) ], + [ map $_->p, @{$self->fill_surfaces} ], + undef, + 1, # to ensure adjacent expolygons are unified + )}; + + # give priority to bottom surfaces + @top = @{diff_ex( + [ map @$_, @top ], + [ map @$_, @bottom ], + )}; + + # generate new surfaces + my @new_surfaces = (); + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => S_TYPE_TOP, + ), @top; + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => S_TYPE_BOTTOM, + ), @bottom; + + # subtract the new top surfaces from the other non-top surfaces and re-add them + my @other = grep $_->surface_type != S_TYPE_TOP && $_->surface_type != S_TYPE_BOTTOM, @{$self->fill_surfaces}; + foreach my $group (Slic3r::Surface->group(@other)) { + push @new_surfaces, map Slic3r::Surface->new( + expolygon => $_, + surface_type => $group->[0]->surface_type, + ), @{diff_ex( + [ map $_->p, @$group ], + [ map $_->p, @new_surfaces ], + )}; } + @{$self->fill_surfaces} = @new_surfaces; + } + + # detect bridge direction (skip bottom layer) + if ($self->id > 0) { + my @bottom = grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}; # surfaces + my @lower = @{$self->layer->object->layers->[ $self->id - 1 ]->slices}; # expolygons - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("bridge_surfaces.svg", - green_polygons => [ map $_->p, @supporting_surfaces ], - red_polygons => [ @$expolygon ], - ); - } - - Slic3r::debugf "Found $description on layer %d with %d support(s)\n", - $self->id, scalar(@supporting_surfaces); - - next SURFACE unless @supporting_surfaces; - - my $bridge_angle = undef; - if ($surface->surface_type == S_TYPE_BOTTOM) { - # detect optimal bridge angle - - my $bridge_over_hole = 0; - my @edges = (); # edges are POLYLINES - foreach my $supporting_surface (@supporting_surfaces) { - my @surface_edges = map $_->clip_with_polygon($contour_offset), - ($supporting_surface->contour, $supporting_surface->holes); - - if (@supporting_surfaces == 1 && @surface_edges == 1 - && @{$supporting_surface->contour} == @{$surface_edges[0]}) { - $bridge_over_hole = 1; + foreach my $surface (@bottom) { + # detect what edges lie on lower slices + my @edges = (); # polylines + foreach my $lower (@lower) { + # turn bridge contour and holes into polylines and then clip them + # with each lower slice's contour + my @clipped = map $_->split_at_first_point->clip_with_polygon($lower->contour), @{$surface->expolygon}; + if (@clipped == 2) { + # If the split_at_first_point() call above happens to split the polygon inside the clipping area + # we would get two consecutive polylines instead of a single one, so we use this ugly hack to + # recombine them back into a single one in order to trigger the @edges == 2 logic below. + # This needs to be replaced with something way better. + if (points_coincide($clipped[0][0], $clipped[-1][-1])) { + @clipped = (Slic3r::Polyline->new(@{$clipped[-1]}, @{$clipped[0]})); + } + if (points_coincide($clipped[-1][0], $clipped[0][-1])) { + @clipped = (Slic3r::Polyline->new(@{$clipped[0]}, @{$clipped[1]})); + } } - push @edges, grep { @$_ } @surface_edges; + push @edges, @clipped; } - Slic3r::debugf " Bridge is supported on %d edge(s)\n", scalar(@edges); - Slic3r::debugf " and covers a hole\n" if $bridge_over_hole; + + Slic3r::debugf "Found bridge on layer %d with %d support(s)\n", $self->id, scalar(@edges); + next if !@edges; + + my $bridge_angle = undef; if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output("bridge_edges.svg", - polylines => [ map $_->p, @edges ], + Slic3r::SVG::output("bridge.svg", + polygons => [ $surface->p ], + red_polygons => [ map @$_, @lower ], + polylines => [ @edges ], ); } @@ -553,80 +581,8 @@ sub process_bridges { Slic3r::debugf " Optimal infill angle of bridge on layer %d is %d degrees\n", $self->id, $bridge_angle if defined $bridge_angle; - } - - # now, extend our bridge by taking a portion of supporting surfaces - { - # offset the bridge by the specified amount of mm (minimum 3) - my $bridge_overlap = scale 3; - my ($bridge_offset) = $expolygon->contour->offset($bridge_overlap); - # calculate the new bridge - my $intersection = intersection_ex( - [ @$expolygon, map $_->p, @supporting_surfaces ], - [ $bridge_offset ], - ); - - push @bridges, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $surface->surface_type, - bridge_angle => $bridge_angle, - ), @$intersection; - } - } - - # now we need to merge bridges to avoid overlapping - { - # build a list of unique bridge types - my @surface_groups = Slic3r::Surface->group(@bridges); - - # merge bridges of the same type, removing any of the bridges already merged; - # the order of @surface_groups determines the priority between bridges having - # different surface_type or bridge_angle - @bridges = (); - foreach my $surfaces (@surface_groups) { - my $union = union_ex([ map $_->p, @$surfaces ]); - my $diff = diff_ex( - [ map @$_, @$union ], - [ map $_->p, @bridges ], - ); - - push @bridges, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $surfaces->[0]->surface_type, - bridge_angle => $surfaces->[0]->bridge_angle, - ), @$union; - } - } - - # apply bridges to layer - { - my @surfaces = @{$self->fill_surfaces}; - @{$self->fill_surfaces} = (); - - # intersect layer surfaces with bridges to get actual bridges - foreach my $bridge (@bridges) { - my $actual_bridge = intersection_ex( - [ map $_->p, @surfaces ], - [ $bridge->p ], - ); - - push @{$self->fill_surfaces}, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $bridge->surface_type, - bridge_angle => $bridge->bridge_angle, - ), @$actual_bridge; - } - - # difference between layer surfaces and bridges are the other surfaces - foreach my $group (Slic3r::Surface->group(@surfaces)) { - my $difference = diff_ex( - [ map $_->p, @$group ], - [ map $_->p, @bridges ], - ); - push @{$self->fill_surfaces}, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $group->[0]->surface_type), @$difference; + $surface->bridge_angle($bridge_angle); } } } diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 0f38273817..4af44acc99 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -118,7 +118,7 @@ sub subdivide { # returns false if the polyline is too tight to be printed sub is_printable { my $self = shift; - my ($flow) = @_; + my ($width) = @_; # try to get an inwards offset # for a distance equal to half of the extrusion width; @@ -129,7 +129,7 @@ sub is_printable { # detect them and we would be discarding them. my $p = $self->clone; $p->make_counter_clockwise; - return $p->offset(-$flow->scaled_width / 2) ? 1 : 0; + return $p->offset(-$width / 2) ? 1 : 0; } sub is_valid { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1aad0ae390..56b868335d 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -337,7 +337,8 @@ sub export_gcode { for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); } - # this will transform $layer->fill_surfaces from expolygon + # this will assign a type (top/bottom/internal) to $layerm->slices + # and transform $layerm->fill_surfaces from expolygon # to typed top/bottom/internal surfaces; $status_cb->(30, "Detecting solid surfaces"); $_->detect_surfaces_type for @{$self->objects}; @@ -349,7 +350,7 @@ sub export_gcode { # this will detect bridges and reverse bridges # and rearrange top/bottom/internal surfaces $status_cb->(45, "Detect bridges"); - $_->process_bridges for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; + $_->process_external_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; # detect which fill surfaces are near external layers # they will be split in internal and internal-solid surfaces diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 08be023887..c77fd228f1 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -300,7 +300,7 @@ sub detect_surfaces_type { [ map @$_, @$clip_surfaces ], 1, ); - return grep $_->contour->is_printable($layerm->perimeter_flow), + return grep $_->contour->is_printable($layerm->perimeter_flow->scaled_width), map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @$expolygons; }; @@ -515,7 +515,9 @@ sub discover_horizontal_shells { foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) { # find slices of current type for current layer - my @surfaces = grep $_->surface_type == $type, @{$layerm->slices} or next; + # get both slices and fill_surfaces before the former contains the perimeters area + # and the latter contains the enlarged external surfaces + my @surfaces = grep $_->surface_type == $type, @{$layerm->slices}, @{$layerm->fill_surfaces} or next; my $surfaces_p = [ map $_->p, @surfaces ]; Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", $i, scalar(@surfaces), ($type == S_TYPE_TOP ? 'top' : 'bottom'); @@ -548,7 +550,7 @@ sub discover_horizontal_shells { ( map @$_, @$new_internal_solid ), ]); - # subtract intersections from layer surfaces to get resulting inner surfaces + # subtract intersections from layer surfaces to get resulting internal surfaces my $internal = diff_ex( [ map $_->p, grep $_->surface_type == S_TYPE_INTERNAL, @neighbor_fill_surfaces ], [ map @$_, @$internal_solid ], @@ -557,10 +559,7 @@ sub discover_horizontal_shells { Slic3r::debugf " %d internal-solid and %d internal surfaces found\n", scalar(@$internal_solid), scalar(@$internal); - # Note: due to floating point math we're going to get some very small - # polygons as $internal; they will be removed by removed_small_features() - - # assign resulting inner surfaces to layer + # assign resulting internal surfaces to layer my $neighbor_fill_surfaces = $self->layers->[$n]->regions->[$region_id]->fill_surfaces; @$neighbor_fill_surfaces = (); push @$neighbor_fill_surfaces, Slic3r::Surface->new @@ -586,17 +585,7 @@ sub discover_horizontal_shells { } } - my $area_threshold = $layerm->infill_area_threshold; - @{$layerm->fill_surfaces} = grep $_->expolygon->area > $area_threshold, @{$layerm->fill_surfaces}; - } - - for (my $i = 0; $i < $self->layer_count; $i++) { - my $layerm = $self->layers->[$i]->regions->[$region_id]; - - # if hollow object is requested, remove internal surfaces - if ($Slic3r::Config->fill_density == 0) { - @{$layerm->fill_surfaces} = grep $_->surface_type != S_TYPE_INTERNAL, @{$layerm->fill_surfaces}; - } + @{$layerm->fill_surfaces} = grep $_->expolygon->area > $layerm->infill_area_threshold, @{$layerm->fill_surfaces}; } } } diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index a3cbe4b085..d5ec9838e6 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -35,8 +35,8 @@ sub new { sub expolygon { $_[0][S_EXPOLYGON] } sub surface_type { $_[0][S_SURFACE_TYPE] = $_[1] if defined $_[1]; $_[0][S_SURFACE_TYPE] } sub depth_layers { $_[0][S_DEPTH_LAYERS] } # this integer represents the thickness of the surface expressed in layers -sub bridge_angle { $_[0][S_BRIDGE_ANGLE] } -sub additional_inner_perimeters { $_[0][S_ADDITIONAL_INNER_PERIMETERS] = $_[1] if $_[1]; $_[0][S_ADDITIONAL_INNER_PERIMETERS] } +sub bridge_angle { $_[0][S_BRIDGE_ANGLE] = $_[1] if defined $_[1]; $_[0][S_BRIDGE_ANGLE] } +sub additional_inner_perimeters { $_[0][S_ADDITIONAL_INNER_PERIMETERS] = $_[1] if defined $_[1]; $_[0][S_ADDITIONAL_INNER_PERIMETERS] } # delegate handles sub encloses_point { $_[0]->expolygon->encloses_point } diff --git a/t/combineinfill.t b/t/combineinfill.t index aa4ed91a11..e2e63f4a05 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -48,7 +48,7 @@ use Slic3r::Test; } $_->detect_surfaces_type for @{$self->objects}; $_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; - $_->process_bridges for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; + $_->process_external_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; $_->discover_horizontal_shells for @{$self->objects}; $_->combine_infill for @{$self->objects}; From 97e864699eb6121f1358ca6ba3d5589059ac9f2e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 7 Mar 2013 16:00:58 +0100 Subject: [PATCH 111/114] New top-infill-extrusion-width option --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/Fill.pm | 4 +++- lib/Slic3r/GUI/Tab.pm | 2 +- lib/Slic3r/Layer/Region.pm | 4 ++++ lib/Slic3r/Print.pm | 5 +++-- slic3r.pl | 2 ++ 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index e6ec81eba8..4b54f8b393 100644 --- a/README.markdown +++ b/README.markdown @@ -289,6 +289,8 @@ The author of the Silk icon set is Mark James. Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill + --top-infill-extrusion-width + Set a different extrusion width for top infill --support-material-extrusion-width Set a different extrusion width for support material --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 4ea040401b..01891fd70c 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -422,6 +422,14 @@ our $Options = { type => 'f', default => 0, }, + 'top_infill_extrusion_width' => { + label => 'Top infill', + tooltip => 'Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) if will be computed over layer height.', + sidetext => 'mm or % (leave 0 for default)', + cli => 'top-infill-extrusion-width=s', + type => 'f', + default => 0, + }, 'support_material_extrusion_width' => { label => 'Support material', tooltip => 'Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) if will be computed over layer height.', diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 6e9dcaf450..15806e2138 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -102,7 +102,9 @@ sub make_fill { SURFACE: foreach my $surface (@surfaces) { my $filler = $Slic3r::Config->fill_pattern; my $density = $Slic3r::Config->fill_density; - my $flow_spacing = $layerm->infill_flow->spacing; + my $flow_spacing = ($surface->surface_type == S_TYPE_TOP) + ? $layerm->top_infill_flow->spacing + : $layerm->infill_flow->spacing; my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index d970da1bde..e78b1a02c5 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -510,7 +510,7 @@ sub build { { title => 'Extrusion width', label_width => 180, - options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width support_material_extrusion_width)], + options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width)], }, { title => 'Flow', diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index f98bfc9a75..77f7784348 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -16,6 +16,7 @@ has 'layer' => ( has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'perimeter_flow' => (is => 'rw'); has 'infill_flow' => (is => 'rw'); +has 'top_infill_flow' => (is => 'rw'); has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); @@ -62,9 +63,12 @@ sub _update_flows { ($self->region->first_layer_flows->{perimeter} || $self->region->flows->{perimeter}); $self->infill_flow ($self->region->first_layer_flows->{infill} || $self->region->flows->{infill}); + $self->top_infill_flow + ($self->region->first_layer_flows->{top_infill} || $self->region->flows->{top_infill}); } else { $self->perimeter_flow($self->region->flows->{perimeter}); $self->infill_flow($self->region->flows->{infill}); + $self->top_infill_flow($self->region->flows->{top_infill}); } } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 56b868335d..c32c97aa2b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -200,10 +200,11 @@ sub init_extruders { my $region = $self->regions->[$region_id]; # per-role extruders and flows - for (qw(perimeter infill)) { + for (qw(perimeter infill top_infill)) { + my $extruder_name = $_ eq 'top_infill' ? 'infill' : $_; $region->extruders->{$_} = ($self->regions_count > 1) ? $self->extruders->[$extruder_mapping{$region_id}] - : $self->extruders->[$self->config->get("${_}_extruder")-1]; + : $self->extruders->[$self->config->get("${extruder_name}_extruder")-1]; $region->flows->{$_} = $region->extruders->{$_}->make_flow( width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width, ); diff --git a/slic3r.pl b/slic3r.pl index 741da1de8b..cd7f714fde 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -337,6 +337,8 @@ $j Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill + --top-infill-extrusion-width + Set a different extrusion width for top infill --support-material-extrusion-width Set a different extrusion width for support material --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $config->{bridge_flow_ratio}) From 2f192bddca2bbe40f0b7bece3351fe42ec7b614e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 7 Mar 2013 16:24:25 +0100 Subject: [PATCH 112/114] Make tests happy --- lib/Slic3r/Fill.pm | 9 ++++++++- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/Layer/Region.pm | 5 ----- t/retraction.t | 1 + t/shells.t | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 15806e2138..6150eba842 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -52,10 +52,17 @@ sub make_fill { Slic3r::debugf "Filling layer %d:\n", $layerm->id; + my @surfaces = (); + + # if hollow object is requested, remove internal surfaces + # (this needs to be done after internal-solid shells are created) + if ($Slic3r::Config->fill_density == 0) { + @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; + } + # merge adjacent surfaces # in case of bridge surfaces, the ones with defined angle will be attached to the ones # without any angle (shouldn't this logic be moved to process_external_surfaces()?) - my @surfaces = (); { my @surfaces_with_bridge_angle = grep defined $_->bridge_angle, @{$layerm->fill_surfaces}; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index a27508be2c..6706854782 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -40,7 +40,7 @@ has 'speeds' => ( default => sub {+{ map { $_ => 60 * $Slic3r::Config->get_value("${_}_speed") } qw(travel perimeter small_perimeter external_perimeter infill - solid_infill top_solid_infill support_material bridge gap_fill), + solid_infill top_solid_infill support_material bridge gap_fill retract), }}, ); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 77f7784348..9739128e2f 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -437,11 +437,6 @@ sub _add_perimeter { sub prepare_fill_surfaces { my $self = shift; - # if hollow object is requested, remove internal surfaces - if ($Slic3r::Config->fill_density == 0) { - @{$self->fill_surfaces} = grep $_->surface_type != S_TYPE_INTERNAL, @{$self->fill_surfaces}; - } - # if no solid layers are requested, turn top/bottom surfaces to internal if ($Slic3r::Config->top_solid_layers == 0) { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @{$self->fill_surfaces}; diff --git a/t/retraction.t b/t/retraction.t index 3d10876567..9918546b25 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -89,6 +89,7 @@ my $test = sub { $config->set('retract_length', [1.5]); $config->set('retract_before_travel', [3]); +$config->set('only_retract_when_crossing_perimeters', 0); my $retract_tests = sub { my ($descr) = @_; diff --git a/t/shells.t b/t/shells.t index ae5b7670ab..a631ff9ca4 100644 --- a/t/shells.t +++ b/t/shells.t @@ -40,8 +40,8 @@ use Slic3r::Test; }; ok $test->(), "proper number of shells is applied"; - $config->set('fill_density', 0); + ok $test->(), "proper number of shells is applied even when fill density is none"; } From f494335f77a091a0ab5dad6c103860670d02edf2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 8 Mar 2013 13:50:50 +0100 Subject: [PATCH 113/114] Bugfix: not all the calls to travel_to() were updated --- lib/Slic3r/GCode.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 6706854782..cb1613d3bb 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -164,7 +164,7 @@ sub extrude_loop { $point->rotate($angle, $extrusion_path->polyline->[0]); # generate the travel move - $gcode .= $self->travel_to($point, "move inwards before travel"); + $gcode .= $self->travel_to($point, $loop->role, "move inwards before travel"); } return $gcode; From 4fc435f0fdb4ab47fc9d544dbe97e56f11491bad Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 8 Mar 2013 17:52:33 +0100 Subject: [PATCH 114/114] Bugfix: retract speed was applied to travel --- lib/Slic3r/GCode.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index cb1613d3bb..4c40ee329a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -245,7 +245,6 @@ sub travel_to { my $self = shift; my ($point, $role, $comment) = @_; - $self->speed('travel'); my $gcode = ""; my $travel = Slic3r::Line->new($self->last_pos->clone, $point->clone); @@ -260,10 +259,12 @@ sub travel_to { || ($role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands_enclose_line($travel)) ) { $self->straight_once(0); + $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, $comment || ""); } elsif (!$Slic3r::Config->avoid_crossing_perimeters || $self->straight_once) { $self->straight_once(0); $gcode .= $self->retract(travel_to => $point); + $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, $comment || ""); } else { my $plan = sub { @@ -289,6 +290,7 @@ sub travel_to { $gcode .= $self->retract(travel_to => $point) if $need_retract; # append the actual path and return + $self->speed('travel'); $gcode .= join '', map $self->G0($_->[B], undef, 0, $comment || ""), @travel; return $gcode; };