From 0aa224ffadd948af69506f511dfc3545e852384e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 25 Aug 2012 14:23:46 +0200 Subject: [PATCH 001/172] Dynamic extrusion width for better gap filling --- MANIFEST | 1 + lib/Slic3r/ExPolygon.pm | 7 ++++ lib/Slic3r/Fill.pm | 14 ++------ lib/Slic3r/Flow.pm | 10 ++++++ lib/Slic3r/Layer.pm | 46 +++++++++++++++++++----- t/dynamic.t | 79 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 t/dynamic.t diff --git a/MANIFEST b/MANIFEST index 19de72498..64d93f368 100644 --- a/MANIFEST +++ b/MANIFEST @@ -51,6 +51,7 @@ t/arcs.t t/clean_polylines.t t/clipper.t t/collinear.t +t/dynamic.t t/fill.t t/geometry.t t/polyclip.t diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 5f9d23577..ed572d694 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -91,6 +91,13 @@ sub offset_ex { return @{ union_ex(\@offsets) }; } +sub noncollapsing_offset_ex { + my $self = shift; + my ($distance, @params) = @_; + + return $self->offset_ex($distance + 1, @params); +} + sub encloses_point { my $self = shift; my ($point) = @_; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index e30eec65f..8068bb87e 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -178,18 +178,8 @@ sub make_fill { } # add thin fill regions - { - my %args = ( - role => EXTR_ROLE_SOLIDFILL, - flow_spacing => $layer->perimeter_flow->spacing, - ); - push @fills, map { - $_->isa('Slic3r::Polygon') - ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon => $_, %args)->split_at_first_point) - : Slic3r::ExtrusionPath->pack(polyline => $_, %args), - } @{$layer->thin_fills}; - } - push @fills_ordering_points, map $_->[0], @{$layer->thin_fills}; + 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 = @{shortest_path([ diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 9c01dd30c..5e8f23a2b 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -55,4 +55,14 @@ sub _build_spacing { return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing); } +sub clone { + my $self = shift; + + return (ref $self)->new( + nozzle_diameter => $self->nozzle_diameter, + layer_height => $self->layer_height, + @_, + ); +} + 1; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index fc33cf420..9794e0ed9 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -225,12 +225,14 @@ sub make_perimeters { # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); foreach my $expolygon (@last_offsets) { - my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->offset_ex(-1.5*$distance); + my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->noncollapsing_offset_ex(-1.5*$distance); push @new_offsets, @offsets; + # 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(-$distance) ], - [ map @$_, @offsets ], + [ map @$_, $expolygon->offset_ex(-0.5*$distance) ], + [ map @$_, map $_->offset_ex(+0.5*$distance), @offsets ], # should these be offsetted in a single pass? ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } @@ -245,14 +247,40 @@ sub make_perimeters { my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets; $_->simplify(scale &Slic3r::RESOLUTION) for @fill_boundaries; push @{ $self->surfaces }, @fill_boundaries; - + } + + # fill gaps using dynamic extrusion width + { # detect the small gaps that we need to treat like thin polygons, # thus generating the skeleton and using it to fill them - push @{ $self->thin_fills }, - map $_->medial_axis(scale $self->perimeter_flow->width), - @gaps; - Slic3r::debugf " %d gaps filled\n", scalar @{ $self->thin_fills } - if @{ $self->thin_fills }; + my $w = $self->perimeter_flow->width; + my @widths = (1.5 * $w, $w, 0.5 * $w, 0.2 * $w); + foreach my $width (@widths) { + my $scaled_width = scale $width; + + # extract the gaps having this width + my @this_width = map $_->offset_ex(+0.5*$scaled_width), map $_->noncollapsing_offset_ex(-0.5*$scaled_width), @gaps; + + # fill them + my %path_args = ( + role => EXTR_ROLE_SOLIDFILL, + flow_spacing => $self->perimeter_flow->clone(width => $width)->spacing, + ); + push @{ $self->thin_fills }, map { + $_->isa('Slic3r::Polygon') + ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point) # we should keep these as loops + : Slic3r::ExtrusionPath->pack(polyline => $_, %path_args), + } map $_->medial_axis($scaled_width), @this_width; + + Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @this_width, $width + if @{ $self->thin_fills }; + + # check what's left + @gaps = @{diff_ex( + [ map @$_, @gaps ], + [ map @$_, @this_width ], + )}; + } } } diff --git a/t/dynamic.t b/t/dynamic.t new file mode 100644 index 000000000..0b5dc490f --- /dev/null +++ b/t/dynamic.t @@ -0,0 +1,79 @@ +use Test::More; +use strict; +use warnings; + +plan tests => 20; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use List::Util qw(first); +use Slic3r; +use Slic3r::Geometry qw(X Y scale epsilon); +use Slic3r::Surface ':types'; + +sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } + +{ + my $square = Slic3r::ExPolygon->new([ + scale_points [0,0], [10,0], [10,10], [0,10], + ]); + + my @offsets = $square->noncollapsing_offset_ex(- scale 5); + is scalar @offsets, 1, 'non-collapsing offset'; +} + +{ + my $w = 0.7; + local $Slic3r::perimeter_flow = Slic3r::Flow->new( + nozzle_diameter => 0.5, + layer_height => 0.4, + width => $w, + ); + local $Slic3r::Config = Slic3r::Config->new( + perimeters => 3, + ); + + my $make_layer = sub { + my ($width) = @_; + my $layer = Slic3r::Layer->new( + id => 1, + slices => [ + Slic3r::Surface->new( + surface_type => S_TYPE_INTERNAL, + expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,$width], [0,$width] ]), + ), + ], + thin_walls => [], + ); + $layer->make_perimeters; + return $layer; + }; + + my %widths = ( + 1 * $w => { perimeters => 1, gaps => 0 }, + 1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.2 * $w)->spacing }, + 1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.5 * $w)->spacing }, + 2 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing }, + 2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 1.5 * $w)->spacing }, + 3 * $w => { perimeters => 2, gaps => 0 }, + 4 * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing }, + ); + + foreach my $width (sort keys %widths) { + my $layer = $make_layer->($width); + is scalar @{$layer->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters'; + is scalar @{$layer->thin_fills} ? 1 : 0, $widths{$width}{gaps}, + ($widths{$width}{gaps} ? 'gaps were filled' : 'no gaps detected'); # TODO: we should check the exact number of gaps, but we need a better medial axis algorithm + + my @gaps = map $_->unpack, @{$layer->thin_fills}; + if (@gaps) { + ok +(!first { abs($_->flow_spacing - $widths{$width}{gap_flow_spacing}) > epsilon } @gaps), + 'flow spacing was dynamically adjusted'; + } + } +} + +__END__ From 1445820673b8235e46359a1b682280a58f29710f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 12:04:44 +0200 Subject: [PATCH 002/172] External perimeter first on bottom layer when brim is enabled. #761 --- lib/Slic3r/Layer/Region.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 5c34496e5..31c785e99 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -276,6 +276,12 @@ sub make_perimeters { } } + # if brim will be printed, reverse the order of perimeters so that + # we continue inwards after having finished the brim + if ($self->layer->id == 0 && $Slic3r::Config->brim_width > 0) { + @{$self->perimeters} = reverse @{$self->perimeters}; + } + # add thin walls as perimeters { my @thin_paths = (); From 6beaf5e5979ed592fc751c93a646393430e73c52 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 16:17:09 +0200 Subject: [PATCH 003/172] New faster algorithm for filling gaps, while we work on a new medial axis implementation --- lib/Slic3r/Fill/Rectilinear.pm | 2 +- lib/Slic3r/Layer/Region.pm | 67 ++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index d13321015..223b8ca4b 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -58,7 +58,7 @@ sub fill_surface { } # connect lines - { + unless ($params{dont_connect}) { my $collection = Slic3r::ExtrusionPath::Collection->new( paths => [ map Slic3r::ExtrusionPath->new(polyline => Slic3r::Polyline->new(@$_), role => -1), @paths ], ); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index e6cbff4c4..a7ca7c157 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -211,31 +211,60 @@ sub make_perimeters { push @{ $self->surfaces }, @fill_boundaries; } - # fill gaps using dynamic extrusion width + # fill gaps { - # detect the small gaps that we need to treat like thin polygons, - # thus generating the skeleton and using it to fill them + my $filler = Slic3r::Fill->new(print => $self->layer->object->print)->filler('rectilinear'); + my $w = $self->perimeter_flow->width; - my @widths = (1.5 * $w, $w, 0.5 * $w, 0.2 * $w); + my @widths = (1.5 * $w, $w, 0.5 * $w); # worth trying 0.2 too? foreach my $width (@widths) { - my $scaled_width = scale $width; + my $flow = $self->perimeter_flow->clone(width => $width); # extract the gaps having this width - my @this_width = map $_->offset_ex(+0.5*$scaled_width), map $_->noncollapsing_offset_ex(-0.5*$scaled_width), @gaps; + my @this_width = map $_->offset_ex(+0.5*$flow->scaled_width), + map $_->noncollapsing_offset_ex(-0.5*$flow->scaled_width), + @gaps; - # fill them - my %path_args = ( - role => EXTR_ROLE_SOLIDFILL, - flow_spacing => $self->perimeter_flow->clone(width => $width)->spacing, - ); - push @{ $self->thin_fills }, map { - $_->isa('Slic3r::Polygon') - ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point) # we should keep these as loops - : Slic3r::ExtrusionPath->pack(polyline => $_, %path_args), - } map $_->medial_axis($scaled_width), @this_width; - - Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @this_width, $width - if @{ $self->thin_fills }; + if (0) { + # fill gaps using dynamic extrusion width, by treating them like thin polygons, + # thus generating the skeleton and using it to fill them + my %path_args = ( + role => EXTR_ROLE_SOLIDFILL, + flow_spacing => $flow->spacing, + ); + push @{ $self->thin_fills }, map { + $_->isa('Slic3r::Polygon') + ? (map $_->pack, Slic3r::ExtrusionLoop->new(polygon => $_, %path_args)->split_at_first_point) # we should keep these as loops + : Slic3r::ExtrusionPath->pack(polyline => $_, %path_args), + } map $_->medial_axis($flow->scaled_width), @this_width; + + Slic3r::debugf " %d gaps filled with extrusion width = %s\n", scalar @this_width, $width + if @{ $self->thin_fills }; + + } else { + # fill gaps using zigzag infill + + # since this is infill, we have to offset by half-extrusion width inwards + my @infill = map $_->offset_ex(-0.5*$flow->scaled_width), @this_width; + + foreach my $expolygon (@infill) { + my @paths = $filler->fill_surface( + Slic3r::Surface->new(expolygon => $expolygon), + density => 1, + flow_spacing => $flow->spacing, + dont_connect => 1, # time-saver + ); + my $params = shift @paths; + + push @{ $self->thin_fills }, + map Slic3r::ExtrusionPath->pack( + polyline => Slic3r::Polyline->new(@$_), + role => EXTR_ROLE_SOLIDFILL, + depth_layers => 1, + flow_spacing => $params->{flow_spacing}, + ), @paths; + } + } # check what's left @gaps = @{diff_ex( From f0d2b0e5eae7742d2f8a670845e5403d678b3dda Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 16:43:41 +0200 Subject: [PATCH 004/172] Avoid overlapping regions (thanks Mike Sheldrake!). #726 --- lib/Slic3r/Layer/Region.pm | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 909288dd7..a23a85b6e 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -89,8 +89,12 @@ sub make_surfaces { foreach my $surface (@surfaces) { push @{$self->slices}, map Slic3r::Surface->new (expolygon => $_, surface_type => S_TYPE_INTERNAL), - map $_->offset_ex(+$distance), - $surface->expolygon->offset_ex(-2*$distance); + @{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 @@ -187,14 +191,19 @@ sub make_perimeters { # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); foreach my $expolygon (@last_offsets) { - my @offsets = map $_->offset_ex(+0.5*$distance), $expolygon->noncollapsing_offset_ex(-1.5*$distance); + my @offsets = @{union_ex([ + Slic3r::Geometry::Clipper::offset( + [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$distance)], + +0.5*$distance, + ), + ])}; push @new_offsets, @offsets; # 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 @$_, map $_->offset_ex(+0.5*$distance), @offsets ], # should these be offsetted in a single pass? + [ Slic3r::Geometry::Clipper::offset([map @$_, @offsets], +0.5*$distance) ], ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } From 44016b38c52bf358ca0e8332f4ee04c043774271 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 19:13:40 +0200 Subject: [PATCH 005/172] Distribute skirt loops across all extruders --- lib/Slic3r/Print.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 3669187b2..d7bbf75d9 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -711,7 +711,11 @@ sub write_gcode { $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); # skip skirt if we have a large brim if ($layer_id < $Slic3r::Config->skirt_height) { - $gcode .= $gcodegen->extrude_loop($_, 'skirt') for @{$self->skirt}; + # distribute skirt loops across all extruders + for my $i (0 .. $#{$self->skirt}) { + $gcode .= $gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]); + $gcode .= $gcodegen->extrude_loop($self->skirt->[$i], 'skirt'); + } } $skirt_done++; } From 355031fe2f8916c8ef1341a8ede6adebf5a8e9ee Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 19:38:18 +0200 Subject: [PATCH 006/172] Remove unnecessary retractions --- lib/Slic3r/Print.pm | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d7bbf75d9..fcbf1ba16 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -745,18 +745,22 @@ sub write_gcode { my $region = $self->regions->[$region_id]; # extrude perimeters - $gcode .= $gcodegen->set_extruder($region->extruders->{perimeter}); - $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters }; + if (@{ $layerm->perimeters }) { + $gcode .= $gcodegen->set_extruder($region->extruders->{perimeter}); + $gcode .= $gcodegen->extrude($_, 'perimeter') for @{ $layerm->perimeters }; + } # extrude fills - $gcode .= $gcodegen->set_extruder($region->extruders->{infill}); - $gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration); - for my $fill (@{ $layerm->fills }) { - if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { - $gcode .= $gcodegen->extrude($_, 'fill') - for $fill->shortest_path($gcodegen->last_pos); - } else { - $gcode .= $gcodegen->extrude($fill, 'fill') ; + if (@{ $layerm->fills }) { + $gcode .= $gcodegen->set_extruder($region->extruders->{infill}); + $gcode .= $gcodegen->set_acceleration($Slic3r::Config->infill_acceleration); + for my $fill (@{ $layerm->fills }) { + if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { + $gcode .= $gcodegen->extrude($_, 'fill') + for $fill->shortest_path($gcodegen->last_pos); + } else { + $gcode .= $gcodegen->extrude($fill, 'fill') ; + } } } } From 3c8ac435ad58f852f02f108d80bf50c407ab5830 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 20:24:11 +0200 Subject: [PATCH 007/172] Remember last used filament presets when using multiple extruders too --- lib/Slic3r/GUI.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 8bfd4d1fc..737204926 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -234,7 +234,7 @@ sub notify { } sub save_settings { - my $self = shift; + my $class = shift; Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings); } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 6d1f093c3..afd66be6d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -4,7 +4,7 @@ use warnings; use utf8; use File::Basename qw(basename dirname); -use List::Util qw(max sum); +use List::Util qw(max sum first); use Math::ConvexHull::MonotoneChain qw(convex_hull); use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX); use Slic3r::Geometry::Clipper qw(JT_ROUND); @@ -222,11 +222,7 @@ sub new { my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], []); $self->{preset_choosers}{$group} = [$choice]; - EVT_CHOICE($choice, $choice, sub { - my $choice = shift; # avoid leaks - return if $group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1; #/ - $self->skeinpanel->{options_tabs}{$group}->select_preset($choice->GetSelection); - }); + EVT_CHOICE($choice, $choice, sub { $self->on_select_preset($group, @_) }); $self->{preset_choosers_sizers}{$group} = Wx::BoxSizer->new(wxVERTICAL); $self->{preset_choosers_sizers}{$group}->Add($choice, 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING); @@ -246,6 +242,21 @@ sub new { return $self; } +sub on_select_preset { + my $self = shift; + my ($group, $choice) = @_; + + if ($group eq 'filament' && @{$self->{preset_choosers}{filament}} > 1) { + my @filament_presets = $self->filament_presets; + $Slic3r::GUI::Settings->{presets}{filament} = $choice->GetString($filament_presets[0]) . ".ini"; + $Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_]) + for 1 .. $#filament_presets; + Slic3r::GUI->save_settings; + return; + } + $self->skeinpanel->{options_tabs}{$group}->select_preset($choice->GetSelection); +} + sub skeinpanel { my $self = shift; return $self->GetParent->GetParent; @@ -754,8 +765,12 @@ sub on_config_change { if ($opt_key eq 'extruders_count' && defined $value) { my $choices = $self->{preset_choosers}{filament}; while (@$choices < $value) { - push @$choices, Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [$choices->[0]->GetStrings]); + my @presets = $choices->[0]->GetStrings; + push @$choices, Wx::Choice->new($self, -1, wxDefaultPosition, [150, -1], [@presets]); $self->{preset_choosers_sizers}{filament}->Add($choices->[-1], 0, wxEXPAND | wxBOTTOM, FILAMENT_CHOOSERS_SPACING); + EVT_CHOICE($choices->[-1], $choices->[-1], sub { $self->on_select_preset('filament', @_) }); + my $i = first { $choices->[-1]->GetString($_) eq ($Slic3r::GUI::Settings->{presets}{"filament_" . $#$choices} || '') } 0 .. $#presets; + $choices->[-1]->SetSelection($i || 0); } while (@$choices > $value) { $self->{preset_choosers_sizers}{filament}->Remove(-1); From 304676b6aafd9ec22d73db23633dc8f120c49967 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 20:36:32 +0200 Subject: [PATCH 008/172] Use XML::SAX::ExpatXS if available --- Build.PL | 2 +- lib/Slic3r/Format/AMF.pm | 11 +++++++---- lib/Slic3r/Format/AMF/Parser.pm | 2 -- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Build.PL b/Build.PL index 4ad56e18e..e34a56d0f 100644 --- a/Build.PL +++ b/Build.PL @@ -26,7 +26,7 @@ my $build = Module::Build->new( recommends => { 'Growl::GNTP' => '0.15', 'Net::DBus' => '0', - 'XML::SAX' => '0', + 'XML::SAX::ExpatXS' => '0', 'Wx' => '0.9901', }, script_files => ['slic3r.pl'], diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index d654ca1a5..0e44353cf 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -7,14 +7,17 @@ sub read_file { my $self = shift; my ($file) = @_; - eval "require Slic3r::Format::AMF::Parser; 1" - or die "AMF parsing requires XML::SAX\n"; + eval qq{ + require Slic3r::Format::AMF::Parser; + use XML::SAX::ParserFactory; + 1; + } or die "AMF parsing requires XML::SAX\n"; open my $fh, '<', $file or die "Failed to open $file\n"; my $model = Slic3r::Model->new; - XML::SAX::PurePerl - ->new(Handler => Slic3r::Format::AMF::Parser->new(_model => $model)) + XML::SAX::ParserFactory + ->parser(Handler => Slic3r::Format::AMF::Parser->new(_model => $model)) ->parse_file($fh); close $fh; diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index 173dfc091..22e4204b3 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -2,8 +2,6 @@ package Slic3r::Format::AMF::Parser; use strict; use warnings; -use XML::SAX::PurePerl; - use base 'XML::SAX::Base'; my %xyz_index = (x => 0, y => 1, z => 2); #= From 392af5542b2082fb19e397197ce61016876e5c12 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 22:44:08 +0200 Subject: [PATCH 009/172] Show object information when double-clicking it in the plater --- lib/Slic3r/GUI/Plater.pm | 97 ++++++++++++++++++++++++++++++++++++-- lib/Slic3r/Model.pm | 7 +++ lib/Slic3r/TriangleMesh.pm | 2 + 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index afd66be6d..c881ceb74 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -10,7 +10,7 @@ use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX); use Slic3r::Geometry::Clipper qw(JT_ROUND); use threads::shared qw(shared_clone); use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); -use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE); +use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE); use base 'Wx::Panel'; use constant TB_MORE => &Wx::NewId; @@ -87,6 +87,7 @@ sub new { $self->{list}->InsertColumn(2, "Scale", wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER); EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected); EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected); + EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated); EVT_KEY_DOWN($self->{list}, sub { my ($list, $event) = @_; if ($event->GetKeyCode == WXK_TAB) { @@ -319,6 +320,7 @@ sub load_file { : [0,0], ], ); + $object->check_manifoldness; # we only consider the rotation of the first instance for now $object->set_rotation($model->objects->[$i]->instances->[0]->rotation) @@ -908,6 +910,9 @@ sub mouse_event { $self->{drag_start_pos} = undef; $self->{drag_object} = undef; $self->SetCursor(wxSTANDARD_CURSOR); + } elsif ($event->ButtonDClick) { + $parent->list_item_activated(undef, $parent->{selected_objects}->[0][0]) + if @{$parent->{selected_objects}}; } elsif ($event->Dragging) { return if !$self->{drag_start_pos}; # concurrency problems for my $preview ($self->{drag_object}) { @@ -948,6 +953,16 @@ sub list_item_selected { $self->selection_changed(1); } +sub list_item_activated { + my ($self, $event, $obj_idx) = @_; + + $obj_idx //= $event->GetIndex; + my $dlg = Slic3r::GUI::Plater::ObjectInfoDialog->new($self, + object => $self->{objects}[$obj_idx], + ); + $dlg->ShowModal; +} + sub object_list_changed { my $self = shift; @@ -1028,7 +1043,7 @@ package Slic3r::GUI::Plater::Object; use Moo; use Math::ConvexHull::MonotoneChain qw(convex_hull); -use Slic3r::Geometry qw(X Y); +use Slic3r::Geometry qw(X Y Z); has 'name' => (is => 'rw', required => 1); has 'input_file' => (is => 'rw', required => 1); @@ -1040,9 +1055,28 @@ has 'rotate' => (is => 'rw', default => sub { 0 }); has 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis has 'thumbnail' => (is => 'rw'); +# statistics +has 'facets' => (is => 'rw'); +has 'vertices' => (is => 'rw'); +has 'materials' => (is => 'rw'); +has 'is_manifold' => (is => 'rw'); + sub _trigger_model_object { my $self = shift; - $self->size([$self->model_object->mesh->size]) if $self->model_object; + if ($self->model_object) { + my $mesh = $self->model_object->mesh; + $self->size([$mesh->size]); + $self->facets(scalar @{$mesh->facets}); + $self->vertices(scalar @{$mesh->vertices}); + $self->materials($self->model_object->materials_count); + } +} + +sub check_manifoldness { + my $self = shift; + + $self->is_manifold($self->get_model_object->mesh->check_manifoldness); + return $self->is_manifold; } sub free_model_object { @@ -1103,7 +1137,7 @@ sub set_scale { my $factor = $scale / $self->scale; return if $factor == 1; - $self->size->[$_] *= $factor for X,Y; + $self->size->[$_] *= $factor for X,Y,Z; if ($self->thumbnail) { $self->thumbnail->scale($factor); $self->thumbnail->align_to_origin; @@ -1119,4 +1153,59 @@ sub rotated_size { ->size; } +package Slic3r::GUI::Plater::ObjectInfoDialog; +use Wx qw(:dialog :id :misc :sizer :propgrid :systemsettings); +use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, "Object Info", wxDefaultPosition, wxDefaultSize); + $self->{object} = $params{object}; + + my $properties_box = Wx::StaticBox->new($self, -1, "Info", wxDefaultPosition, [400,200]); + my $grid_sizer = Wx::FlexGridSizer->new(3, 2, 10, 5); + $properties_box->SetSizer($grid_sizer); + $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); + $grid_sizer->AddGrowableCol(1); + + my $label_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + $label_font->SetPointSize(10); + + my $properties = $self->get_properties; + foreach my $property (@$properties) { + my $label = Wx::StaticText->new($properties_box, -1, $property->[0] . ":"); + my $value = Wx::StaticText->new($properties_box, -1, $property->[1]); + $label->SetFont($label_font); + $grid_sizer->Add($label, 1, wxALIGN_BOTTOM); + $grid_sizer->Add($value, 0); + } + + my $buttons = $self->CreateStdDialogButtonSizer(wxOK); + EVT_BUTTON($self, wxID_OK, sub { $self->EndModal(wxID_OK); }); + + my $sizer = Wx::BoxSizer->new(wxVERTICAL); + $sizer->Add($properties_box, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); + $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + $self->SetSizer($sizer); + $sizer->SetSizeHints($self); + + return $self; +} + +sub get_properties { + my $self = shift; + + return [ + ['Name' => $self->{object}->name], + ['Size' => sprintf "%.2f x %.2f x %.2f", @{$self->{object}->size}], + ['Facets' => $self->{object}->facets], + ['Vertices' => $self->{object}->vertices], + ['Materials' => $self->{object}->materials], + ['Two-Manifold' => $self->{object}->is_manifold ? 'Yes' : 'No'], + ]; +} + 1; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index ffc5ab901..c05ee8ee2 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -129,6 +129,13 @@ sub scale { } } +sub materials_count { + my $self = shift; + + my %materials = map { $_->material_id // '_default' => 1 } @{$self->volumes}; + return scalar keys %materials; +} + package Slic3r::Model::Volume; use Moo; diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 770a2a8c4..a677273b9 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -154,7 +154,9 @@ sub check_manifoldness { warn sprintf "Warning: The input file contains a hole near edge %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]}; + return 0; } + return 1; } sub unpack_line { From 50f6909475640a3178ad51390cb49f979c5173d3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 24 Oct 2012 22:55:03 +0200 Subject: [PATCH 010/172] Don't apply slowdown factor to bridges. #640 --- lib/Slic3r/Print.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index fcbf1ba16..6920cd652 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -790,7 +790,7 @@ sub write_gcode { Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100; if ($speed_factor < 1) { - $gcode =~ s/^(?=.*? [XY])(?=.*? E)(G1 .*?F)(\d+(?:\.\d+)?)/ + $gcode =~ s/^(?=.*? [XY])(?=.*? E)(? Date: Wed, 24 Oct 2012 22:57:19 +0200 Subject: [PATCH 011/172] Bugfix: bed temperature wasn't being set after the first layer when first layer bed temperature was set to zero. #760 --- lib/Slic3r/Print.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 6920cd652..652a1dee7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -697,7 +697,7 @@ sub write_gcode { if $self->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; } $gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature) - if $Slic3r::Config->first_layer_bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; + if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; } # go to layer (just use the first one, we only need Z from it) From bbd7765f401649a6901731c1893350b8258b4798 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 25 Oct 2012 11:24:56 +0200 Subject: [PATCH 012/172] Refactored the options group building to allow for more flexible layouts --- lib/Slic3r/GUI/OptionsGroup.pm | 189 +++++++++++++++++++-------------- lib/Slic3r/GUI/Tab.pm | 9 +- 2 files changed, 119 insertions(+), 79 deletions(-) diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index de056cb8a..9eec3791b 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -44,6 +44,7 @@ Slic3r::GUI::OptionsGroup - pre-filled Wx::StaticBoxSizer wrapper containing one has 'parent' => (is => 'ro', required => 1); has 'title' => (is => 'ro', required => 1); has 'options' => (is => 'ro', required => 1, trigger => 1); +has 'lines' => (is => 'lazy'); has 'on_change' => (is => 'ro', default => sub { sub {} }); has 'no_labels' => (is => 'ro', default => sub { 0 }); has 'label_width' => (is => 'ro', default => sub { 180 }); @@ -66,89 +67,121 @@ sub BUILD { $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); $grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1); - my $sidetext_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - - foreach my $opt (@{$self->options}) { - my $opt_key = $opt->{opt_key}; - $self->_triggers->{$opt_key} = $opt->{on_change} || sub {}; - - my $label; - if (!$self->no_labels) { - $label = Wx::StaticText->new($self->parent, -1, "$opt->{label}:", wxDefaultPosition, [$self->label_width, -1]); - $label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug - $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0); - } - - my $field; - if ($opt->{type} =~ /^(i|f|s|s@)$/) { - my $style = 0; - $style = wxTE_MULTILINE if $opt->{multiline}; - my $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); - - $field = $opt->{type} eq 'i' - ? Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 2147483647, $opt->{default}) - : Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style); - $field->Disable if $opt->{readonly}; - $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; - - my $on_change = sub { $self->_on_change($opt_key, $field->GetValue) }; - $opt->{type} eq 'i' - ? EVT_SPINCTRL ($self->parent, $field, $on_change) - : EVT_TEXT ($self->parent, $field, $on_change); - } elsif ($opt->{type} eq 'bool') { - $field = Wx::CheckBox->new($self->parent, -1, ""); - $field->SetValue($opt->{default}); - EVT_CHECKBOX($self->parent, $field, sub { $self->_on_change($opt_key, $field->GetValue); }); - $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; - } elsif ($opt->{type} eq 'point') { - $field = Wx::BoxSizer->new(wxHORIZONTAL); - my $field_size = Wx::Size->new(40, -1); - my @items = ( - Wx::StaticText->new($self->parent, -1, "x:"), - my $x_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[0], wxDefaultPosition, $field_size), - Wx::StaticText->new($self->parent, -1, " y:"), - my $y_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[1], wxDefaultPosition, $field_size), - ); - $field->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items; - if ($opt->{tooltip}) { - $_->SetToolTipString($opt->{tooltip}) for @items; - } - EVT_TEXT($self->parent, $_, sub { $self->_on_change($opt_key, [ $x_field->GetValue, $y_field->GetValue ]) }) - for $x_field, $y_field; - $self->_setters->{$opt_key} = sub { - $x_field->SetValue($_[0][0]); - $y_field->SetValue($_[0][1]); - }; - } elsif ($opt->{type} eq 'select') { - $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, wxDefaultSize, $opt->{labels} || $opt->{values}, wxCB_READONLY); - EVT_COMBOBOX($self->parent, $field, sub { - $self->_on_change($opt_key, $opt->{values}[$field->GetSelection]); - }); - $self->_setters->{$opt_key} = sub { - $field->SetSelection(grep $opt->{values}[$_] eq $_[0], 0..$#{$opt->{values}}); - }; - $self->_setters->{$opt_key}->($opt->{default}); - } else { - die "Unsupported option type: " . $opt->{type}; - } - $label->SetToolTipString($opt->{tooltip}) if $label && $opt->{tooltip}; - $field->SetToolTipString($opt->{tooltip}) if $opt->{tooltip} && $field->can('SetToolTipString'); - if ($opt->{sidetext}) { - my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($field, 0, wxALIGN_CENTER_VERTICAL, 0); - my $sidetext = Wx::StaticText->new($self->parent, -1, $opt->{sidetext}, wxDefaultPosition, wxDefaultSize); - $sidetext->SetFont($sidetext_font); - $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); - $grid_sizer->Add($sizer); - } else { - $grid_sizer->Add($field, 0, ($opt->{full_width} ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); - } - } + $self->{sidetext_font} = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + $self->_build_line($_, $grid_sizer) for @{$self->lines}; # TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific $self->sizer->Add($grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5); } +# default behavior: one option per line +sub _build_lines { + my $self = shift; + + my $lines = []; + foreach my $opt (@{$self->options}) { + push @$lines, { + label => $opt->{label}, + sidetext => $opt->{sidetext}, + full_width => $opt->{full_width}, + options => [$opt->{opt_key}], + }; + } + return $lines; +} + +sub _build_line { + my $self = shift; + my ($line, $grid_sizer) = @_; + + my $label; + if (!$self->no_labels) { + $label = Wx::StaticText->new($self->parent, -1, "$line->{label}:", wxDefaultPosition, [$self->label_width, -1]); + $label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug + $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0); + $label->SetToolTipString($line->{tooltip}) if $line->{tooltip}; + } + + my @fields = (); + foreach my $opt_key (@{$line->{options}}) { + my $opt = first { $_->{opt_key} eq $opt_key } @{$self->options}; + push @fields, $self->_build_field($opt); + } + if (@fields > 1 || $line->{sidetext}) { + my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $sizer->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @fields; + my $sidetext = Wx::StaticText->new($self->parent, -1, $line->{sidetext}, wxDefaultPosition, wxDefaultSize); + $sidetext->SetFont($self->{sidetext_font}); + $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); + $grid_sizer->Add($sizer); + } else { + $grid_sizer->Add($fields[0], 0, ($line->{full_width} ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); + } +} + +sub _build_field { + my $self = shift; + my ($opt) = @_; + + my $opt_key = $opt->{opt_key}; + $self->_triggers->{$opt_key} = $opt->{on_change} || sub {}; + + my $field; + if ($opt->{type} =~ /^(i|f|s|s@)$/) { + my $style = 0; + $style = wxTE_MULTILINE if $opt->{multiline}; + my $size = Wx::Size->new($opt->{width} || -1, $opt->{height} || -1); + + $field = $opt->{type} eq 'i' + ? Wx::SpinCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style, $opt->{min} || 0, $opt->{max} || 2147483647, $opt->{default}) + : Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style); + $field->Disable if $opt->{readonly}; + $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; + + my $on_change = sub { $self->_on_change($opt_key, $field->GetValue) }; + $opt->{type} eq 'i' + ? EVT_SPINCTRL ($self->parent, $field, $on_change) + : EVT_TEXT ($self->parent, $field, $on_change); + } elsif ($opt->{type} eq 'bool') { + $field = Wx::CheckBox->new($self->parent, -1, ""); + $field->SetValue($opt->{default}); + EVT_CHECKBOX($self->parent, $field, sub { $self->_on_change($opt_key, $field->GetValue); }); + $self->_setters->{$opt_key} = sub { $field->SetValue($_[0]) }; + } elsif ($opt->{type} eq 'point') { + $field = Wx::BoxSizer->new(wxHORIZONTAL); + my $field_size = Wx::Size->new(40, -1); + my @items = ( + Wx::StaticText->new($self->parent, -1, "x:"), + my $x_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[0], wxDefaultPosition, $field_size), + Wx::StaticText->new($self->parent, -1, " y:"), + my $y_field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}->[1], wxDefaultPosition, $field_size), + ); + $field->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items; + if ($opt->{tooltip}) { + $_->SetToolTipString($opt->{tooltip}) for @items; + } + EVT_TEXT($self->parent, $_, sub { $self->_on_change($opt_key, [ $x_field->GetValue, $y_field->GetValue ]) }) + for $x_field, $y_field; + $self->_setters->{$opt_key} = sub { + $x_field->SetValue($_[0][0]); + $y_field->SetValue($_[0][1]); + }; + } elsif ($opt->{type} eq 'select') { + $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, wxDefaultSize, $opt->{labels} || $opt->{values}, wxCB_READONLY); + EVT_COMBOBOX($self->parent, $field, sub { + $self->_on_change($opt_key, $opt->{values}[$field->GetSelection]); + }); + $self->_setters->{$opt_key} = sub { + $field->SetSelection(grep $opt->{values}[$_] eq $_[0], 0..$#{$opt->{values}}); + }; + $self->_setters->{$opt_key}->($opt->{default}); + } else { + die "Unsupported option type: " . $opt->{type}; + } + $field->SetToolTipString($opt->{tooltip}) if $opt->{tooltip} && $field->can('SetToolTipString'); + return $field; +} + sub _option { my $self = shift; my ($opt_key) = @_; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 0a6eacab8..fbab74eb2 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -397,6 +397,12 @@ sub build { { title => 'Horizontal shells', options => [qw(solid_layers)], + lines => [ + { + label => 'Solid layers', + options => [qw(solid_layers)], + }, + ], }, ]); @@ -722,7 +728,8 @@ sub append_optgroup { my $self = shift; my %params = @_; - my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + my $class = $params{class} || 'Slic3r::GUI::ConfigOptionsGroup'; + my $optgroup = $class->new( parent => $self, config => $self->GetParent->{config}, label_width => 200, From cee3864fc9651cbdcb8e80fce503b311c9a2bfa5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 25 Oct 2012 12:21:04 +0200 Subject: [PATCH 013/172] Independently set number of top/bottom solid layers. #676 --- lib/Slic3r/Config.pm | 32 ++++++++++++++++++++++++++++---- lib/Slic3r/GUI/OptionsGroup.pm | 19 +++++++++++++++---- lib/Slic3r/GUI/Tab.pm | 4 ++-- lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print.pm | 4 +++- lib/Slic3r/Print/Object.pm | 3 ++- 6 files changed, 51 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 1c78843fd..e5318d879 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -424,9 +424,23 @@ our $Options = { }, 'solid_layers' => { label => 'Solid layers', - tooltip => 'Number of solid layers to generate on top and bottom.', + tooltip => 'Number of solid layers to generate on top and bottom surfaces.', cli => 'solid-layers=i', type => 'i', + shortcut => [qw(top_solid_layers bottom_solid_layers)], + }, + 'top_solid_layers' => { + label => 'Top', + tooltip => 'Number of solid layers to generate on top surfaces.', + cli => 'top-solid-layers=i', + type => 'i', + default => 3, + }, + 'bottom_solid_layers' => { + label => 'Bottom', + tooltip => 'Number of solid layers to generate on bottom surfaces.', + cli => 'bottom-solid-layers=i', + type => 'i', default => 3, }, 'fill_pattern' => { @@ -867,7 +881,11 @@ sub new { sub new_from_defaults { my $class = shift; my @opt_keys = - return $class->new(map { $_ => $Options->{$_}{default} } (@_ ? @_ : keys %$Options)); + return $class->new( + map { $_ => $Options->{$_}{default} } + grep !$Options->{$_}{shortcut}, + (@_ ? @_ : keys %$Options) + ); } sub new_from_cli { @@ -972,6 +990,10 @@ sub set { if $deserialize && $Options->{$opt_key}{deserialize}; $self->{$opt_key} = $value; + + if ($Options->{$opt_key}{shortcut}) { + $self->set($_, $value, $deserialize) for @{$Options->{$opt_key}{shortcut}}; + } } sub set_ifndef { @@ -1003,6 +1025,7 @@ sub save { my $ini = { _ => {} }; foreach my $opt_key (sort keys %$self) { + next if $Options->{$opt_key}{shortcut}; next if $Options->{$opt_key}{gui_only}; $ini->{_}{$opt_key} = $self->serialize($opt_key); } @@ -1055,8 +1078,9 @@ sub validate { if $self->perimeters < 0; # --solid-layers - die "Invalid value for --solid-layers\n" - if $self->solid_layers < 0; + die "Invalid value for --solid-layers\n" if defined $self->solid_layers && $self->solid_layers < 0; + die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0; + die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0; # --print-center die "Invalid value for --print-center\n" diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index 9eec3791b..6cfda414b 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -103,16 +103,27 @@ sub _build_line { } my @fields = (); + my @field_labels = (); foreach my $opt_key (@{$line->{options}}) { my $opt = first { $_->{opt_key} eq $opt_key } @{$self->options}; push @fields, $self->_build_field($opt); + push @field_labels, $opt->{label}; } if (@fields > 1 || $line->{sidetext}) { my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $sizer->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @fields; - my $sidetext = Wx::StaticText->new($self->parent, -1, $line->{sidetext}, wxDefaultPosition, wxDefaultSize); - $sidetext->SetFont($self->{sidetext_font}); - $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); + for my $i (0 .. $#fields) { + if (@fields > 1) { + my $field_label = Wx::StaticText->new($self->parent, -1, "$field_labels[$i]:", wxDefaultPosition, wxDefaultSize); + $field_label->SetFont($self->{sidetext_font}); + $sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0); + } + $sizer->Add($fields[$i], 0, wxALIGN_CENTER_VERTICAL, 0); + } + if ($line->{sidetext}) { + my $sidetext = Wx::StaticText->new($self->parent, -1, $line->{sidetext}, wxDefaultPosition, wxDefaultSize); + $sidetext->SetFont($self->{sidetext_font}); + $sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4); + } $grid_sizer->Add($sizer); } else { $grid_sizer->Add($fields[0], 0, ($line->{full_width} ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fbab74eb2..45b235040 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -396,11 +396,11 @@ sub build { }, { title => 'Horizontal shells', - options => [qw(solid_layers)], + options => [qw(top_solid_layers bottom_solid_layers)], lines => [ { label => 'Solid layers', - options => [qw(solid_layers)], + options => [qw(top_solid_layers bottom_solid_layers)], }, ], }, diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index a23a85b6e..564a436a4 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -384,7 +384,7 @@ sub prepare_fill_surfaces { # if no solid layers are requested, turn top/bottom surfaces to internal # note that this modifies $self->surfaces in place - if ($Slic3r::Config->solid_layers == 0) { + if ($Slic3r::Config->top_solid_layers == 0 && $Slic3r::Config->bottom_solid_layers == 0) { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 652a1dee7..06ef9e5c7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -54,6 +54,8 @@ sub _trigger_config { $self->config->set_ifndef('bridge_speed', $self->config->infill_speed); $self->config->set_ifndef('solid_infill_speed', $self->config->infill_speed); $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); + $self->config->set_ifndef('top_solid_layers', $self->config->solid_layers); + $self->config->set_ifndef('bottom_solid_layers', $self->config->solid_layers); # G-code flavors $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3'; @@ -629,7 +631,7 @@ sub write_gcode { print $fh "; $_\n" foreach split /\R/, $Slic3r::Config->notes; print $fh "\n" if $Slic3r::Config->notes; - for (qw(layer_height perimeters solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) { + for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed scale)) { printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_; } for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 3d7066a34..2760fa6f2 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -344,8 +344,9 @@ sub discover_horizontal_shells { Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", $i, scalar(@surfaces), ($type == S_TYPE_TOP ? 'top' : 'bottom'); + my $solid_layers = S_TYPE_TOP ? $Slic3r::Config->top_solid_layers : $Slic3r::Config->bottom_solid_layers; for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1; - abs($n - $i) <= $Slic3r::Config->solid_layers-1; + abs($n - $i) <= $solid_layers-1; $type == S_TYPE_TOP ? $n-- : $n++) { next if $n < 0 || $n >= $self->layer_count; From 667d88603e70890ea715d9be76de6fb5107d4fb4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 25 Oct 2012 12:37:02 +0200 Subject: [PATCH 014/172] Reorder some options using the more compact layout --- lib/Slic3r/Config.pm | 18 +++++++++--------- lib/Slic3r/GUI/OptionsGroup.pm | 13 ++++++++++++- lib/Slic3r/GUI/Tab.pm | 27 ++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index e5318d879..1bb176ee4 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -155,7 +155,7 @@ our $Options = { default => [1], }, 'temperature' => { - label => 'Temperature', + label => 'Other layers', tooltip => 'Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output.', sidetext => '°C', cli => 'temperature=i@', @@ -166,7 +166,7 @@ our $Options = { default => [200], }, 'first_layer_temperature' => { - label => 'First layer temperature', + label => 'First layer', tooltip => 'Extruder temperature for first layer. If you want to control temperature manually during print, set this to zero to disable temperature control commands in the output file.', sidetext => '°C', cli => 'first-layer-temperature=i@', @@ -203,7 +203,7 @@ our $Options = { # filament options 'first_layer_bed_temperature' => { - label => 'First layer bed temperature', + label => 'First layer', tooltip => 'Heated build plate temperature for the first layer. Set this to zero to disable bed temperature control commands in the output.', sidetext => '°C', cli => 'first-layer-bed-temperature=i', @@ -212,7 +212,7 @@ our $Options = { default => 0, }, 'bed_temperature' => { - label => 'Bed Temperature', + label => 'Other layers', tooltip => 'Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output.', sidetext => '°C', cli => 'bed-temperature=i', @@ -680,7 +680,7 @@ END default => 0, }, 'min_fan_speed' => { - label => 'Min fan speed', + label => 'Min', tooltip => 'This setting represents the minimum PWM your fan needs to work.', sidetext => '%', cli => 'min-fan-speed=i', @@ -689,7 +689,7 @@ END default => 35, }, 'max_fan_speed' => { - label => 'Max fan speed', + label => 'Max', tooltip => 'This setting represents the maximum speed of your fan.', sidetext => '%', cli => 'max-fan-speed=i', @@ -698,7 +698,7 @@ END default => 100, }, 'bridge_fan_speed' => { - label => 'Bridge fan speed', + label => 'Bridges', tooltip => 'This fan speed is enforced during all bridges.', sidetext => '%', cli => 'bridge-fan-speed=i', @@ -844,7 +844,7 @@ END default => 0, }, 'extruder_clearance_radius' => { - label => 'Extruder clearance radius', + label => 'Radius', tooltip => 'Set this to the clearance radius around your extruder. If the extruder is not centered, choose the largest value for safety. This setting is used to check for collisions and to display the graphical preview in the plater.', sidetext => 'mm', cli => 'extruder-clearance-radius=f', @@ -852,7 +852,7 @@ END default => 20, }, 'extruder_clearance_height' => { - label => 'Extruder clearance height', + label => 'Height', tooltip => 'Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects.', sidetext => 'mm', cli => 'extruder-clearance-height=f', diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index 6cfda414b..a43184898 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -90,6 +90,17 @@ sub _build_lines { return $lines; } +sub single_option_line { + my $class = shift; + my ($opt_key) = @_; + + return { + label => $Slic3r::Config::Options->{$opt_key}{label}, + sidetext => $Slic3r::Config::Options->{$opt_key}{sidetext}, + options => [$opt_key], + }; +} + sub _build_line { my $self = shift; my ($line, $grid_sizer) = @_; @@ -112,7 +123,7 @@ sub _build_line { if (@fields > 1 || $line->{sidetext}) { my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); for my $i (0 .. $#fields) { - if (@fields > 1) { + if (@fields > 1 && $field_labels[$i]) { my $field_label = Wx::StaticText->new($self->parent, -1, "$field_labels[$i]:", wxDefaultPosition, wxDefaultSize); $field_label->SetFont($self->{sidetext_font}); $sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 45b235040..2916f060c 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -462,6 +462,13 @@ sub build { { title => 'Sequential printing', options => [qw(complete_objects extruder_clearance_radius extruder_clearance_height)], + lines => [ + Slic3r::GUI::OptionsGroup->single_option_line('complete_objects'), + { + label => 'Extruder clearance (mm)', + options => [qw(extruder_clearance_radius extruder_clearance_height)], + }, + ], }, { title => 'Output file', @@ -515,8 +522,18 @@ sub build { options => ['filament_diameter#0', 'extrusion_multiplier#0'], }, { - title => 'Temperature', + title => 'Temperature (°C)', options => ['temperature#0', 'first_layer_temperature#0', qw(bed_temperature first_layer_bed_temperature)], + lines => [ + { + label => 'Extruder', + options => ['first_layer_temperature#0', 'temperature#0'], + }, + { + label => 'Bed', + options => [qw(first_layer_bed_temperature bed_temperature)], + }, + ], }, ]); @@ -528,6 +545,14 @@ sub build { { title => 'Fan settings', options => [qw(min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers fan_always_on)], + lines => [ + { + label => 'Fan speed', + options => [qw(min_fan_speed max_fan_speed bridge_fan_speed)], + }, + Slic3r::GUI::OptionsGroup->single_option_line('disable_fan_first_layers'), + Slic3r::GUI::OptionsGroup->single_option_line('fan_always_on'), + ], }, { title => 'Cooling thresholds', From e2ad22fccae7e56a7a229de802a711774694d9c6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 25 Oct 2012 12:39:22 +0200 Subject: [PATCH 015/172] Update --help with the new --top-solid-layers and --bottom-solid-layers --- README.markdown | 5 +++-- slic3r.pl | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 5ca1ac240..21093eeb5 100644 --- a/README.markdown +++ b/README.markdown @@ -158,8 +158,9 @@ The author of the Silk icon set is Mark James. Print options: --perimeters Number of perimeters/horizontal skins (range: 0+, default: 3) - --solid-layers Number of solid layers to do for top/bottom surfaces - (range: 1+, default: 3) + --top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: 3) + --bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: 3) + --solid-layers Shortcut for setting the two options above at once --fill-density Infill density (range: 0-1, default: 0.4) --fill-angle Infill angle in degrees (range: 0-90, default: 45) --fill-pattern Pattern to use to fill non-solid layers (default: rectilinear) diff --git a/slic3r.pl b/slic3r.pl index bcda50571..f0b558133 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -206,8 +206,9 @@ $j Print options: --perimeters Number of perimeters/horizontal skins (range: 0+, default: $config->{perimeters}) - --solid-layers Number of solid layers to do for top/bottom surfaces - (range: 1+, default: $config->{solid_layers}) + --top-solid-layers Number of solid layers to do for top surfaces (range: 0+, default: $config->{top_solid_layers}) + --bottom-solid-layers Number of solid layers to do for bottom surfaces (range: 0+, default: $config->{bottom_solid_layers}) + --solid-layers Shortcut for setting the two options above at once --fill-density Infill density (range: 0-1, default: $config->{fill_density}) --fill-angle Infill angle in degrees (range: 0-90, default: $config->{fill_angle}) --fill-pattern Pattern to use to fill non-solid layers (default: $config->{fill_pattern}) From b0f24a2666fb1b8d72e08cca9e1e6f6a69f4e79f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 25 Oct 2012 18:49:59 +0200 Subject: [PATCH 016/172] Bugfix: unnecessary tool changes were generated when using the same tool for subsequent parts --- 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 7653e4c32..b75def91f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -374,7 +374,7 @@ sub set_extruder { my ($extruder) = @_; # return nothing if this extruder was already selected - return "" if (defined $self->extruder) && ($self->extruder->id == $extruder); + return "" if (defined $self->extruder) && ($self->extruder->id == $extruder->id); # if we are running a single-extruder setup, just set the extruder and return nothing if (!$self->multiple_extruders) { From f9e20db156a48bb5828dcdfeffd6f8aee21b2141 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 27 Oct 2012 21:20:32 +0200 Subject: [PATCH 017/172] Update t/dynamic.t --- lib/Slic3r/Layer/Region.pm | 2 +- t/dynamic.t | 41 +++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 564a436a4..d12cb6041 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -234,7 +234,7 @@ sub make_perimeters { map $_->noncollapsing_offset_ex(-0.5*$flow->scaled_width), @gaps; - if (0) { + if (0) { # remember to re-enable t/dynamic.t # fill gaps using dynamic extrusion width, by treating them like thin polygons, # thus generating the skeleton and using it to fill them my %path_args = ( diff --git a/t/dynamic.t b/t/dynamic.t index 0b5dc490f..c9cb95823 100644 --- a/t/dynamic.t +++ b/t/dynamic.t @@ -2,6 +2,7 @@ use Test::More; use strict; use warnings; +plan skip_all => 'variable-width paths are currently disabled'; plan tests => 20; BEGIN { @@ -26,19 +27,30 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } } { + local $Slic3r::Config = Slic3r::Config->new( + perimeters => 3, + ); my $w = 0.7; - local $Slic3r::perimeter_flow = Slic3r::Flow->new( + my $perimeter_flow = Slic3r::Flow->new( nozzle_diameter => 0.5, layer_height => 0.4, width => $w, ); - local $Slic3r::Config = Slic3r::Config->new( - perimeters => 3, - ); + my $print = Slic3r::Print->new; + my $region = Slic3r::Print::Region->new( + print => $print, + flows => { perimeter => $perimeter_flow }, + ); + push @{$print->regions}, $region; + my $object = Slic3r::Print::Object->new( + print => $print, + size => [1,1], + ); my $make_layer = sub { my ($width) = @_; my $layer = Slic3r::Layer->new( + object => $object, id => 1, slices => [ Slic3r::Surface->new( @@ -48,27 +60,28 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } ], thin_walls => [], ); + my $layerm = $layer->region(0); $layer->make_perimeters; - return $layer; + return $layerm; }; my %widths = ( 1 * $w => { perimeters => 1, gaps => 0 }, - 1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.2 * $w)->spacing }, - 1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 0.5 * $w)->spacing }, - 2 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing }, - 2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->clone(width => 1.5 * $w)->spacing }, + 1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.2 * $w)->spacing }, + 1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.5 * $w)->spacing }, + 2 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing }, + 2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 1.5 * $w)->spacing }, 3 * $w => { perimeters => 2, gaps => 0 }, - 4 * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $Slic3r::perimeter_flow->spacing }, + 4 * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing }, ); foreach my $width (sort keys %widths) { - my $layer = $make_layer->($width); - is scalar @{$layer->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters'; - is scalar @{$layer->thin_fills} ? 1 : 0, $widths{$width}{gaps}, + my $layerm = $make_layer->($width); + is scalar @{$layerm->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters'; + is scalar @{$layerm->thin_fills} ? 1 : 0, $widths{$width}{gaps}, ($widths{$width}{gaps} ? 'gaps were filled' : 'no gaps detected'); # TODO: we should check the exact number of gaps, but we need a better medial axis algorithm - my @gaps = map $_->unpack, @{$layer->thin_fills}; + my @gaps = map $_->unpack, @{$layerm->thin_fills}; if (@gaps) { ok +(!first { abs($_->flow_spacing - $widths{$width}{gap_flow_spacing}) > epsilon } @gaps), 'flow spacing was dynamically adjusted'; From bc9ff848dd0ef16e125aaced93fe6450698f78e9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 27 Oct 2012 21:21:18 +0200 Subject: [PATCH 018/172] Removed unused 'propgrid' import group from Wx. #764 --- 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 c881ceb74..c90400e2f 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1154,7 +1154,7 @@ sub rotated_size { } package Slic3r::GUI::Plater::ObjectInfoDialog; -use Wx qw(:dialog :id :misc :sizer :propgrid :systemsettings); +use Wx qw(:dialog :id :misc :sizer :systemsettings); use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); use base 'Wx::Dialog'; From d0bee4bf4198babf07be84569c95ce5611141a07 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 27 Oct 2012 21:39:57 +0200 Subject: [PATCH 019/172] Fixed regression in Split. #766 --- lib/Slic3r/GUI/Plater.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index c90400e2f..1d9d32001 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -489,13 +489,18 @@ sub split_object { # thumbnail thread returns) $self->remove($obj_idx); + # create a bogus Model object, we only need to instantiate the new Model::Object objects + my $new_model = Slic3r::Model->new; + foreach my $mesh (@new_meshes) { my @extents = $mesh->extents; + my $model_object = $new_model->add_object(vertices => $mesh->vertices); + $model_object->add_volume(facets => $mesh->facets); my $object = Slic3r::GUI::Plater::Object->new( name => basename($current_object->input_file), input_file => $current_object->input_file, input_file_object_id => undef, - mesh => $mesh, + model_object => $model_object, instances => [ map [$extents[X][MIN], $extents[Y][MIN]], 1..$current_copies_num ], ); push @{ $self->{objects} }, $object; From 49916b6178f5fc507538b79d860a62a29c47b586 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 27 Oct 2012 22:09:22 +0200 Subject: [PATCH 020/172] Revert 2bcac88683c8d4f6ce31127e8789f3df4f97fe2e by setting the scale factor manually to 1. #720 --- lib/Slic3r/Geometry/Clipper.pm | 4 ++-- lib/Slic3r/Layer/Region.pm | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index fca00af57..4739f15dc 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -14,12 +14,12 @@ our $clipper = Math::Clipper->new; sub safety_offset { my ($polygons, $factor) = @_; - return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100000, JT_MITER, 2); + return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 1, JT_MITER, 2); } sub offset { my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= 100000; + $scale ||= 1; $joinType = JT_MITER if !defined $joinType; $miterLimit ||= 2; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index d12cb6041..c5dc43051 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -402,7 +402,7 @@ sub prepare_fill_surfaces { # offset inwards my @offsets = $surface->expolygon->offset_ex(-$distance); - @offsets = @{union_ex(Math::Clipper::offset([ map @$_, @offsets ], $distance, 100000, JT_MITER))}; + @offsets = @{union_ex(Math::Clipper::offset([ map @$_, @offsets ], $distance, 1, JT_MITER))}; map Slic3r::Surface->new( expolygon => $_, surface_type => $surface->surface_type, From a0fe93e8cffb8589dd443206adc62103c0ce1346 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 10:28:40 +0100 Subject: [PATCH 021/172] Bugfix: sometimes first layer extrusion width was not applied --- lib/Slic3r/Layer.pm | 7 ++++++- lib/Slic3r/Layer/Region.pm | 21 ++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index bc65064a9..4a2c89689 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -3,7 +3,7 @@ use Moo; use Slic3r::Geometry::Clipper qw(union_ex); -has 'id' => (is => 'rw', required => 1); # sequential number of layer, 0-based +has 'id' => (is => 'rw', required => 1, trigger => 1); # sequential number of layer, 0-based has 'object' => (is => 'ro', weak_ref => 1, required => 1); has 'regions' => (is => 'ro', default => sub { [] }); has 'slicing_errors' => (is => 'rw'); @@ -20,6 +20,11 @@ has 'slices' => (is => 'rw'); # ordered collection of extrusion paths to fill surfaces for support material has 'support_fills' => (is => 'rw'); +sub _trigger_id { + my $self = shift; + $_->_trigger_layer for @{$self->regions || []}; +} + # Z used for slicing sub _build_slice_z { my $self = shift; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index c5dc43051..dd16c17d0 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -11,11 +11,12 @@ has 'layer' => ( is => 'ro', weak_ref => 1, required => 1, + trigger => 1, handles => [qw(id slice_z print_z height flow)], ); has 'region' => (is => 'ro', required => 1); -has 'perimeter_flow' => (is => 'lazy'); -has 'infill_flow' => (is => 'lazy'); +has 'perimeter_flow' => (is => 'rw'); +has 'infill_flow' => (is => 'rw'); # collection of spare segments generated by slicing the original geometry; # these need to be merged in continuos (closed) polylines @@ -47,18 +48,16 @@ has 'perimeters' => (is => 'rw', default => sub { [] }); # ordered collection of extrusion paths to fill surfaces has 'fills' => (is => 'rw', default => sub { [] }); -sub _build_perimeter_flow { +sub _trigger_layer { my $self = shift; - return $self->id == 0 + + $self->perimeter_flow($self->id == 0 ? $self->region->first_layer_flows->{perimeter} - : $self->region->flows->{perimeter} -} - -sub _build_infill_flow { - my $self = shift; - return $self->id == 0 + : $self->region->flows->{perimeter}); + + $self->infill_flow($self->id == 0 ? $self->region->first_layer_flows->{infill} - : $self->region->flows->{infill} + : $self->region->flows->{infill}); } # build polylines from lines From f9b6caaecb8cbf2be940de68fcbba4a1f5c7f0f6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 12:22:30 +0100 Subject: [PATCH 022/172] Bugfix, last commit was incomplete --- lib/Slic3r/Layer/Region.pm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index dd16c17d0..f765b6e03 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -48,8 +48,19 @@ has 'perimeters' => (is => 'rw', default => sub { [] }); # ordered collection of extrusion paths to fill surfaces has 'fills' => (is => 'rw', default => sub { [] }); +sub BUILD { + my $self = shift; + $self->_update_flows; +} + sub _trigger_layer { my $self = shift; + $self->_update_flows; +} + +sub _update_flows { + my $self = shift; + return if !$self->region; $self->perimeter_flow($self->id == 0 ? $self->region->first_layer_flows->{perimeter} From deea02b444c1223d7c9f927878fddeb9910f8478 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 12:43:41 +0100 Subject: [PATCH 023/172] Descriptive textual feedback for cooling settings --- lib/Slic3r/Config.pm | 2 +- lib/Slic3r/GUI/OptionsGroup.pm | 39 +++++++++++++++++++++++++++++---- lib/Slic3r/GUI/Tab.pm | 40 +++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 1bb176ee4..0b63f2815 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -698,7 +698,7 @@ END default => 100, }, 'bridge_fan_speed' => { - label => 'Bridges', + label => 'Bridges fan speed', tooltip => 'This fan speed is enforced during all bridges.', sidetext => '%', cli => 'bridge-fan-speed=i', diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index a43184898..20f548d1d 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -67,11 +67,19 @@ sub BUILD { $grid_sizer->SetFlexibleDirection(wxHORIZONTAL); $grid_sizer->AddGrowableCol($self->no_labels ? 0 : 1); - $self->{sidetext_font} = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - $self->_build_line($_, $grid_sizer) for @{$self->lines}; - # TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific $self->sizer->Add($grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5); + + $self->{sidetext_font} = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + + foreach my $line (@{$self->lines}) { + if ($line->{widget}) { + my $window = $line->{widget}->GetWindow($self->parent); + $self->sizer->Add($window, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15); + } else { + $self->_build_line($line, $grid_sizer); + } + } } # default behavior: one option per line @@ -107,7 +115,7 @@ sub _build_line { my $label; if (!$self->no_labels) { - $label = Wx::StaticText->new($self->parent, -1, "$line->{label}:", wxDefaultPosition, [$self->label_width, -1]); + $label = Wx::StaticText->new($self->parent, -1, $line->{label} ? "$line->{label}:" : "", wxDefaultPosition, [$self->label_width, -1]); $label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug $grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0); $label->SetToolTipString($line->{tooltip}) if $line->{tooltip}; @@ -363,4 +371,27 @@ sub _config_methods { : qw(get 0); } +package Slic3r::GUI::OptionsGroup::StaticTextLine; +use Moo; +use Wx qw(:misc :systemsettings); + +sub GetWindow { + my $self = shift; + my ($parent) = @_; + + $self->{statictext} = Wx::StaticText->new($parent, -1, "foo", wxDefaultPosition, wxDefaultSize); + my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + $self->{statictext}->SetFont($font); + return $self->{statictext}; +} + +sub SetText { + my $self = shift; + my ($value) = @_; + + $self->{statictext}->SetLabel($value); + $self->{statictext}->Wrap(400); + $self->{statictext}->GetParent->Layout; +} + 1; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 2916f060c..d130799b3 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -541,6 +541,13 @@ sub build { { title => 'Enable', options => [qw(cooling)], + lines => [ + Slic3r::GUI::OptionsGroup->single_option_line('cooling'), + { + label => '', + widget => ($self->{description_line} = Slic3r::GUI::OptionsGroup::StaticTextLine->new), + }, + ], }, { title => 'Fan settings', @@ -548,8 +555,9 @@ sub build { lines => [ { label => 'Fan speed', - options => [qw(min_fan_speed max_fan_speed bridge_fan_speed)], + options => [qw(min_fan_speed max_fan_speed)], }, + Slic3r::GUI::OptionsGroup->single_option_line('bridge_fan_speed'), Slic3r::GUI::OptionsGroup->single_option_line('disable_fan_first_layers'), Slic3r::GUI::OptionsGroup->single_option_line('fan_always_on'), ], @@ -562,6 +570,36 @@ sub build { ]); } +sub _update_description { + my $self = shift; + + my $config = $self->config; + + my $msg = ""; + if ($config->cooling) { + $msg = sprintf "If estimated layer time is below ~%ds, fan will run at 100%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).", + $config->slowdown_below_layer_time, $config->slowdown_below_layer_time, $config->min_print_speed; + if ($config->fan_below_layer_time > $config->slowdown_below_layer_time) { + $msg .= sprintf "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.", + $config->fan_below_layer_time, $config->max_fan_speed, $config->min_fan_speed; + } + if ($config->fan_always_on) { + $msg .= sprintf "\nDuring the other layers, fan will always run at %d%%.", $config->min_fan_speed; + } else { + $msg .= "\nDuring the other layers, fan will be turned off." + } + } + $self->{description_line}->SetText($msg); +} + +sub on_value_change { + my $self = shift; + my ($opt_key) = @_; + $self->SUPER::on_value_change(@_); + + $self->_update_description; +} + package Slic3r::GUI::Tab::Printer; use base 'Slic3r::GUI::Tab'; From 2796041a6cdf422dc7fa89131192b3f55885e348 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 12:52:44 +0100 Subject: [PATCH 024/172] Bugfix: --top-solid-layers was ignored. #769 --- lib/Slic3r/Print/Object.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2760fa6f2..716007430 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -344,7 +344,9 @@ sub discover_horizontal_shells { Slic3r::debugf "Layer %d has %d surfaces of type '%s'\n", $i, scalar(@surfaces), ($type == S_TYPE_TOP ? 'top' : 'bottom'); - my $solid_layers = S_TYPE_TOP ? $Slic3r::Config->top_solid_layers : $Slic3r::Config->bottom_solid_layers; + my $solid_layers = ($type == S_TYPE_TOP) + ? $Slic3r::Config->top_solid_layers + : $Slic3r::Config->bottom_solid_layers; for (my $n = $type == S_TYPE_TOP ? $i-1 : $i+1; abs($n - $i) <= $solid_layers-1; $type == S_TYPE_TOP ? $n-- : $n++) { From f35cdef2aad22011f92b50b7333ef2445835d1e4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 14:22:51 +0100 Subject: [PATCH 025/172] Renamed ExtrusionPath->depth_layers to height --- lib/Slic3r/ExtrusionPath.pm | 24 ++++++++++++------------ lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print/Object.pm | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index c5466bcf7..200278496 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -18,8 +18,8 @@ has 'polyline' => ( handles => [qw(merge_continuous_lines lines length reverse)], ); -# depth_layers is the vertical thickness of the extrusion expressed in layers -has 'depth_layers' => (is => 'ro', default => sub {1}); +# height is the vertical thickness of the extrusion expressed in mm +has 'height' => (is => 'ro'); has 'flow_spacing' => (is => 'rw'); has 'role' => (is => 'rw', required => 1); @@ -34,7 +34,7 @@ use constant EXTR_ROLE_BRIDGE => 7; use constant EXTR_ROLE_SKIRT => 8; use constant EXTR_ROLE_SUPPORTMATERIAL => 9; -use constant PACK_FMT => 'cfca*'; +use constant PACK_FMT => 'ffca*'; # class or object method sub pack { @@ -42,11 +42,11 @@ sub pack { my %args = @_; if (ref $self) { - %args = map { $_ => $self->$_ } qw(depth_layers flow_spacing role polyline); + %args = map { $_ => $self->$_ } qw(height flow_spacing role polyline); } my $o = \ pack PACK_FMT, - $args{depth_layers} || 1, + $args{height} // -1, $args{flow_spacing} || -1, $args{role} // (die "Missing mandatory attribute 'role'"), #/ $args{polyline}->serialize; @@ -93,7 +93,7 @@ sub clip_with_expolygon { foreach my $polyline ($self->polyline->clip_with_expolygon($expolygon)) { push @paths, (ref $self)->new( polyline => $polyline, - depth_layers => $self->depth_layers, + height => $self->height, flow_spacing => $self->flow_spacing, role => $self->role, ); @@ -137,7 +137,7 @@ sub split_at_acute_angles { push @paths, (ref $self)->new( polyline => Slic3r::Polyline->new(\@p), role => $self->role, - depth_layers => $self->depth_layers, + height => $self->height, ); @p = ($p3); push @p, grep $_, shift @points or last; @@ -148,7 +148,7 @@ sub split_at_acute_angles { push @paths, (ref $self)->new( polyline => Slic3r::Polyline->new(\@p), role => $self->role, - depth_layers => $self->depth_layers, + height => $self->height, ) if @p > 1; return @paths; @@ -246,7 +246,7 @@ sub detect_arcs { push @paths, (ref $self)->new( polyline => Slic3r::Polyline->new(@points[0..$i]), role => $self->role, - depth_layers => $self->depth_layers, + height => $self->height, ) if $i > 0; # add our arc @@ -265,7 +265,7 @@ sub detect_arcs { push @paths, (ref $self)->new( polyline => Slic3r::Polyline->new(\@points), role => $self->role, - depth_layers => $self->depth_layers, + height => $self->height, ) if @points > 1; return @paths; @@ -275,11 +275,11 @@ package Slic3r::ExtrusionPath::Packed; sub unpack { my $self = shift; - my ($depth_layers, $flow_spacing, $role, $polyline_s) + my ($height, $flow_spacing, $role, $polyline_s) = unpack Slic3r::ExtrusionPath::PACK_FMT, $$self; return Slic3r::ExtrusionPath->new( - depth_layers => $depth_layers, + height => ($height == -1) ? undef : $height, flow_spacing => ($flow_spacing == -1) ? undef : $flow_spacing, role => $role, polyline => Slic3r::Polyline->deserialize($polyline_s), diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 4ae1f5415..8e919b738 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -169,7 +169,7 @@ sub make_fill { : $is_solid ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) : EXTR_ROLE_FILL), - depth_layers => $surface->depth_layers, + height => $surface->depth_layers * $Slic3r::Config->layer_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, ], diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b75def91f..d71627f39 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -149,7 +149,7 @@ sub extrude_path { $area = ($s**2) * PI/4; } else { my $s = $path->flow_spacing || ($self->layer ? $self->layer->flow->spacing : $Slic3r::flow->spacing); - my $h = $path->depth_layers * $self->layer->height; + my $h = $path->height // $self->layer->height; $area = $self->extruder->mm3_per_mm($s, $h); } diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index f765b6e03..d57105a48 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -279,7 +279,7 @@ sub make_perimeters { map Slic3r::ExtrusionPath->pack( polyline => Slic3r::Polyline->new(@$_), role => EXTR_ROLE_SOLIDFILL, - depth_layers => 1, + height => $self->height, flow_spacing => $params->{flow_spacing}, ), @paths; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 716007430..d7fad828b 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -579,7 +579,7 @@ sub generate_support_material { map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), role => EXTR_ROLE_SUPPORTMATERIAL, - depth_layers => 1, + height => undef, flow_spacing => $params->{flow_spacing}, ), @paths; } From b5bd216f5cd30406a877c0444ef23d4524ddc5f2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 14:23:47 +0100 Subject: [PATCH 026/172] Do not combine infill for bottom layer --- 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 d7fad828b..09ed0b05e 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -434,7 +434,7 @@ sub combine_infill { # 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; + next if ($i - $d) <= 0; # do not combine infill for bottom layer my $lower_layerm = $self->layer($i - 1)->regions->[$region_id]; # select surfaces of the lower layer having the depth we're looking for From ccb49a8439f61c7bcac3fd18fd63808f64664ca4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 16:59:20 +0100 Subject: [PATCH 027/172] Do a slightly thinner interface layer at the top of support material structures by taking into account the diameter of bridge extrudates (nophead's idea) --- lib/Slic3r/ExtrusionPath.pm | 2 +- lib/Slic3r/GCode.pm | 25 ++++++++++++++++++------- lib/Slic3r/Layer.pm | 27 ++++++++++++++++++++++++--- lib/Slic3r/Print.pm | 28 +++++++++++++++++++--------- lib/Slic3r/Print/Object.pm | 35 ++++++++++++++++++++++++++++------- 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 200278496..c55cd02ae 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -19,7 +19,7 @@ has 'polyline' => ( ); # height is the vertical thickness of the extrusion expressed in mm -has 'height' => (is => 'ro'); +has 'height' => (is => 'rw'); has 'flow_spacing' => (is => 'rw'); has 'role' => (is => 'rw', required => 1); diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d71627f39..6c21e5721 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -6,7 +6,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); -has 'layer' => (is => 'rw'); +has 'layer' => (is => 'rw'); # this is not very correct, we should replace it with explicit layer_id and avoid using $self->layer->flow at all here because it's too general has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw', default => sub {0} ); @@ -52,16 +52,12 @@ my %role_speeds = ( sub change_layer { my $self = shift; - my ($layer) = @_; + my ($layer, %params) = @_; $self->layer($layer); - my $z = $Slic3r::Config->z_offset + $layer->print_z * &Slic3r::SCALING_FACTOR; my $gcode = ""; - - $gcode .= $self->retract(move_z => $z); - $gcode .= $self->G0(undef, $z, 0, 'move to next layer (' . $layer->id . ')') - if $self->z != $z && !$self->lifted; + $gcode .= $self->move_z($layer->print_z) unless $params{dont_move_z}; $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" if $Slic3r::Config->layer_gcode; @@ -69,6 +65,21 @@ sub change_layer { return $gcode; } +# this method accepts Z in scaled coordinates +sub move_z { + my $self = shift; + my ($z, $comment) = @_; + + $z = $Slic3r::Config->z_offset + $z * &Slic3r::SCALING_FACTOR; + + my $gcode = ""; + $gcode .= $self->retract(move_z => $z); + $gcode .= $self->G0(undef, $z, 0, $comment || 'move to next layer (' . $self->layer->id . ')') + if $self->z != $z && !$self->lifted; + + return $gcode; +} + sub extrude { my $self = shift; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 4a2c89689..18169fd1d 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -18,14 +18,15 @@ has 'flow' => (is => 'ro', default => sub { $Slic3r::flow }); has 'slices' => (is => 'rw'); # ordered collection of extrusion paths to fill surfaces for support material -has 'support_fills' => (is => 'rw'); +has 'support_fills' => (is => 'rw'); +has 'support_interface_fills' => (is => 'rw'); sub _trigger_id { my $self = shift; $_->_trigger_layer for @{$self->regions || []}; } -# Z used for slicing +# Z used for slicing in scaled coordinates sub _build_slice_z { my $self = shift; @@ -36,17 +37,37 @@ sub _build_slice_z { / &Slic3r::SCALING_FACTOR; #/ } -# Z used for printing +# Z used for printing in scaled coordinates sub _build_print_z { my $self = shift; return ($Slic3r::Config->get_value('first_layer_height') + ($self->id * $Slic3r::Config->layer_height)) / &Slic3r::SCALING_FACTOR; } +# layer height in unscaled coordinates sub _build_height { my $self = shift; return $self->id == 0 ? $Slic3r::Config->get_value('first_layer_height') : $Slic3r::Config->layer_height; } +# layer height of interface paths in unscaled coordinates +sub support_material_interface_height { + my $self = shift; + + 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 + return 2*$self->height - $self->flow->nozzle_diameter; +} + +# Z used for printing support material interface in scaled coordinates +sub support_material_interface_z { + my $self = shift; + return $self->print_z - ($self->height - $self->support_material_interface_height) / &Slic3r::SCALING_FACTOR; +} + sub region { my $self = shift; my ($region_id) = @_; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 06ef9e5c7..8053dfd2e 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -702,8 +702,8 @@ sub write_gcode { if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; } - # go to layer (just use the first one, we only need Z from it) - $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); + # set new layer, but don't move Z as support material interfaces may need an intermediate one + $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id], dont_move_z => 1); $gcodegen->elapsed_time(0); # extrude skirt @@ -742,6 +742,23 @@ sub write_gcode { $gcodegen->shift_x($shift[X] + unscale $copy->[X]); $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); + # 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) { + $gcode .= $gcodegen->move_z($layer->support_material_interface_z) + if @{ $layer->support_interface_fills->paths }; + $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); + $gcode .= $gcodegen->extrude_path($_, 'support material interface') + for $layer->support_interface_fills->shortest_path($gcodegen->last_pos); + + $gcode .= $gcodegen->move_z($layer->print_z); + $gcode .= $gcodegen->extrude_path($_, 'support material') + for $layer->support_fills->shortest_path($gcodegen->last_pos); + } + + # set actual Z + $gcode .= $gcodegen->move_z($layer->print_z); + foreach my $region_id (0 .. ($self->regions_count-1)) { my $layerm = $layer->regions->[$region_id]; my $region = $self->regions->[$region_id]; @@ -766,13 +783,6 @@ sub write_gcode { } } } - - # extrude support material - if ($layer->support_fills) { - $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); - $gcode .= $gcodegen->extrude_path($_, 'support material') - for $layer->support_fills->shortest_path($gcodegen->last_pos); - } } return if !$gcode; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 09ed0b05e..dac760250 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -517,7 +517,8 @@ sub generate_support_material { # determine support regions in each layer (for upper layers) Slic3r::debugf "Detecting regions\n"; - my %layers = (); + 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 @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 @@ -525,6 +526,10 @@ sub generate_support_material { my $layer = $self->layers->[$i]; my $lower_layer = $i > 0 ? $self->layers->[$i-1] : undef; + # $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] || [] }]; + # step 1: generate support material in current layer (for upper layers) push @current_support_regions, @{ shift @queue } if @queue && $i < $#{$self->layers}; @@ -535,7 +540,10 @@ sub generate_support_material { $layers{$i} = diff_ex( [ map @$_, @current_support_regions ], - [ map @$_, map $_->offset_ex($distance_from_object), @{$layer->slices} ], + [ + (map @$_, map $_->offset_ex($distance_from_object), @{$layer->slices}), + (map @$_, @{ $layers_interfaces{$i} }), + ], ); $_->simplify($flow->scaled_spacing * 2) for @{$layers{$i}}; @@ -599,12 +607,13 @@ sub generate_support_material { Slic3r::debugf "Applying patterns\n"; { my $clip_pattern = sub { - my ($layer_id, $expolygons) = @_; + my ($layer_id, $expolygons, $height) = @_; my @paths = (); foreach my $expolygon (@$expolygons) { push @paths, map $_->pack, map { + $_->height($height); $_->flow_spacing($self->print->first_layer_support_material_flow->spacing) if $layer_id == 0; $_; @@ -616,29 +625,41 @@ sub generate_support_material { return @paths; }; my %layer_paths = (); + my %layer_interface_paths = (); + 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) ]; + return ($paths, $interface_paths); + }; Slic3r::parallelize( items => [ keys %layers ], thread_cb => sub { my $q = shift; my $paths = {}; + my $interface_paths = {}; while (defined (my $layer_id = $q->dequeue)) { - $paths->{$layer_id} = [ $clip_pattern->($layer_id, $layers{$layer_id}) ]; + ($paths->{$layer_id}, $interface_paths->{$layer_id}) = $process_layer->($layer_id); } - return $paths; + return [ $paths, $interface_paths ]; }, collect_cb => sub { my $paths = shift; - $layer_paths{$_} = $paths->{$_} for keys %$paths; + ($layer_paths{$_}, $layer_interface_paths{$_}) = @{ $paths->{$_} } for keys %$paths; }, no_threads_cb => sub { - $layer_paths{$_} = [ $clip_pattern->($_, $layers{$_}) ] for keys %layers; + ($layer_paths{$_}, $layer_interface_paths{$_}) = $process_layer->($_) for keys %layers; }, ); foreach my $layer_id (keys %layer_paths) { my $layer = $self->layers->[$layer_id]; $layer->support_fills(Slic3r::ExtrusionPath::Collection->new); + $layer->support_interface_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}}; } } } From 935173047a9196fe122694258a0dc1f1b03dfdd3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 17:23:35 +0100 Subject: [PATCH 028/172] Allow [print_center_X] and [print_center_Y] syntax for all coordinates settings (including bed_center etc.) and [temperature_0], [temperature_1] etc. for all index-based settings --- lib/Slic3r/Config.pm | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0b63f2815..67dde2bdc 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -1165,13 +1165,21 @@ sub replace_options { $string =~ s/\[version\]/$Slic3r::VERSION/eg; # build a regexp to match the available options - my $options = join '|', - grep !$Slic3r::Config::Options->{$_}{multiline}, + my @options = grep !$Slic3r::Config::Options->{$_}{multiline}, grep $self->has($_), keys %{$Slic3r::Config::Options}; + my $options_regex = join '|', @options; # use that regexp to search and replace option names with option values - $string =~ s/\[($options)\]/$self->serialize($1)/eg; + $string =~ s/\[($options_regex)\]/$self->serialize($1)/eg; + foreach my $opt_key (grep ref $self->$_ eq 'ARRAY', @options) { + my $value = $self->$opt_key; + $string =~ s/\[${opt_key}_${_}\]/$value->[$_]/eg for 0 .. $#$value; + if ($Options->{$opt_key}{type} eq 'point') { + $string =~ s/\[${opt_key}_X\]/$value->[0]/eg; + $string =~ s/\[${opt_key}_Y\]/$value->[1]/eg; + } + } return $string; } From ecd054a57c7ef23eeaa9747b405d3c8b68a4cdbb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 17:29:01 +0100 Subject: [PATCH 029/172] Don't emit T0 at the beginning of the file, so that it's generated automatically just right before starting the print --- lib/Slic3r/Print.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8053dfd2e..8cf66cc8a 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -648,7 +648,6 @@ sub write_gcode { ); my $min_print_speed = 60 * $Slic3r::Config->min_print_speed; my $dec = $gcodegen->dec; - print $fh $gcodegen->set_extruder($self->extruders->[0]); print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers; # write start commands to file From c57e94c06590f212e9fe75318728108efa9d024e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 19:15:34 +0100 Subject: [PATCH 030/172] =?UTF-8?q?Experimental=20feature:=20make=20a=20li?= =?UTF-8?q?ttle=20move=20inwards=20by=2045=C2=B0=20after=20finishing=20the?= =?UTF-8?q?=20external=20perimeter=20and=20before=20retracting.=20#186?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Slic3r/GCode.pm | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 6c21e5721..c1bb3e0dd 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -3,7 +3,7 @@ use Moo; use List::Util qw(first); use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y); +use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y A B); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); has 'layer' => (is => 'rw'); # this is not very correct, we should replace it with explicit layer_id and avoid using $self->layer->flow at all here because it's too general @@ -20,6 +20,7 @@ has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); has 'last_speed' => (is => 'rw', default => sub {""}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); +has 'last_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); # calculate speeds (mm/min) @@ -142,6 +143,16 @@ sub extrude_path { my $travel = Slic3r::Line->new($self->last_pos, $path->points->[0]); if ($travel->length >= scale $self->extruder->retract_before_travel) { if (!$Slic3r::Config->only_retract_when_crossing_perimeters || $path->role != EXTR_ROLE_FILL || !first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { + if ($self->last_path && $self->last_path->role == &EXTR_ROLE_EXTERNAL_PERIMETER) { + my @lines = $self->last_path->lines; + my $last_line = $lines[-1]; + if (points_coincide($last_line->[B], $self->last_pos)) { + my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $self->layer->flow->spacing); + bless $point, 'Slic3r::Point'; + $point->rotate(PI/4, $last_line->[B]); + $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); + } + } $gcode .= $self->retract(travel_to => $path->points->[0]); } } @@ -192,6 +203,8 @@ sub extrude_path { $self->elapsed_time($self->elapsed_time + $path_time); } + $self->last_path($path); + return $gcode; } From 8da4151c6d911e7a3772a0784939e6a7016a9973 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 19:24:24 +0100 Subject: [PATCH 031/172] Fix regression causing skirt to be printed at Z = 0 after a recent commit --- lib/Slic3r/GCode.pm | 6 +++--- lib/Slic3r/Print.pm | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index c1bb3e0dd..dde29a5f5 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -9,7 +9,7 @@ has 'multiple_extruders' => (is => 'ro', default => sub {0} ); has 'layer' => (is => 'rw'); # this is not very correct, we should replace it with explicit layer_id and avoid using $self->layer->flow at all here because it's too general has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); -has 'z' => (is => 'rw', default => sub {0} ); +has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); has 'extruder' => (is => 'rw'); @@ -76,7 +76,7 @@ sub move_z { my $gcode = ""; $gcode .= $self->retract(move_z => $z); $gcode .= $self->G0(undef, $z, 0, $comment || 'move to next layer (' . $self->layer->id . ')') - if $self->z != $z && !$self->lifted; + if (!defined $self->z || $self->z != $z) && !$self->lifted; return $gcode; } @@ -319,7 +319,7 @@ sub _G0_G1 { ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** $self->last_pos($point); } - if (defined $z && $z != $self->z) { + if (defined $z && (!defined $self->z || $z != $self->z)) { $self->z($z); $gcode .= sprintf " Z%.${dec}f", $z; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8cf66cc8a..023a649fc 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -709,6 +709,8 @@ sub write_gcode { if ($skirt_done < $Slic3r::Config->skirt_height) { $gcodegen->shift_x($shift[X]); $gcodegen->shift_y($shift[Y]); + $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) { @@ -723,6 +725,7 @@ sub write_gcode { # extrude brim if ($layer_id == 0 && !$brim_done) { + $gcode .= $gcodegen->move_z($gcodegen->layer->print_z); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); $gcodegen->shift_x($shift[X]); $gcodegen->shift_y($shift[Y]); From 1ecadc10fb94b0b15efc7ebc87387995f9074479 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 28 Oct 2012 19:27:54 +0100 Subject: [PATCH 032/172] Fixed regression causing one perimeter to be generated even when perimeters where set to 0. #759 --- 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 d57105a48..0fbd1f495 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -196,7 +196,7 @@ 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]]; + push @perimeters, [[@last_offsets]] if $loop_number > 0; for (my $loop = 1; $loop < $loop_number; $loop++) { # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); From f2389682b6e5a50fd3bea2a48dbb09f3266eb8eb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 00:23:33 +0100 Subject: [PATCH 033/172] Factor out some hard-coded settings --- lib/Slic3r.pm | 2 ++ lib/Slic3r/Fill/Concentric.pm | 2 +- lib/Slic3r/Fill/Honeycomb.pm | 2 +- lib/Slic3r/Fill/Rectilinear.pm | 2 +- lib/Slic3r/GCode.pm | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 410e0b0b0..1b4c57069 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -60,6 +60,8 @@ use constant SCALING_FACTOR => 0.000001; use constant RESOLUTION => 0.01; use constant OVERLAP_FACTOR => 0.5; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; +use constant LOOP_CLIPPING_LENGTH_OVER_WIDTH => 0.15; +use constant PERIMETER_INFILL_OVERLAP_OVER_WIDTH => 0.4; # The following variables hold the objects used throughout the slicing # process. They should belong to the Print object, but we are keeping diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index 2a7c7823b..b66f54bb6 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -63,7 +63,7 @@ sub fill_surface { my $path = $loop->split_at_index($index); # clip the path to avoid the extruder to get exactly on the first point of the loop - $path->clip_end(($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * 0.15); + $path->clip_end(($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); push @paths, $path->points if @{$path->points}; } diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 66fbd7ebb..b635062c5 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -21,7 +21,7 @@ sub fill_surface { # infill math my $min_spacing = scale $params{flow_spacing}; my $distance = $min_spacing / $params{density}; - my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * 0.4; + my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_WIDTH; my $cache_id = sprintf "d%s_s%s_a%s", $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 223b8ca4b..eceed4ef3 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -31,7 +31,7 @@ sub fill_surface { $flow_spacing = unscale $distance_between_lines; } - my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * 0.4; + my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_WIDTH; my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index dde29a5f5..4bea8ddda 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -112,7 +112,7 @@ sub extrude_loop { # clip the path to avoid the extruder to get exactly on the first point of the loop; # if polyline was shorter than the clipping distance we'd get a null polyline, so # we discard it in that case - $extrusion_path->clip_end($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width * 0.15); + $extrusion_path->clip_end($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); return '' if !@{$extrusion_path->polyline}; # extrude along the path From c25e54a830013ffb3948de2ff4cf35b7a9fe1e10 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 00:31:25 +0100 Subject: [PATCH 034/172] Expose perimeter/infill/support material extrusion widths in G-code comments --- lib/Slic3r/Print.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 023a649fc..312cb3d44 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -637,8 +637,11 @@ sub write_gcode { for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { printf $fh "; %s = %s\n", $_, $Slic3r::Config->$_->[0]; } - printf $fh "; single wall width = %.2fmm\n", $Slic3r::flow->width; - printf $fh "; first layer single wall width = %.2fmm\n", $Slic3r::first_layer_flow->width + printf $fh "; perimeters extrusion width = %.2fmm\n", $self->regions->[0]->flows->{perimeter}->width; + 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; print $fh "\n"; From e9ae62a9d156af5a675d41e8fc3f96431181f0ce Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 11:17:57 +0100 Subject: [PATCH 035/172] New --min-skirt-length option. #269 --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 9 +++++++++ lib/Slic3r/Extruder.pm | 6 ++++++ lib/Slic3r/GUI/Tab.pm | 2 +- lib/Slic3r/Print.pm | 34 +++++++++++++++++++++++++++++----- slic3r.pl | 2 ++ 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 21093eeb5..3d708d722 100644 --- a/README.markdown +++ b/README.markdown @@ -227,6 +227,8 @@ The author of the Silk icon set is Mark James. --skirt-distance Distance in mm between innermost skirt and object (default: 6) --skirt-height Height of skirts to draw (expressed in layers, 0+, default: 1) + --min-skirt-length Generate no less than the number of loops required to consume this length + of filament on the first layer, for each extruder (mm, 0+, default: 0) --brim-width Width of the brim that will get added to each object to help adhesion (mm, default: 0) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 67dde2bdc..4ccb6bcab 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -760,6 +760,15 @@ END type => 'i', default => 1, }, + 'min_skirt_length' => { + label => 'Minimum extrusion length', + tooltip => 'Generate no less than the number of skirt loops required to consume the specified amount of filament on the bottom layer. For multi-extruder machines, this minimum applies to each extruder.', + sidetext => 'mm', + cli => 'min-skirt-length=f', + type => 'f', + default => 0, + min => 0, + }, 'skirt_distance' => { label => 'Distance from object', tooltip => 'Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.', diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 3b77a3497..8bac9d0d9 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -55,4 +55,10 @@ sub mm3_per_mm { return $self->_mm3_per_mm_cache->{$cache_key}; } +sub e_per_mm { + my $self = shift; + my ($s, $h) = @_; + return $self->mm3_per_mm($s, $h) * $self->e_per_mm3; +} + 1; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index d130799b3..e1469053c 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -435,7 +435,7 @@ sub build { $self->add_options_page('Skirt and brim', 'box.png', optgroups => [ { title => 'Skirt', - options => [qw(skirts skirt_distance skirt_height)], + options => [qw(skirts skirt_distance skirt_height min_skirt_length)], }, { title => 'Brim', diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 312cb3d44..05eef7479 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -3,7 +3,7 @@ use Moo; use File::Basename qw(basename fileparse); use File::Spec; -use List::Util qw(max); +use List::Util qw(max first); use Math::ConvexHull::MonotoneChain qw(convex_hull); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN PI scale unscale move_points nearest_point); @@ -568,16 +568,40 @@ sub make_skirt { # find out convex hull my $convex_hull = convex_hull(\@points); + my @extruded_length = (); # for each extruder + my $spacing = $Slic3r::first_layer_flow->spacing; + my $first_layer_height = $Slic3r::Config->get_value('first_layer_height'); + my @extruders_e_per_mm = (); + my $extruder_idx = 0; + # draw outlines from outside to inside + # loop while we have less skirts than required or any extruder hasn't reached the min length if any + my $distance = scale $Slic3r::Config->skirt_distance; for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) { - my $distance = scale ($Slic3r::Config->skirt_distance + ($Slic3r::first_layer_flow->spacing * $i)); - my $outline = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND); + $distance += scale $spacing; + my $loop = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND)->[0]; push @{$self->skirt}, Slic3r::ExtrusionLoop->pack( - polygon => Slic3r::Polygon->new(@{$outline->[0]}), + polygon => Slic3r::Polygon->new(@$loop), role => EXTR_ROLE_SKIRT, - flow_spacing => $Slic3r::first_layer_flow->spacing, + flow_spacing => $spacing, ); + + if ($Slic3r::Config->min_skirt_length > 0) { + bless $loop, 'Slic3r::Polygon'; + $extruded_length[$extruder_idx] ||= 0; + $extruders_e_per_mm[$extruder_idx] ||= $self->extruders->[$extruder_idx]->e_per_mm($spacing, $first_layer_height); + $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx]; + $i++ if defined first { ($extruded_length[$_] // 0) < $Slic3r::Config->min_skirt_length } 0 .. $#{$self->extruders}; + if ($extruded_length[$extruder_idx] >= $Slic3r::Config->min_skirt_length) { + if ($extruder_idx < $#{$self->extruders}) { + $extruder_idx++; + next; + } + } + } } + + @{$self->skirt} = reverse @{$self->skirt}; } sub make_brim { diff --git a/slic3r.pl b/slic3r.pl index f0b558133..d540fcae4 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -275,6 +275,8 @@ $j --skirt-distance Distance in mm between innermost skirt and object (default: $config->{skirt_distance}) --skirt-height Height of skirts to draw (expressed in layers, 0+, default: $config->{skirt_height}) + --min-skirt-length Generate no less than the number of loops required to consume this length + of filament on the first layer, for each extruder (mm, 0+, default: $config->{min_skirt_length}) --brim-width Width of the brim that will get added to each object to help adhesion (mm, default: $config->{brim_width}) From 9b94a661d9c43715da58692ca745a4bd2bb0ca57 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 11:20:27 +0100 Subject: [PATCH 036/172] Fixed regression causing too much clipping at the endpoints of loops --- 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 4bea8ddda..1cc06fe97 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -112,7 +112,7 @@ sub extrude_loop { # clip the path to avoid the extruder to get exactly on the first point of the loop; # if polyline was shorter than the clipping distance we'd get a null polyline, so # we discard it in that case - $extrusion_path->clip_end($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); + $extrusion_path->clip_end(($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); return '' if !@{$extrusion_path->polyline}; # extrude along the path From df4dcf053483993daa9decb7c7394c0d8a60385a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 11:21:41 +0100 Subject: [PATCH 037/172] =?UTF-8?q?Move=20by=2030=C2=B0=20instead=20of=204?= =?UTF-8?q?5=C2=B0=20when=20completing=20an=20external=20loop.=20#186?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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 1cc06fe97..658fca26a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -149,7 +149,7 @@ sub extrude_path { if (points_coincide($last_line->[B], $self->last_pos)) { my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $self->layer->flow->spacing); bless $point, 'Slic3r::Point'; - $point->rotate(PI/4, $last_line->[B]); + $point->rotate(PI/6, $last_line->[B]); $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); } } From 709cbc1c15b5f90eb6484b49ebea884fce04ebd2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 19:38:40 +0100 Subject: [PATCH 038/172] Fixed regression causing support material generation to fail under threaded perls --- lib/Slic3r/Print/Object.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index dac760250..cba8dedd6 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -647,7 +647,8 @@ sub generate_support_material { }, collect_cb => sub { my $paths = shift; - ($layer_paths{$_}, $layer_interface_paths{$_}) = @{ $paths->{$_} } for keys %$paths; + $layer_paths{$_} = $paths->[0]{$_} for keys %{$paths->[0]}; + $layer_interface_paths{$_} = $paths->[1]{$_} for keys %{$paths->[1]}; }, no_threads_cb => sub { ($layer_paths{$_}, $layer_interface_paths{$_}) = $process_layer->($_) for keys %layers; From 887634e1e2174b1bee55f4a3d37056ab57c2f4b7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 29 Oct 2012 19:54:04 +0100 Subject: [PATCH 039/172] Use G92 to apply Z offset. #486 --- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/Print.pm | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 658fca26a..a127c9bfb 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -71,7 +71,7 @@ sub move_z { my $self = shift; my ($z, $comment) = @_; - $z = $Slic3r::Config->z_offset + $z * &Slic3r::SCALING_FACTOR; + $z = $z * &Slic3r::SCALING_FACTOR; my $gcode = ""; $gcode .= $self->retract(move_z => $z); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 05eef7479..e6a1990e7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -705,6 +705,14 @@ sub write_gcode { } } + # apply Z offset + if ($Slic3r::Config->z_offset > 0) { + printf $fh "G1 Z%s ; set Z offset\n", $Slic3r::Config->z_offset; + print $fh "G92 Z0 ; set Z offset\n"; + } elsif ($Slic3r::Config->z_offset < 0) { + printf $fh "G92 Z%s ; set Z offset\n", 1*(-$Slic3r::Config->z_offset); + } + # calculate X,Y shift to center print around specified origin my @print_bb = $self->bounding_box; my @shift = ( From 4c6fe0b3d495949c3dc1254900c76410fe1cf833 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 10:45:55 +0100 Subject: [PATCH 040/172] Fixed regression preventing lift to work correctly --- lib/Slic3r/GCode.pm | 16 +++++++++------- lib/Slic3r/Print.pm | 8 ++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index a127c9bfb..994e4fc7f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -53,13 +53,11 @@ my %role_speeds = ( sub change_layer { my $self = shift; - my ($layer, %params) = @_; + my ($layer) = @_; $self->layer($layer); my $gcode = ""; - $gcode .= $self->move_z($layer->print_z) unless $params{dont_move_z}; - $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" if $Slic3r::Config->layer_gcode; @@ -74,9 +72,13 @@ sub move_z { $z = $z * &Slic3r::SCALING_FACTOR; my $gcode = ""; - $gcode .= $self->retract(move_z => $z); - $gcode .= $self->G0(undef, $z, 0, $comment || 'move to next layer (' . $self->layer->id . ')') - if (!defined $self->z || $self->z != $z) && !$self->lifted; + + my $current_z = $self->z; + if (!defined $current_z || $current_z != ($z + $self->lifted)) { + $gcode .= $self->retract(move_z => $z); + $gcode .= $self->G0(undef, $z, 0, $comment || 'move to next layer (' . $self->layer->id . ')') + unless ($current_z // -1) != ($self->z // -1); + } return $gcode; } @@ -226,7 +228,7 @@ sub retract { my $retract = [undef, undef, -$length, $comment]; my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) ? undef - : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during retraction']; + : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel']; my $gcode = ""; if (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && $params{travel_to}) { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e6a1990e7..94f1d377b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -737,7 +737,7 @@ sub write_gcode { } # set new layer, but don't move Z as support material interfaces may need an intermediate one - $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id], dont_move_z => 1); + $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); $gcodegen->elapsed_time(0); # extrude skirt @@ -772,10 +772,6 @@ sub write_gcode { my ($obj_idx, $copy) = @$obj_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; - $gcodegen->shift_x($shift[X] + unscale $copy->[X]); $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); @@ -793,7 +789,7 @@ sub write_gcode { for $layer->support_fills->shortest_path($gcodegen->last_pos); } - # set actual Z + # set actual Z - this will force a retraction $gcode .= $gcodegen->move_z($layer->print_z); foreach my $region_id (0 .. ($self->regions_count-1)) { From 37637c34f5b21dc093970e217e35f0fc742ce941 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 13:59:33 +0100 Subject: [PATCH 041/172] Refactor shortest_path --- lib/Slic3r/ExtrusionPath/Collection.pm | 23 +++-------------- lib/Slic3r/Polyline.pm | 35 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index 560b63a83..9abb1faf9 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -12,26 +12,11 @@ sub shortest_path { my $self = shift; my ($start_near) = @_; - my @my_paths = map $_->unpack, @{$self->paths}; + my $collection = Slic3r::Polyline::Collection->new( + polylines => [ map $_->unpack->polyline, @{$self->paths} ], + ); - my @paths = (); - my $start_at; - my $endpoints = [ map $_->endpoints, @my_paths ]; - while (@my_paths) { - # find nearest point - my $start_index = $start_near - ? Slic3r::Geometry::nearest_point_index($start_near, $endpoints) - : 0; - - my $path_index = int($start_index/2); - if ($start_index%2) { # index is end so reverse to make it the start - $my_paths[$path_index]->reverse; - } - push @paths, splice @my_paths, $path_index, 1; - splice @$endpoints, $path_index*2, 2; - $start_near = $paths[-1]->points->[-1]; - } - return @paths; + return $collection->shortest_path($start_near, $self->paths); } sub cleanup { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index a870cd37b..3ec90b5db 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -181,4 +181,39 @@ sub scale { return $self; } +package Slic3r::Polyline::Collection; +use Moo; + +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 +sub shortest_path { + my $self = shift; + my ($start_near, $items) = @_; + + $items ||= $self->polylines; + my %items_map = map { $self->polylines->[$_] => $items->[$_] } 0 .. $#{$self->polylines}; + my @my_paths = @{$self->polylines}; + + my @paths = (); + my $start_at; + my $endpoints = [ map { $_->[0], $_->[-1] } @my_paths ]; + while (@my_paths) { + # find nearest point + my $start_index = $start_near + ? Slic3r::Geometry::nearest_point_index($start_near, $endpoints) + : 0; + + my $path_index = int($start_index/2); + if ($start_index%2) { # index is end so reverse to make it the start + $my_paths[$path_index]->reverse; + } + push @paths, splice @my_paths, $path_index, 1; + splice @$endpoints, $path_index*2, 2; + $start_near = $paths[-1][-1]; + } + return map $items_map{"$_"}, @paths; +} + 1; From be7e211bb69d9863d6141002bdc28793a01abad5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:07:01 +0100 Subject: [PATCH 042/172] Make flow_spacing required for ExtrusionPath objects --- lib/Slic3r.pm | 2 +- lib/Slic3r/Fill/Concentric.pm | 2 +- lib/Slic3r/Fill/Honeycomb.pm | 6 ++---- lib/Slic3r/Fill/Rectilinear.pm | 14 +++++++------- lib/Slic3r/GCode.pm | 8 ++++---- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 1b4c57069..f7ffc3af8 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -60,7 +60,7 @@ use constant SCALING_FACTOR => 0.000001; use constant RESOLUTION => 0.01; use constant OVERLAP_FACTOR => 0.5; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; -use constant LOOP_CLIPPING_LENGTH_OVER_WIDTH => 0.15; +use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; use constant PERIMETER_INFILL_OVERLAP_OVER_WIDTH => 0.4; # The following variables hold the objects used throughout the slicing diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index b66f54bb6..a0cf087d6 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -63,7 +63,7 @@ sub fill_surface { my $path = $loop->split_at_index($index); # clip the path to avoid the extruder to get exactly on the first point of the loop - $path->clip_end(($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); + $path->clip_end(($self->layer ? $self->layer->flow->scaled_spacing : $Slic3r::flow->scaled_spacing) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); push @paths, $path->points if @{$path->points}; } diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index b635062c5..ccbe89956 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -90,12 +90,10 @@ sub fill_surface { $self->cache->{$cache_id}, [ map @$_, $expolygon->offset_ex($overlap_distance) ], )}; - my $collection = Slic3r::ExtrusionPath::Collection->new( - paths => [ map Slic3r::ExtrusionPath->pack(polyline => $_, role => -1), @paths ], - ); return { flow_spacing => $params{flow_spacing} }, - map $_->polyline, $collection->shortest_path; + map $_->polyline, + Slic3r::Polyline::Collection->new(polylines => \@paths)->shortest_path; } 1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index eceed4ef3..37de1521c 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -59,8 +59,8 @@ sub fill_surface { # connect lines unless ($params{dont_connect}) { - my $collection = Slic3r::ExtrusionPath::Collection->new( - paths => [ map Slic3r::ExtrusionPath->new(polyline => Slic3r::Polyline->new(@$_), role => -1), @paths ], + my $collection = Slic3r::Polyline::Collection->new( + polylines => [ map Slic3r::Polyline->new(@$_), @paths ], ); @paths = (); @@ -75,17 +75,17 @@ sub fill_surface { foreach my $path ($collection->shortest_path) { if (@paths) { - my @distance = map abs($path->points->[0][$_] - $paths[-1][-1][$_]), (X,Y); + my @distance = map abs($path->[0][$_] - $paths[-1][-1][$_]), (X,Y); # TODO: we should also check that both points are on a fill_boundary to avoid # connecting paths on the boundaries of internal regions - if ($can_connect->(@distance, $paths[-1][-1], $path->points->[0]) - && $expolygon_off->encloses_line(Slic3r::Line->new($paths[-1][-1], $path->points->[0]), $tolerance)) { - push @{$paths[-1]}, @{$path->points}; + if ($can_connect->(@distance, $paths[-1][-1], $path->[0]) + && $expolygon_off->encloses_line(Slic3r::Line->new($paths[-1][-1], $path->[0]), $tolerance)) { + push @{$paths[-1]}, @$path; next; } } - push @paths, $path->points; + push @paths, $path; } } diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 994e4fc7f..095b5666a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -114,7 +114,7 @@ sub extrude_loop { # clip the path to avoid the extruder to get exactly on the first point of the loop; # if polyline was shorter than the clipping distance we'd get a null polyline, so # we discard it in that case - $extrusion_path->clip_end(($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_WIDTH); + $extrusion_path->clip_end(scale $extrusion_path->flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); return '' if !@{$extrusion_path->polyline}; # extrude along the path @@ -149,7 +149,7 @@ sub extrude_path { my @lines = $self->last_path->lines; my $last_line = $lines[-1]; if (points_coincide($last_line->[B], $self->last_pos)) { - my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $self->layer->flow->spacing); + my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $path->flow_spacing); bless $point, 'Slic3r::Point'; $point->rotate(PI/6, $last_line->[B]); $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); @@ -169,10 +169,10 @@ sub extrude_path { my $area; # mm^3 of extrudate per mm of tool movement if ($path->role == EXTR_ROLE_BRIDGE) { - my $s = $path->flow_spacing || $self->extruder->nozzle_diameter; + my $s = $path->flow_spacing; $area = ($s**2) * PI/4; } else { - my $s = $path->flow_spacing || ($self->layer ? $self->layer->flow->spacing : $Slic3r::flow->spacing); + my $s = $path->flow_spacing; my $h = $path->height // $self->layer->height; $area = $self->extruder->mm3_per_mm($s, $h); } From 52ed2c6ea4af8d9a5549c65deca6af2d16a86856 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:07:50 +0100 Subject: [PATCH 043/172] Make flow_spacing required for ExtrusionLoop objects too --- lib/Slic3r/ExtrusionLoop.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index ed89a6735..0a950eaf7 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -10,7 +10,7 @@ has 'polygon' => ( handles => [qw(is_printable nearest_point_index_to reverse)], ); -has 'flow_spacing' => (is => 'rw'); +has 'flow_spacing' => (is => 'rw', required => 1); # see EXTR_ROLE_* constants in ExtrusionPath.pm has 'role' => (is => 'rw', required => 1); From 25d88bddff1aa323de1144b7373d511b4599f018 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:11:32 +0100 Subject: [PATCH 044/172] Remove the change_layer method --- lib/Slic3r/GCode.pm | 17 ++--------------- lib/Slic3r/Print.pm | 4 +++- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 095b5666a..d9a41129a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -6,7 +6,7 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y A B); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); -has 'layer' => (is => 'rw'); # this is not very correct, we should replace it with explicit layer_id and avoid using $self->layer->flow at all here because it's too general +has 'layer' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw'); @@ -51,19 +51,6 @@ my %role_speeds = ( &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', ); -sub change_layer { - my $self = shift; - my ($layer) = @_; - - $self->layer($layer); - - my $gcode = ""; - $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" - if $Slic3r::Config->layer_gcode; - - return $gcode; -} - # this method accepts Z in scaled coordinates sub move_z { my $self = shift; @@ -76,7 +63,7 @@ sub move_z { my $current_z = $self->z; if (!defined $current_z || $current_z != ($z + $self->lifted)) { $gcode .= $self->retract(move_z => $z); - $gcode .= $self->G0(undef, $z, 0, $comment || 'move to next layer (' . $self->layer->id . ')') + $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')')) unless ($current_z // -1) != ($self->z // -1); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 94f1d377b..9100da634 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -737,8 +737,10 @@ sub write_gcode { } # set new layer, but don't move Z as support material interfaces may need an intermediate one - $gcode .= $gcodegen->change_layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); + $gcodegen->layer($self->objects->[$object_copies->[0][0]]->layers->[$layer_id]); $gcodegen->elapsed_time(0); + $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" + if $Slic3r::Config->layer_gcode; # extrude skirt if ($skirt_done < $Slic3r::Config->skirt_height) { From 0a5016a5091532c9aceedbd63b07d462f80598a9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:15:48 +0100 Subject: [PATCH 045/172] Cleaner usage of the PI constant --- lib/Slic3r/Config.pm | 2 -- lib/Slic3r/Fill/Base.pm | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 4ccb6bcab..06185ad1e 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -5,8 +5,6 @@ use utf8; use List::Util qw(first); -use constant PI => 4 * atan2(1, 1); - # cemetery of old config settings our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool); diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index dec811991..546d914ab 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -1,14 +1,13 @@ package Slic3r::Fill::Base; use Moo; +use Slic3r::Geometry qw(PI); has 'print' => (is => 'rw'); has 'layer' => (is => 'rw'); has 'max_print_dimension' => (is => 'rw'); has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle }); -use constant PI => 4 * atan2(1, 1); - sub angles () { [0, PI/2] } sub infill_direction { From 610862a97fc3d34bd5a753e8786f7a85d8fac970 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:17:30 +0100 Subject: [PATCH 046/172] Use the actual flow spacing for clipping concentric loops --- lib/Slic3r/Fill/Concentric.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index a0cf087d6..c64548e47 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -63,7 +63,7 @@ sub fill_surface { my $path = $loop->split_at_index($index); # clip the path to avoid the extruder to get exactly on the first point of the loop - $path->clip_end(($self->layer ? $self->layer->flow->scaled_spacing : $Slic3r::flow->scaled_spacing) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); + $path->clip_end(scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); push @paths, $path->points if @{$path->points}; } From 61047f0df5eb1099162cf7d18d667919dd110cc3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:21:59 +0100 Subject: [PATCH 047/172] Define perimeter/infill overlap over spacing instead of width and increase it slightly to compensate --- lib/Slic3r.pm | 2 +- lib/Slic3r/Fill/Honeycomb.pm | 2 +- lib/Slic3r/Fill/Rectilinear.pm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index f7ffc3af8..3b99ab9e5 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -61,7 +61,7 @@ use constant RESOLUTION => 0.01; use constant OVERLAP_FACTOR => 0.5; 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_WIDTH => 0.4; +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 diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index ccbe89956..6698fb8e6 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -21,7 +21,7 @@ sub fill_surface { # infill math my $min_spacing = scale $params{flow_spacing}; my $distance = $min_spacing / $params{density}; - my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_WIDTH; + my $overlap_distance = scale $params{flow_spacing} * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING; my $cache_id = sprintf "d%s_s%s_a%s", $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 37de1521c..52737cb82 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -31,7 +31,7 @@ sub fill_surface { $flow_spacing = unscale $distance_between_lines; } - my $overlap_distance = ($self->layer ? $self->layer->flow->scaled_width : $Slic3r::flow->scaled_width) * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_WIDTH; + my $overlap_distance = scale $params{flow_spacing} * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING; my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); From 5943114574ea56dea9419e6159e56c268621af8f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:25:48 +0100 Subject: [PATCH 048/172] Only take layer_id instead of layer in filler objects --- lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/Fill/Base.pm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 8e919b738..e3a447d75 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -56,7 +56,7 @@ sub make_fill { my $self = shift; my ($layer) = @_; - $_->layer($layer) for values %{$self->fillers}; + $_->layer_id($layer->id) for values %{$self->fillers}; Slic3r::debugf "Filling layer %d:\n", $layer->id; diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 546d914ab..aa68e2989 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -4,7 +4,7 @@ use Moo; use Slic3r::Geometry qw(PI); has 'print' => (is => 'rw'); -has 'layer' => (is => 'rw'); +has 'layer_id' => (is => 'rw'); has 'max_print_dimension' => (is => 'rw'); has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle }); @@ -20,9 +20,9 @@ sub infill_direction { $rotate[1] = [ $self->max_print_dimension * sqrt(2) / 2, $self->max_print_dimension * sqrt(2) / 2 ]; @shift = @{$rotate[1]}; - if ($self->layer) { + if (defined $self->layer_id) { # alternate fill direction - my $layer_num = $self->layer->id / $surface->depth_layers; + my $layer_num = $self->layer_id / $surface->depth_layers; my $angle = $self->angles->[$layer_num % @{$self->angles}]; $rotate[0] = Slic3r::Geometry::deg2rad($self->angle) + $angle if $angle; } From 6c97e588b1d1fcc6ebbf915e8e977b8a2f16a376 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 14:34:41 +0100 Subject: [PATCH 049/172] Don't require the print object in filler objects --- lib/Slic3r/Fill.pm | 8 ++++++-- lib/Slic3r/Fill/Honeycomb.pm | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index e3a447d75..0449a68b2 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -45,9 +45,13 @@ sub BUILD { sub filler { my $self = shift; my ($filler) = @_; + if (!$self->fillers->{$filler}) { - $self->fillers->{$filler} = $FillTypes{$filler}->new(print => $self->print); - $self->fillers->{$filler}->max_print_dimension($self->max_print_dimension); + my $f = $FillTypes{$filler}->new( + max_print_dimension => $self->max_print_dimension + ); + $f->bounding_box([ $self->print->bounding_box ]) if $filler->can('bounding_box'); + $self->fillers->{$filler} = $f; } return $self->fillers->{$filler}; } diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 6698fb8e6..5db50c301 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -3,7 +3,8 @@ use Moo; extends 'Slic3r::Fill::Base'; -has 'cache' => (is => 'rw', default => sub {{}}); +has 'bounding_box' => (is => 'rw'); +has 'cache' => (is => 'rw', default => sub {{}}); use Slic3r::Geometry qw(PI X1 Y1 X2 Y2 X Y scale); use Slic3r::Geometry::Clipper qw(intersection_ex); @@ -39,8 +40,8 @@ sub fill_surface { # adjust actual bounding box to the nearest multiple of our hex pattern # and align it so that it matches across layers - my $print_bounding_box = [ $self->print->bounding_box ]; - my $bounding_box = [ 0, 0, $print_bounding_box->[X2], $print_bounding_box->[Y2] ]; + $self->bounding_box([ $expolygon->bounding_box ]) if !defined $self->bounding_box; + my $bounding_box = [ 0, 0, $self->bounding_box->[X2], $self->bounding_box->[Y2] ]; { my $bb_polygon = Slic3r::Polygon->new([ [ $bounding_box->[X1], $bounding_box->[Y1] ], @@ -92,7 +93,6 @@ sub fill_surface { )}; return { flow_spacing => $params{flow_spacing} }, - map $_->polyline, Slic3r::Polyline::Collection->new(polylines => \@paths)->shortest_path; } From 20e73face2fa3910c0571efe8f56aff018e07b74 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 15:09:54 +0100 Subject: [PATCH 050/172] Get rid of max_print_dimension in filler objects --- lib/Slic3r/ExPolygon.pm | 5 +++++ lib/Slic3r/Fill.pm | 18 ++---------------- lib/Slic3r/Fill/Base.pm | 4 +--- t/fill.t | 5 +---- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 6a3e946c1..9a82ba0e7 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -144,6 +144,11 @@ sub bounding_box_polygon { ]); } +sub bounding_box_center { + my $self = shift; + return Slic3r::Geometry::bounding_box_center($self->contour); +} + sub clip_line { my $self = shift; my ($line) = @_; # line must be a Slic3r::Line object diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 0449a68b2..b44d9dcf0 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -18,7 +18,6 @@ use Slic3r::Surface ':types'; has 'print' => (is => 'ro', required => 1); -has 'max_print_dimension' => (is => 'rw'); has 'fillers' => (is => 'rw', default => sub { {} }); our %FillTypes = ( @@ -32,26 +31,13 @@ our %FillTypes = ( honeycomb => 'Slic3r::Fill::Honeycomb', ); -sub BUILD { - my $self = shift; - - my $print_size = $self->print->size; - my $max_print_dimension = ($print_size->[X] > $print_size->[Y] ? $print_size->[X] : $print_size->[Y]) * sqrt(2); - $self->max_print_dimension($max_print_dimension); - - $self->filler($_) for ('rectilinear', $Slic3r::Config->fill_pattern, $Slic3r::Config->solid_fill_pattern); -} - sub filler { my $self = shift; my ($filler) = @_; if (!$self->fillers->{$filler}) { - my $f = $FillTypes{$filler}->new( - max_print_dimension => $self->max_print_dimension - ); + my $f = $self->fillers->{$filler} = $FillTypes{$filler}->new; $f->bounding_box([ $self->print->bounding_box ]) if $filler->can('bounding_box'); - $self->fillers->{$filler} = $f; } return $self->fillers->{$filler}; } @@ -155,7 +141,7 @@ sub make_fill { next SURFACE unless $density > 0; } - my @paths = $self->fillers->{$filler}->fill_surface( + my @paths = $self->filler($filler)->fill_surface( $surface, density => $density, flow_spacing => $flow_spacing, diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index aa68e2989..93af733d4 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -3,9 +3,7 @@ use Moo; use Slic3r::Geometry qw(PI); -has 'print' => (is => 'rw'); has 'layer_id' => (is => 'rw'); -has 'max_print_dimension' => (is => 'rw'); has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle }); sub angles () { [0, PI/2] } @@ -17,7 +15,7 @@ sub infill_direction { # set infill angle my (@rotate, @shift); $rotate[0] = Slic3r::Geometry::deg2rad($self->angle); - $rotate[1] = [ $self->max_print_dimension * sqrt(2) / 2, $self->max_print_dimension * sqrt(2) / 2 ]; + $rotate[1] = $surface->expolygon->bounding_box_center; @shift = @{$rotate[1]}; if (defined $self->layer_id) { diff --git a/t/fill.t b/t/fill.t index 27b9da41d..28d3b9cb7 100644 --- a/t/fill.t +++ b/t/fill.t @@ -29,10 +29,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } } { - my $filler = Slic3r::Fill::Rectilinear->new( - print => Slic3r::Print->new, - max_print_dimension => scale 100, - ); + my $filler = Slic3r::Fill::Rectilinear->new; my $surface = Slic3r::Surface->new( surface_type => S_TYPE_TOP, expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]), From cec7bf815c47354121640f14463941a1b88bb187 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 15:29:13 +0100 Subject: [PATCH 051/172] Get rid of the ExtrusionLoop objects in concentric filler --- lib/Slic3r/ExtrusionLoop.pm | 36 ++++++++--------------------------- lib/Slic3r/ExtrusionPath.pm | 22 +-------------------- lib/Slic3r/Fill/Concentric.pm | 8 ++++---- lib/Slic3r/Polygon.pm | 27 ++++++++++++++++++++++++++ lib/Slic3r/Polyline.pm | 20 +++++++++++++++++++ 5 files changed, 60 insertions(+), 53 deletions(-) diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index 0a950eaf7..44a779207 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -37,36 +37,22 @@ sub pack { sub split_at_index { my $self = shift; - my ($index) = @_; - - my @new_points = (); - push @new_points, @{$self->polygon}[$index .. $#{$self->polygon}]; - push @new_points, @{$self->polygon}[0 .. $index]; return Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polyline->new(\@new_points), - role => $self->role, - flow_spacing => $self->flow_spacing, + polyline => $self->polygon->split_at_index(@_), + role => $self->role, + flow_spacing => $self->flow_spacing, ); } sub split_at { my $self = shift; - my ($point) = @_; - $point = Slic3r::Point->new($point); - - # find index of point - my $i = -1; - for (my $n = 0; $n <= $#{$self->polygon}; $n++) { - if (same_point($point, $self->polygon->[$n])) { - $i = $n; - last; - } - } - die "Point not found" if $i == -1; - - return $self->split_at_index($i); + return Slic3r::ExtrusionPath->new( + polyline => $self->polygon->split_at(@_), + role => $self->role, + flow_spacing => $self->flow_spacing, + ); } sub split_at_first_point { @@ -82,12 +68,6 @@ sub endpoints { return ($self->polygon->[0], $self->polygon->[-1]); } -# provided for ExtrusionPath::Collection->shortest_path() -sub points { - my $self = shift; - return $self->polygon; -} - package Slic3r::ExtrusionLoop::Packed; sub unpack { my $self = shift; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index c55cd02ae..bae6e61cd 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -15,7 +15,7 @@ use Slic3r::Geometry qw(PI X Y epsilon deg2rad rotate_points); has 'polyline' => ( is => 'rw', required => 1, - handles => [qw(merge_continuous_lines lines length reverse)], + handles => [qw(merge_continuous_lines lines length reverse clip_end)], ); # height is the vertical thickness of the extrusion expressed in mm @@ -58,26 +58,6 @@ sub pack { # no-op, this allows to use both packed and non-packed objects in Collections sub unpack { $_[0] } -sub clip_end { - my $self = shift; - my ($distance) = @_; - - while ($distance > 0) { - my $last_point = pop @{$self->points}; - last if !@{$self->points}; - - my $last_segment_length = $last_point->distance_to($self->points->[-1]); - if ($last_segment_length <= $distance) { - $distance -= $last_segment_length; - next; - } - - my $new_point = Slic3r::Geometry::point_along_segment($last_point, $self->points->[-1], $distance); - push @{$self->points}, Slic3r::Point->new($new_point); - $distance = 0; - } -} - sub clip_with_polygon { my $self = shift; my ($polygon) = @_; diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index c64548e47..fe508eb94 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -51,13 +51,13 @@ sub fill_surface { ($bounding_box->[X1] + $bounding_box->[X2]) / 2, ($bounding_box->[Y1] + $bounding_box->[Y2]) / 2, ); - foreach my $loop (map Slic3r::ExtrusionLoop->new(polygon => $_, role => EXTR_ROLE_FILL), @loops) { + foreach my $loop (@loops) { # extrude all loops ccw - $loop->polygon->make_counter_clockwise; + $loop->make_counter_clockwise; # find the point of the loop that is closest to the current extruder position my $index = $loop->nearest_point_index_to($cur_pos); - $cur_pos = $loop->polygon->[0]; + $cur_pos = $loop->[0]; # split the loop at the starting point and make a path my $path = $loop->split_at_index($index); @@ -65,7 +65,7 @@ sub fill_surface { # clip the path to avoid the extruder to get exactly on the first point of the loop $path->clip_end(scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING); - push @paths, $path->points if @{$path->points}; + push @paths, $path if @$path; } return { flow_spacing => $flow_spacing }, @paths; diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index ee6f193d9..18f840a45 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -119,4 +119,31 @@ sub is_valid { return @$self >= 3; } +sub split_at_index { + my $self = shift; + my ($index) = @_; + + return (ref $self)->new( + @$self[$index .. $#$self], + @$self[0 .. $index], + ); +} + +sub split_at { + my $self = shift; + my ($point) = @_; + + # find index of point + my $i = -1; + for (my $n = 0; $n <= $#$self; $n++) { + if (Slic3r::Geometry::same_point($point, $self->[$n])) { + $i = $n; + last; + } + } + die "Point not found" if $i == -1; + + return $self->split_at_index($i); +} + 1; \ No newline at end of file diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 3ec90b5db..0813822aa 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -181,6 +181,26 @@ sub scale { return $self; } +sub clip_end { + my $self = shift; + my ($distance) = @_; + + while ($distance > 0) { + my $last_point = pop @$self; + last if !@$self; + + my $last_segment_length = $last_point->distance_to($self->[-1]); + if ($last_segment_length <= $distance) { + $distance -= $last_segment_length; + next; + } + + my $new_point = Slic3r::Geometry::point_along_segment($last_point, $self->[-1], $distance); + push @$self, Slic3r::Point->new($new_point); + $distance = 0; + } +} + package Slic3r::Polyline::Collection; use Moo; From 44d91774a63359d6e11f4b3d7c710e988ee77fbc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 15:38:17 +0100 Subject: [PATCH 052/172] Get rid of useless Slic3r::Fill instances --- lib/Slic3r/Fill.pm | 4 ++++ lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print/Object.pm | 3 +-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index b44d9dcf0..03b0ef842 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -35,6 +35,10 @@ sub filler { my $self = shift; my ($filler) = @_; + if (!ref $self) { + return $FillTypes{$filler}->new; + } + if (!$self->fillers->{$filler}) { my $f = $self->fillers->{$filler} = $FillTypes{$filler}->new; $f->bounding_box([ $self->print->bounding_box ]) if $filler->can('bounding_box'); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 0fbd1f495..f2e34426f 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -232,7 +232,7 @@ sub make_perimeters { # fill gaps { - my $filler = Slic3r::Fill->new(print => $self->layer->object->print)->filler('rectilinear'); + my $filler = Slic3r::Fill::Rectilinear->new; my $w = $self->perimeter_flow->width; my @widths = (1.5 * $w, $w, 0.5 * $w); # worth trying 0.2 too? diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index cba8dedd6..d1e3611dc 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -570,8 +570,7 @@ sub generate_support_material { my @support_material_areas = map $_->offset_ex(- 0.5 * $flow->scaled_width), @{union_ex([ map $_->contour, map @$_, values %layers ])}; - my $fill = Slic3r::Fill->new(print => $self->print); - my $filler = $fill->filler($Slic3r::Config->support_material_pattern); + my $filler = Slic3r::Fill->filler($Slic3r::Config->support_material_pattern); $filler->angle($Slic3r::Config->support_material_angle); { my @patterns = (); From 92ab53868ebdff10c0be68f9bc7b6fddaeacd27b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 15:49:11 +0100 Subject: [PATCH 053/172] Remove unused methods --- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/Line.pm | 29 ----------------------------- lib/Slic3r/Point.pm | 14 -------------- lib/Slic3r/Polyline.pm | 15 --------------- 4 files changed, 1 insertion(+), 59 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d9a41129a..5534e7653 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -178,7 +178,7 @@ sub extrude_path { foreach my $line ($path->lines) { my $line_length = $line->length; $path_length += $line_length; - $gcode .= $self->G1($line->b, undef, $e * unscale $line_length, $description); + $gcode .= $self->G1($line->[B], undef, $e * unscale $line_length, $description); } } diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index 85dea8abe..4979189c9 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -14,14 +14,6 @@ sub new { return $self; } -sub a { $_[0][0] } -sub b { $_[0][1] } - -sub coordinates { - my $self = shift; - return ($self->a->coordinates, $self->b->coordinates); -} - sub boost_linestring { my $self = shift; return Boost::Geometry::Utils::linestring($self); @@ -35,27 +27,6 @@ sub coincides_with { || ($self->a->coincides_with($line->b) && $self->b->coincides_with($line->a)); } -sub has_endpoint { - my $self = shift; - my ($point) = @_; - return $point->coincides_with($self->a) || $point->coincides_with($self->b); -} - -sub has_segment { - my $self = shift; - my ($line) = @_; - - # a segment belongs to another segment if its points belong to it - return Slic3r::Geometry::point_in_segment($line->[0], $self) - && Slic3r::Geometry::point_in_segment($line->[1], $self); -} - -sub parallel_to { - my $self = shift; - my ($line) = @_; - return Slic3r::Geometry::lines_parallel($self, $line); -} - sub length { my $self = shift; return Slic3r::Geometry::line_length($self); diff --git a/lib/Slic3r/Point.pm b/lib/Slic3r/Point.pm index cba2f1af9..9c704c853 100644 --- a/lib/Slic3r/Point.pm +++ b/lib/Slic3r/Point.pm @@ -23,20 +23,6 @@ sub clone { return (ref $self)->new(@$self); } -sub cast { - my $class = shift; - if (ref $_[0] eq 'Slic3r::Point') { - return $_[0]; - } else { - return $class->new(@_); - } -} - -sub coordinates { - my $self = shift; - return @$self; -} - sub coincides_with { my $self = shift; my ($point) = @_; diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 0813822aa..8d7f6a434 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -40,11 +40,6 @@ sub deserialize { return $class->new(map [ $v[2*$_], $v[2*$_+1] ], 0 .. int($#v/2)); } -sub is_serialized { - my $self = shift; - return (reftype $self) eq 'SCALAR' ? 1 : 0; -} - sub lines { my $self = shift; return polyline_lines($self); @@ -105,16 +100,6 @@ sub nearest_point_index_to { return Slic3r::Geometry::nearest_point_index($point, $self); } -sub has_segment { - my $self = shift; - my ($line) = @_; - - for ($self->lines) { - return 1 if $_->has_segment($line); - } - return 0; -} - sub clip_with_polygon { my $self = shift; my ($polygon) = @_; From d4ccd39ad3cf045a5574a745b65936d1d1f9a8bf Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 30 Oct 2012 15:53:01 +0100 Subject: [PATCH 054/172] Minor code cleanup --- lib/Slic3r/GCode.pm | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 5534e7653..4579fd4bf 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -26,18 +26,14 @@ has 'dec' => (is => 'ro', default => sub { 3 } ); # calculate speeds (mm/min) has 'speeds' => ( is => 'ro', - default => sub {{ - travel => 60 * $Slic3r::Config->get_value('travel_speed'), - perimeter => 60 * $Slic3r::Config->get_value('perimeter_speed'), - small_perimeter => 60 * $Slic3r::Config->get_value('small_perimeter_speed'), - external_perimeter => 60 * $Slic3r::Config->get_value('external_perimeter_speed'), - infill => 60 * $Slic3r::Config->get_value('infill_speed'), - solid_infill => 60 * $Slic3r::Config->get_value('solid_infill_speed'), - top_solid_infill => 60 * $Slic3r::Config->get_value('top_solid_infill_speed'), - bridge => 60 * $Slic3r::Config->get_value('bridge_speed'), + default => sub {+{ + map { $_ => 60 * $Slic3r::Config->get_value("${_}_speed") } + qw(travel perimeter small_perimeter external_perimeter infill + solid_infill top_solid_infill bridge), }}, ); +# assign speeds to roles my %role_speeds = ( &EXTR_ROLE_PERIMETER => 'perimeter', &EXTR_ROLE_SMALLPERIMETER => 'small_perimeter', @@ -56,10 +52,9 @@ sub move_z { my $self = shift; my ($z, $comment) = @_; - $z = $z * &Slic3r::SCALING_FACTOR; + $z *= &Slic3r::SCALING_FACTOR; my $gcode = ""; - my $current_z = $self->z; if (!defined $current_z || $current_z != ($z + $self->lifted)) { $gcode .= $self->retract(move_z => $z); From 554d10c85476ecf69eb707c09d8e945b3b472f96 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 31 Oct 2012 19:08:29 +0100 Subject: [PATCH 055/172] Only put ExtrusionPath objects in ExtrusionPath::Collection (no ExtrusionLoop objects) --- lib/Slic3r/ExtrusionLoop.pm | 8 -------- lib/Slic3r/Layer/Region.pm | 23 +++++++++-------------- lib/Slic3r/Polygon.pm | 5 +++++ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index 44a779207..325593cb5 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -60,14 +60,6 @@ sub split_at_first_point { return $self->split_at_index(0); } -# although a loop doesn't have endpoints, this method is provided to allow -# ExtrusionLoop objects to be added to an ExtrusionPath::Collection and -# sorted by the ->shortest_path() method -sub endpoints { - my $self = shift; - return ($self->polygon->[0], $self->polygon->[-1]); -} - package Slic3r::ExtrusionLoop::Packed; sub unpack { my $self = shift; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index f2e34426f..9f88dae87 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -359,20 +359,15 @@ sub make_perimeters { } # add thin walls as perimeters - { - my @thin_paths = (); - my %properties = ( - role => EXTR_ROLE_EXTERNAL_PERIMETER, - flow_spacing => $self->perimeter_flow->spacing, - ); - for (@{ $self->thin_walls }) { - push @thin_paths, $_->isa('Slic3r::Polygon') - ? Slic3r::ExtrusionLoop->pack(polygon => $_, %properties) - : Slic3r::ExtrusionPath->pack(polyline => $_, %properties); - } - my $collection = Slic3r::ExtrusionPath::Collection->new(paths => \@thin_paths); - push @{ $self->perimeters }, $collection->shortest_path; - } + push @{ $self->perimeters }, Slic3r::ExtrusionPath::Collection->new(paths => [ + map { + Slic3r::ExtrusionPath->pack( + polyline => ($_->isa('Slic3r::Polygon') ? $_->split_at_first_point : $_), + role => EXTR_ROLE_EXTERNAL_PERIMETER, + flow_spacing => $self->perimeter_flow->spacing, + ); + } @{ $self->thin_walls } + ])->shortest_path; } sub _add_perimeter { diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 18f840a45..3210e7896 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -146,4 +146,9 @@ sub split_at { return $self->split_at_index($i); } +sub split_at_first_point { + my $self = shift; + return $self->split_at_index(0); +} + 1; \ No newline at end of file From cb4ede7f9e8bfa6c1c1b6988cf5992407bd7e423 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Nov 2012 11:34:53 +0100 Subject: [PATCH 056/172] Do not pass the Print object to SVG::output anymore --- lib/Slic3r/Fill/PlanePath.pm | 2 +- lib/Slic3r/Layer/Region.pm | 6 +++--- lib/Slic3r/Print/Object.pm | 2 +- lib/Slic3r/SVG.pm | 7 ++----- lib/Slic3r/TriangleMesh.pm | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm index d33b6e6fc..acc1e3318 100644 --- a/lib/Slic3r/Fill/PlanePath.pm +++ b/lib/Slic3r/Fill/PlanePath.pm @@ -51,7 +51,7 @@ sub fill_surface { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "fill.svg", + Slic3r::SVG::output("fill.svg", polygons => $expolygon, polylines => [map $_->p, @paths], ); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 9f88dae87..8ce27bf1d 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -129,7 +129,7 @@ sub make_surfaces { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "surfaces.svg", + Slic3r::SVG::output("surfaces.svg", polygons => [ map $_->contour, @{$self->slices} ], red_polygons => [ map $_->p, map @{$_->holes}, @{$self->slices} ], ); @@ -462,7 +462,7 @@ sub process_bridges { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "bridge_surfaces.svg", + Slic3r::SVG::output("bridge_surfaces.svg", green_polygons => [ map $_->p, @supporting_surfaces ], red_polygons => [ @$expolygon ], ); @@ -494,7 +494,7 @@ sub process_bridges { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "bridge_edges.svg", + Slic3r::SVG::output("bridge_edges.svg", polylines => [ map $_->p, @edges ], ); } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index d1e3611dc..9c5479f27 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -595,7 +595,7 @@ sub generate_support_material { if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "support_$_.svg", + Slic3r::SVG::output("support_$_.svg", polylines => [ map $_->polyline, map @$_, $support_patterns->[$_] ], polygons => [ map @$_, @support_material_areas ], ) for 0 .. $#$support_patterns; diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm index 1bfa839f6..8621614b5 100644 --- a/lib/Slic3r/SVG.pm +++ b/lib/Slic3r/SVG.pm @@ -12,10 +12,7 @@ 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( id => "endArrow", viewBox => "0 0 10 10", @@ -35,9 +32,9 @@ sub svg { } sub output { - my ($print, $filename, %things) = @_; + my ($filename, %things) = @_; - my $svg = svg($print); + my $svg = svg(); foreach my $type (qw(polygons polylines white_polygons green_polygons red_polygons red_polylines)) { if ($things{$type}) { diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index a677273b9..a2a416b55 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -240,7 +240,7 @@ sub make_loops { Slic3r::debugf " this shouldn't happen and should be further investigated\n"; if (0) { require "Slic3r/SVG.pm"; - Slic3r::SVG::output(undef, "same_point.svg", + Slic3r::SVG::output("same_point.svg", lines => [ map $_->line, grep !defined $_->[I_FACET_EDGE], @lines ], red_lines => [ map $_->line, grep defined $_->[I_FACET_EDGE], @lines ], #points => [ $self->vertices->[$point_id] ], From 37c0b45187295e336d460a64e6e059013b9f4180 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Nov 2012 11:51:52 +0100 Subject: [PATCH 057/172] Detect gaps even after the desired number of perimeters has been generated --- lib/Slic3r/Layer/Region.pm | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 8ce27bf1d..1b5e10407 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -197,7 +197,10 @@ 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; - for (my $loop = 1; $loop < $loop_number; $loop++) { + + # 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++) { # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); foreach my $expolygon (@last_offsets) { @@ -217,9 +220,9 @@ sub make_perimeters { ); push @gaps, grep $_->area >= $gap_area_threshold, @$diff; } - @last_offsets = @new_offsets; - last if !@last_offsets; + last if !@new_offsets || $loop == $loop_number; + @last_offsets = @new_offsets; push @{ $perimeters[-1] }, [@last_offsets]; } From 93a8391e186be30feac04f52f6e33f5f865a40f1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Nov 2012 11:53:18 +0100 Subject: [PATCH 058/172] Alternate gaps fill direction --- 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 1b5e10407..3438ec79e 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -235,7 +235,7 @@ sub make_perimeters { # fill gaps { - my $filler = Slic3r::Fill::Rectilinear->new; + my $filler = Slic3r::Fill::Rectilinear->new(layer_id => $self->layer->id); my $w = $self->perimeter_flow->width; my @widths = (1.5 * $w, $w, 0.5 * $w); # worth trying 0.2 too? From d67c07bd47366a4a420ec8655ec1a95680a81202 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Nov 2012 18:32:35 +0100 Subject: [PATCH 059/172] Fix typo causing incomplete honeycomb infill after recent refactoring --- lib/Slic3r/Fill.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 03b0ef842..974b8bf39 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -41,7 +41,7 @@ sub filler { if (!$self->fillers->{$filler}) { my $f = $self->fillers->{$filler} = $FillTypes{$filler}->new; - $f->bounding_box([ $self->print->bounding_box ]) if $filler->can('bounding_box'); + $f->bounding_box([ $self->print->bounding_box ]) if $f->can('bounding_box'); } return $self->fillers->{$filler}; } From 705d6b31797917a98af38bd063adc4ff3890f6d8 Mon Sep 17 00:00:00 2001 From: Mark Hindess Date: Fri, 2 Nov 2012 22:39:08 +0000 Subject: [PATCH 060/172] Quick fix for support material. --- lib/Slic3r/Print.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 9100da634..2793da940 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -781,14 +781,18 @@ sub write_gcode { # and also because we avoid travelling on other things when printing it if ($Slic3r::Config->support_material) { $gcode .= $gcodegen->move_z($layer->support_material_interface_z) - if @{ $layer->support_interface_fills->paths }; + if ($layer->support_interface_fills && @{ $layer->support_interface_fills->paths }); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); - $gcode .= $gcodegen->extrude_path($_, 'support material interface') - for $layer->support_interface_fills->shortest_path($gcodegen->last_pos); + if ($layer->support_interface_fills) { + $gcode .= $gcodegen->extrude_path($_, 'support material interface') + for $layer->support_interface_fills->shortest_path($gcodegen->last_pos); + } $gcode .= $gcodegen->move_z($layer->print_z); - $gcode .= $gcodegen->extrude_path($_, 'support material') - for $layer->support_fills->shortest_path($gcodegen->last_pos); + if ($layer->support_fills) { + $gcode .= $gcodegen->extrude_path($_, 'support material') + for $layer->support_fills->shortest_path($gcodegen->last_pos); + } } # set actual Z - this will force a retraction From 68e1edab80df32afbdb8ed07aa8c5e5853f22efe Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 00:17:46 +0100 Subject: [PATCH 061/172] Require Math::Clipper 1.14 and increase scale factor for offset(). #720 --- Build.PL | 2 +- lib/Slic3r/Geometry/Clipper.pm | 10 +++++----- lib/Slic3r/Layer/Region.pm | 5 ++--- lib/Slic3r/Polygon.pm | 2 +- lib/Slic3r/Polyline.pm | 1 - lib/Slic3r/Print.pm | 4 ++-- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Build.PL b/Build.PL index e34a56d0f..2863c6adf 100644 --- a/Build.PL +++ b/Build.PL @@ -11,7 +11,7 @@ my $build = Module::Build->new( 'File::Basename' => '0', 'File::Spec' => '0', 'Getopt::Long' => '0', - 'Math::Clipper' => '1.09', + 'Math::Clipper' => '1.14', '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 4739f15dc..0c1857a8a 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -8,20 +8,20 @@ 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.09 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); +use Math::Clipper 1.14 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); use Slic3r::Geometry qw(scale); our $clipper = Math::Clipper->new; sub safety_offset { my ($polygons, $factor) = @_; - return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 1, JT_MITER, 2); + return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100000, JT_MITER, 2); } sub offset { my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= 1; - $joinType = JT_MITER if !defined $joinType; - $miterLimit ||= 2; + $scale ||= 100000; + $joinType //= JT_MITER; + $miterLimit //= 2; my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit); return @$offsets; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 3438ec79e..9701d7a12 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -1,7 +1,6 @@ package Slic3r::Layer::Region; use Moo; -use Math::Clipper ':all'; use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale shortest_path); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); @@ -109,7 +108,7 @@ sub make_surfaces { # now detect thin walls by re-outgrowing offsetted surfaces and subtracting # them from the original slices - my $outgrown = Math::Clipper::offset([ map $_->p, @{$self->slices} ], $distance); + my $outgrown = [ Slic3r::Geometry::Clipper::offset([ map $_->p, @{$self->slices} ], $distance) ]; my $diff = diff_ex( [ map $_->p, @surfaces ], $outgrown, @@ -410,7 +409,7 @@ sub prepare_fill_surfaces { # offset inwards my @offsets = $surface->expolygon->offset_ex(-$distance); - @offsets = @{union_ex(Math::Clipper::offset([ map @$_, @offsets ], $distance, 1, JT_MITER))}; + @offsets = @{union_ex([ Slic3r::Geometry::Clipper::offset([ map @$_, @offsets ], $distance)] )}; # isn't the union_ex useless? map Slic3r::Surface->new( expolygon => $_, surface_type => $surface->surface_type, diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 3210e7896..96288f73d 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -21,7 +21,7 @@ sub boost_linestring { sub is_counter_clockwise { my $self = shift; - return Math::Clipper::is_counter_clockwise($self); + return Slic3r::Geometry::Clipper::is_counter_clockwise($self); } sub make_counter_clockwise { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 8d7f6a434..8cdecf39f 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -2,7 +2,6 @@ package Slic3r::Polyline; use strict; use warnings; -use Math::Clipper qw(); use Scalar::Util qw(reftype); use Slic3r::Geometry qw(A B X Y X1 X2 Y1 Y2 polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices polyline_lines move_points same_point); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 2793da940..6ae8bc537 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -579,7 +579,7 @@ sub make_skirt { my $distance = scale $Slic3r::Config->skirt_distance; for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) { $distance += scale $spacing; - my $loop = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND)->[0]; + my ($loop) = Slic3r::Geometry::Clipper::offset([$convex_hull], $distance, 0.0001, JT_ROUND); push @{$self->skirt}, Slic3r::ExtrusionLoop->pack( polygon => Slic3r::Polygon->new(@$loop), role => EXTR_ROLE_SKIRT, @@ -634,7 +634,7 @@ sub make_brim { polygon => Slic3r::Polygon->new($_), role => EXTR_ROLE_SKIRT, flow_spacing => $Slic3r::first_layer_flow->spacing, - ) for @{Math::Clipper::offset(\@islands, $i * $Slic3r::first_layer_flow->scaled_spacing, 100, JT_SQUARE)}; + ) for Slic3r::Geometry::Clipper::offset(\@islands, $i * $Slic3r::first_layer_flow->scaled_spacing, undef, JT_SQUARE); # TODO: we need the offset inwards/offset outwards logic to avoid overlapping extrusions } } From ab6b611123cb1af2b1afa52a02c4b974d3ea7bed Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 11:53:32 +0100 Subject: [PATCH 062/172] New post-processing script to calculate flow information from G-code --- utils/post-processing/flowrate.pl | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 utils/post-processing/flowrate.pl diff --git a/utils/post-processing/flowrate.pl b/utils/post-processing/flowrate.pl new file mode 100755 index 000000000..573597c4c --- /dev/null +++ b/utils/post-processing/flowrate.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl -i + +# +# Post-processing script for calculating flow rate for each move + +use strict; +use warnings; + +my $E = 0; +my ($X, $Y); +while (<>) { + if (/^G1 X([0-9.]+) Y([0-9.]+).*? E([0-9.]+)/) { + my ($x, $y, $e) = ($1, $2, $3); + my $e_length = $e - $E; + if ($e_length > 0 && defined $X && defined $Y) { + my $dist = sqrt( (($x-$X)**2) + (($y-$Y)**2) ); + if ($dist > 0) { + my $flowrate = sprintf '%.2f', $e_length / $dist; + s/(\R+)/ ; XY dist = $dist ; E dist = $e_length ; E\/XY = $flowrate mm\/mm$1/; + } + } + $E = $e; + $X = $x; + $Y = $y; + } + if (/^G1 X([0-9.]+) Y([0-9.]+)/) { + $X = $1; + $Y = $2; + } + if (/^G1.*? E([0-9.]+)/) { + $E = $1; + } + if (/^G92 E0/) { + $E = 0; + } + print; +} + +__END__ From 0a912c631633ac0b377613a6866a65d479b0c213 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 14:26:55 +0100 Subject: [PATCH 063/172] Only apply min_skirt_length on bottom layer --- lib/Slic3r/Print.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 6ae8bc537..5fe70bd29 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -753,7 +753,11 @@ sub write_gcode { if ($layer_id < $Slic3r::Config->skirt_height) { # distribute skirt loops across all extruders for my $i (0 .. $#{$self->skirt}) { - $gcode .= $gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]); + # when printing layers > 0 ignore 'min_skirt_length' and + # just use the 'skirts' setting; also just use the current extruder + last if ($layer_id > 0) && ($i >= $Slic3r::Config->skirts); + $gcode .= $gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]) + if $layer_id == 0; $gcode .= $gcodegen->extrude_loop($self->skirt->[$i], 'skirt'); } } From 0bff8ad576e80c5f23851736b0b953d43f543b69 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 15:59:31 +0100 Subject: [PATCH 064/172] Bugfix: some multimaterial files generated a fatal error --- lib/Slic3r/Layer.pm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 18169fd1d..0a0213a0b 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -72,12 +72,13 @@ sub region { my $self = shift; my ($region_id) = @_; - if (!defined $self->regions->[$region_id]) { - $self->regions->[$region_id] = Slic3r::Layer::Region->new( + for (my $i = @{$self->regions}; $i <= $region_id; $i++) { + $self->regions->[$i] //= Slic3r::Layer::Region->new( layer => $self, - region => $self->object->print->regions->[$region_id], + region => $self->object->print->regions->[$i], ); } + return $self->regions->[$region_id]; } From ed100c477c799317391cca12843d404ce7f0d7e4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 17:52:08 +0100 Subject: [PATCH 065/172] Releasing 0.9.4 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 3b99ab9e5..8ca07b15f 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.4-dev"; +our $VERSION = "0.9.4"; our $debug = 0; sub debugf { From 68c2ac601b75718dd70e1c9579cae359aa8b6884 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Nov 2012 23:38:55 +0100 Subject: [PATCH 066/172] 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 8ca07b15f..dc89b4eba 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.4"; +our $VERSION = "0.9.5-dev"; our $debug = 0; sub debugf { From ab2b31672f18947458abb9ce1d05466126c8e1a1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 6 Nov 2012 19:54:21 +0100 Subject: [PATCH 067/172] Fixed regression causing a long string between copies --- lib/Slic3r/GCode.pm | 10 ++++++++++ lib/Slic3r/Print.pm | 12 ++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 4579fd4bf..492fd190d 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -47,6 +47,16 @@ my %role_speeds = ( &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', ); +sub set_shift { + my $self = shift; + my @shift = @_; + + $self->shift_x($shift[X]); + $self->shift_y($shift[Y]); + + $self->last_pos->translate(map -$_, @shift); +} + # this method accepts Z in scaled coordinates sub move_z { my $self = shift; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 5fe70bd29..f342c3315 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -744,8 +744,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_extruder($self->extruders->[0]); # move_z requires extruder $gcode .= $gcodegen->move_z($gcodegen->layer->print_z); $gcode .= $gcodegen->set_acceleration($Slic3r::Config->perimeter_acceleration); @@ -768,8 +767,7 @@ sub write_gcode { if ($layer_id == 0 && !$brim_done) { $gcode .= $gcodegen->move_z($gcodegen->layer->print_z); $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); - $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; } @@ -778,8 +776,7 @@ sub write_gcode { my ($obj_idx, $copy) = @$obj_copy; my $layer = $self->objects->[$obj_idx]->layers->[$layer_id]; - $gcodegen->shift_x($shift[X] + unscale $copy->[X]); - $gcodegen->shift_y($shift[Y] + unscale $copy->[Y]); + $gcodegen->set_shift(map $shift[$_] + unscale $copy->[$_], X,Y); # 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 @@ -879,8 +876,7 @@ 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(map $shift[$_] + unscale $copy->[$_], X,Y); print $fh $gcodegen->retract; print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object'); } From 1db614a5a98a9c4c9cfd25a0359af96f9c5d5f96 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 6 Nov 2012 20:04:44 +0100 Subject: [PATCH 068/172] Revert "Use G92 to apply Z offset. #486" #778 --- lib/Slic3r/GCode.pm | 1 + lib/Slic3r/Print.pm | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 492fd190d..0deb4b03b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -63,6 +63,7 @@ sub move_z { my ($z, $comment) = @_; $z *= &Slic3r::SCALING_FACTOR; + $z += $Slic3r::Config->z_offset; my $gcode = ""; my $current_z = $self->z; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f342c3315..1bc1ad65f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -705,14 +705,6 @@ sub write_gcode { } } - # apply Z offset - if ($Slic3r::Config->z_offset > 0) { - printf $fh "G1 Z%s ; set Z offset\n", $Slic3r::Config->z_offset; - print $fh "G92 Z0 ; set Z offset\n"; - } elsif ($Slic3r::Config->z_offset < 0) { - printf $fh "G92 Z%s ; set Z offset\n", 1*(-$Slic3r::Config->z_offset); - } - # calculate X,Y shift to center print around specified origin my @print_bb = $self->bounding_box; my @shift = ( From e21c806ee952e51dcedba889d1267bb240fee59d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 6 Nov 2012 20:31:50 +0100 Subject: [PATCH 069/172] New wkt() method for debugging --- lib/Slic3r/Polygon.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 96288f73d..50805ab47 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -19,6 +19,11 @@ sub boost_linestring { return Boost::Geometry::Utils::linestring([@$self, $self->[0]]); } +sub wkt { + my $self = shift; + return sprintf "POLYGON((%s))", join ',', map "$_->[0] $_->[1]", @$self; +} + sub is_counter_clockwise { my $self = shift; return Slic3r::Geometry::Clipper::is_counter_clockwise($self); From 2ef90562ce4da0e46f3d60d0f46b9291854f8328 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 6 Nov 2012 22:16:01 +0100 Subject: [PATCH 070/172] Fixed regression causing first two layers to be infilled with parallel lines --- lib/Slic3r/Fill.pm | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 974b8bf39..f9e930109 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -50,8 +50,6 @@ sub make_fill { my $self = shift; my ($layer) = @_; - $_->layer_id($layer->id) for values %{$self->fillers}; - Slic3r::debugf "Filling layer %d:\n", $layer->id; # merge overlapping surfaces @@ -145,11 +143,16 @@ sub make_fill { next SURFACE unless $density > 0; } - my @paths = $self->filler($filler)->fill_surface( - $surface, - density => $density, - flow_spacing => $flow_spacing, - ); + my @paths; + { + my $f = $self->filler($filler); + $f->layer_id($layer->id); + @paths = $f->fill_surface( + $surface, + density => $density, + flow_spacing => $flow_spacing, + ); + } my $params = shift @paths; # save into layer From 2e3110d2e496a6b6aeeee6d13f7c475d2fdcf747 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 7 Nov 2012 15:22:32 +0100 Subject: [PATCH 071/172] Possible (or at least partial) workaround for the current Clipper segfault. #779 --- 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 0c1857a8a..f1821a053 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -19,7 +19,7 @@ sub safety_offset { sub offset { my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= 100000; + $scale ||= 10000; $joinType //= JT_MITER; $miterLimit //= 2; From 93bb2c1056e03d80fd5e447670aa5e23ebf0a23f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 7 Nov 2012 23:06:32 +0100 Subject: [PATCH 072/172] Bugfix: fatal error when using brim and no skirt. #784 --- lib/Slic3r/Print.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1bc1ad65f..84140e9ee 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -757,8 +757,8 @@ sub write_gcode { # extrude brim if ($layer_id == 0 && !$brim_done) { + $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); # move_z requires extruder $gcode .= $gcodegen->move_z($gcodegen->layer->print_z); - $gcode .= $gcodegen->set_extruder($self->extruders->[$Slic3r::Config->support_material_extruder-1]); $gcodegen->set_shift(@shift); $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim}; $brim_done = 1; From af7846923105702f1232bc818c3616a651b9af00 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 7 Nov 2012 23:07:43 +0100 Subject: [PATCH 073/172] Releasing 0.9.5 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index dc89b4eba..1388688d0 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.5-dev"; +our $VERSION = "0.9.5"; our $debug = 0; sub debugf { From 8f0e388c5bb298e084b2846671ee71975b1e25c2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 7 Nov 2012 23:22:12 +0100 Subject: [PATCH 074/172] 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 1388688d0..153295f80 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.5"; +our $VERSION = "0.9.6-dev"; our $debug = 0; sub debugf { From d3629a3a448eeb23ad288d8c20b7a7c13494a6ea Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Nov 2012 11:53:02 +0100 Subject: [PATCH 075/172] Require Math::Clipper 1.15. #779 --- 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 2863c6adf..361e1beee 100644 --- a/Build.PL +++ b/Build.PL @@ -11,7 +11,7 @@ my $build = Module::Build->new( 'File::Basename' => '0', 'File::Spec' => '0', 'Getopt::Long' => '0', - 'Math::Clipper' => '1.14', + 'Math::Clipper' => '1.15', '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 f1821a053..af696be38 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.14 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); +use Math::Clipper 1.15 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area); use Slic3r::Geometry qw(scale); our $clipper = Math::Clipper->new; From df70f6ac95c3e0e5804ea2bfc26d2e0897e1d7fb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 13 Nov 2012 11:55:42 +0100 Subject: [PATCH 076/172] Update MANIFEST --- MANIFEST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MANIFEST b/MANIFEST index 2cbf9fc2c..e188472c3 100644 --- a/MANIFEST +++ b/MANIFEST @@ -65,6 +65,8 @@ utils/file_info.pl utils/post-processing/filament-weight.pl utils/post-processing/prowl-notification.pl utils/post-processing/z-every-line.pl +utils/post-processing/decimate.pl +utils/post-processing/flowrate.pl utils/split_stl.pl utils/stl-to-amf.pl utils/zsh/functions/_slic3r From 580e36eb255629d5d0db24d49cebf9c8b9bccd86 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Nov 2012 10:09:29 +0100 Subject: [PATCH 077/172] Raise offset scale factor back to 100000 now that Clipper was updated --- 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 af696be38..3382b9319 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -19,7 +19,7 @@ sub safety_offset { sub offset { my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; - $scale ||= 10000; + $scale ||= 100000; $joinType //= JT_MITER; $miterLimit //= 2; From a5a0f32e1970734ba51a3075ddd7d464771408ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 10:07:30 +0100 Subject: [PATCH 078/172] Expose all SLIC3R_* environment variables as placeholders. #781 --- lib/Slic3r/Config.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 06185ad1e..0bf7d7e95 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -1156,9 +1156,11 @@ sub replace_options { my $self = shift; my ($string, $more_variables) = @_; - if ($more_variables) { - my $variables = join '|', keys %$more_variables; - $string =~ s/\[($variables)\]/$more_variables->{$1}/eg; + $more_variables ||= {}; + $more_variables->{$_} = $ENV{$_} for grep /^SLIC3R_/, keys %ENV; + { + my $variables_regex = join '|', keys %$more_variables; + $string =~ s/\[($variables_regex)\]/$more_variables->{$1}/eg; } my @lt = localtime; $lt[5] += 1900; $lt[4] += 1; From b625c3b2b26ae364b976be05a068fe34cea0e51b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 10:47:42 +0100 Subject: [PATCH 079/172] Simplify gaps fills to avoid some shaking --- lib/Slic3r/Layer/Region.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 9701d7a12..69226b2f6 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -278,7 +278,11 @@ sub make_perimeters { my $params = shift @paths; push @{ $self->thin_fills }, - map Slic3r::ExtrusionPath->pack( + map { + $_->polyline->simplify($flow->scaled_width / 3); + $_->pack; + } + map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), role => EXTR_ROLE_SOLIDFILL, height => $self->height, From 06b475a4edd90907f9c7785e28f2aa2ab1ac034c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 11:05:45 +0100 Subject: [PATCH 080/172] Clone last_pos before storing it otherwise it will be translated. #786 --- 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 0deb4b03b..f45b671fa 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -312,7 +312,7 @@ sub _G0_G1 { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - $self->last_pos($point); + $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { $self->z($z); From a1a12ffe8edee329bfd785847519d4848878aa1f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 12:37:47 +0100 Subject: [PATCH 081/172] Allow setting top-solid-layers and bottom-solid-layers to 0 independently --- lib/Slic3r/Layer/Region.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 69226b2f6..e940d9ec8 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -395,8 +395,11 @@ sub prepare_fill_surfaces { # if no solid layers are requested, turn top/bottom surfaces to internal # note that this modifies $self->surfaces in place - if ($Slic3r::Config->top_solid_layers == 0 && $Slic3r::Config->bottom_solid_layers == 0) { - $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; + if ($Slic3r::Config->top_solid_layers == 0) { + $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @surfaces; + } + if ($Slic3r::Config->bottom_solid_layers == 0) { + $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @surfaces; } # if hollow object is requested, remove internal surfaces From 1c2bc94d5a5d7222538f3efffd3ee2b336a28fde Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 12:39:55 +0100 Subject: [PATCH 082/172] Bugfix: only_retract_when_crossing_perimeters might cause some missed retractions when printing multiple copies of the same object. #786 --- lib/Slic3r/GCode.pm | 6 +++++- lib/Slic3r/Line.pm | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index f45b671fa..ec0d6d929 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -135,8 +135,12 @@ sub extrude_path { # 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, $path->points->[0]); + my $travel = Slic3r::Line->new($self->last_pos->clone, $path->points->[0]->clone); if ($travel->length >= scale $self->extruder->retract_before_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->role != EXTR_ROLE_FILL || !first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) { if ($self->last_path && $self->last_path->role == &EXTR_ROLE_EXTERNAL_PERIMETER) { my @lines = $self->last_path->lines; diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index 4979189c9..58c9479d3 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -67,4 +67,12 @@ sub reverse { @$self = reverse @$self; } +sub translate { + my $self = shift; + my ($x, $y) = @_; + @$self = Slic3r::Geometry::move_points([$x, $y], @$self); + bless $_, 'Slic3r::Point' for @$self; + return $self; +} + 1; From a81377d6848a9688bba0868905522926d8952f4d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 13:18:06 +0100 Subject: [PATCH 083/172] Apply overlap factor to bridges too --- lib/Slic3r/Fill.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index f9e930109..3683b06d9 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 scale shortest_path); +use Slic3r::Geometry qw(X Y PI scale shortest_path); use Slic3r::Geometry::Clipper qw(union_ex diff_ex); use Slic3r::Surface ':types'; @@ -135,7 +135,8 @@ sub make_fill { $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = sqrt($Slic3r::Config->bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); + my $width = sqrt($Slic3r::Config->bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); + $flow_spacing = $width + &Slic3r::OVERLAP_FACTOR * ($width * PI / 4 - $width); # this should be moved to Flow.pm } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } From 86f5cb86546ee05888db0af7a6b943901a79cd40 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 18:10:09 +0100 Subject: [PATCH 084/172] Previous commit about bridge spacing was incomplete --- lib/Slic3r/Fill.pm | 1 + lib/Slic3r/Fill/Concentric.pm | 2 +- lib/Slic3r/Fill/Rectilinear.pm | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 3683b06d9..e578b5494 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -152,6 +152,7 @@ sub make_fill { $surface, density => $density, flow_spacing => $flow_spacing, + dont_adjust => $is_bridge, ); } my $params = shift @paths; diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index fe508eb94..d31d39025 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -19,7 +19,7 @@ sub fill_surface { my $distance = $min_spacing / $params{density}; my $flow_spacing = $params{flow_spacing}; - if ($params{density} == 1) { + if ($params{density} == 1 && !$params{dont_adjust) { $distance = $self->adjust_solid_spacing( width => $bounding_box->[X2] - $bounding_box->[X1], distance => $distance, diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 52737cb82..ea9f1ddb0 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -23,7 +23,7 @@ sub fill_surface { my $line_oscillation = $distance_between_lines - $min_spacing; my $flow_spacing = $params{flow_spacing}; - if ($params{density} == 1) { + if ($params{density} == 1 && !$params{dont_adjust}) { $distance_between_lines = $self->adjust_solid_spacing( width => $bounding_box->[X2] - $bounding_box->[X1], distance => $distance_between_lines, From e13fe1d636ec3a0cf3eb343536dd4db6ab48167c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 18:11:06 +0100 Subject: [PATCH 085/172] Raise overlap factor to 1 >:-) --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 153295f80..be105ed10 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -58,7 +58,7 @@ eval "use Slic3r::Build"; use constant SCALING_FACTOR => 0.000001; use constant RESOLUTION => 0.01; -use constant OVERLAP_FACTOR => 0.5; +use constant OVERLAP_FACTOR => 1; 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; From aff0898f35951970bb58fe8cd5bca0b5ba04b217 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 18:24:09 +0100 Subject: [PATCH 086/172] Typo --- lib/Slic3r/Fill/Concentric.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index d31d39025..0f1cce8d8 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -19,7 +19,7 @@ sub fill_surface { my $distance = $min_spacing / $params{density}; my $flow_spacing = $params{flow_spacing}; - if ($params{density} == 1 && !$params{dont_adjust) { + if ($params{density} == 1 && !$params{dont_adjust}) { $distance = $self->adjust_solid_spacing( width => $bounding_box->[X2] - $bounding_box->[X1], distance => $distance, From ac5be309e398e29c8382da5c0c6e26da3163bf62 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 19:20:46 +0100 Subject: [PATCH 087/172] Do not call merge_continuous_lines() anymore --- lib/Slic3r/GCode.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index ec0d6d929..f725ee79d 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -119,7 +119,6 @@ sub extrude_path { my ($path, $description, $recursive) = @_; $path = $path->unpack if $path->isa('Slic3r::ExtrusionPath::Packed'); - $path->merge_continuous_lines; # detect arcs if ($Slic3r::Config->gcode_arcs && !$recursive) { From ed50aefa71880767556c7e83daec3d51454b69e4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 22:08:14 +0100 Subject: [PATCH 088/172] Remove unused code --- lib/Slic3r/Format/STL.pm | 62 ---------------------------------------- 1 file changed, 62 deletions(-) diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 5325a4ea6..0ac21fbc1 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -47,71 +47,9 @@ sub read_file { my $point_id = join ',', @{$facets->[$f][$_]}; if (exists $vertices_map{$point_id}) { $facets->[$f][$_] = $vertices_map{$point_id}; - ### push @{$vertices_facets[$facets->[$f][$_]]}, $f; } else { push @$vertices, $facets->[$f][$_]; $facets->[$f][$_] = $vertices_map{$point_id} = $#$vertices; - ### $vertices_facets[$#$vertices] = [$f]; - } - } - } - - # The following loop checks that @vertices_facets only groups facets that - # are really connected together (i.e. neighbors or sharing neighbors); - # in other words it takes care of multiple vertices occupying the same - # point in space. It enforces topological correctness which is needed by - # the slicing algorithm. - # I'm keeping it disabled until I find a good test case. - # The two lines above commented out with '###' need to be - # uncommented for this to work. - if (0) { - my $vertices_count = $#$vertices; # store it to avoid processing newly created vertices - for (my $v = 0; $v <= $vertices_count; $v++) { - my $more_than_one_vertex_in_this_point = 0; - while (@{$vertices_facets[$v]}) { - my @facets_indexes = @{$vertices_facets[$v]}; - @{$vertices_facets[$v]} = (); - - my @this_f = shift @facets_indexes; - CYCLE: while (@facets_indexes && @this_f) { - - # look for a facet that is connected to $this_f[-1] and whose common line contains $v - my @other_vertices_indexes = grep $_ != $v, @{$facets->[$this_f[-1]]}[-3..-1]; - - OTHER: for my $other_f (@facets_indexes) { - # facet is connected if it shares one more point - for (grep $_ != $v, @{$facets->[$other_f]}[-3..-1]) { - if ($_ ~~ @other_vertices_indexes) { - #printf "facet %d is connected to $other_f (sharing vertices $v and $_)\n", $this_f[-1]; - - # TODO: we should ensure that the common edge has a different orientation - # for each of the two adjacent facets - - push @this_f, $other_f; - @facets_indexes = grep $_ != $other_f, @facets_indexes; - next CYCLE; - } - } - } - # if we're here, then we couldn't find any facet connected to $this_f[-1] - # so we should move this one to a different cluster (that is, a new vertex) - # (or ignore it if it turns to be a non-manifold facet) - if (@this_f > 1) { - push @{$vertices_facets[$v]}, $this_f[-1]; - pop @this_f; - $more_than_one_vertex_in_this_point++; - } else { - last CYCLE; - } - } - - if ($more_than_one_vertex_in_this_point) { - Slic3r::debugf " more than one vertex in the same point\n"; - push @$vertices, $vertices->[$v]; - for my $f (@this_f) { - $facets->[$f][$_] = $#$vertices for grep $facets->[$f][$_] == $v, -3..-1; - } - } } } } From 7310cec0873a0ae9e41704226fa795cd0384d1d0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 22:09:32 +0100 Subject: [PATCH 089/172] Remove one more unused line --- lib/Slic3r/Format/STL.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 0ac21fbc1..6e9665185 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -41,7 +41,6 @@ sub read_file { my $vertices = []; { my %vertices_map = (); # given a vertex's coordinates, what's its index? - my @vertices_facets = (); # given a vertex index, what are the indexes of its tangent facets? for (my $f = 0; $f <= $#$facets; $f++) { for (-3..-1) { my $point_id = join ',', @{$facets->[$f][$_]}; From 8d557bee8cfb21841446a323a015e225e8e204fe Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Nov 2012 22:41:54 +0100 Subject: [PATCH 090/172] Bugfix: AMF export was not working anymore. #799 --- 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 1d9d32001..5a13a6e42 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -705,7 +705,7 @@ sub make_model { material_id => $volume->material_id, facets => $volume->facets, ); - $model->materials->{$volume->material_id || 0} ||= {}; + $model->set_material($volume->material_id || 0, {}); } $new_model_object->scale($plater_object->scale); $new_model_object->add_instance( From 96dd106f61d3dd683fc1acfc19e1fbc25af0204c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Nov 2012 10:40:15 +0100 Subject: [PATCH 091/172] Initial work for limiting vibrations --- lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/GUI/Tab.pm | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0bf7d7e95..6969633f8 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -410,6 +410,14 @@ our $Options = { type => 'f', default => 1, }, + 'vibration_limit' => { + label => 'Vibration limit', + tooltip => 'This experimental option will slow down those parts hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance.', + sidetext => 'Hz', + cli => 'vibration-limit=f', + type => 'f', + default => 15, + }, # print options 'perimeters' => { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index e1469053c..b9c445c59 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -634,6 +634,10 @@ sub build { }, ], }, + { + title => 'Advanced', + options => [qw(vibration_limit)], + }, ]); $self->add_options_page('Custom G-code', 'cog.png', optgroups => [ From 600e951fd83a422d9b64a6dfa18d697653da25c8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Nov 2012 12:08:19 +0100 Subject: [PATCH 092/172] Completed vibration limit --- README.markdown | 3 ++- lib/Slic3r/Config.pm | 2 +- lib/Slic3r/GCode.pm | 44 ++++++++++++++++++++++++++++++++++++++------ lib/Slic3r/Line.pm | 5 +++++ slic3r.pl | 1 + 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 3d708d722..4fa28d7a3 100644 --- a/README.markdown +++ b/README.markdown @@ -263,7 +263,8 @@ The author of the Silk icon set is Mark James. --support-material-extrusion-width Set a different extrusion width for support material --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1) - + --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: 15) + Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement (can be specified multiple times, default: 0x0) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 6969633f8..49542067b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -412,7 +412,7 @@ our $Options = { }, 'vibration_limit' => { label => 'Vibration limit', - tooltip => 'This experimental option will slow down those parts hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance.', + tooltip => 'This experimental option will slow down those moves hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance. Set zero to disable.', sidetext => 'Hz', cli => 'vibration-limit=f', type => 'f', diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index f725ee79d..8be61946d 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -180,23 +180,55 @@ sub extrude_path { $self->speed( $role_speeds{$path->role} || die "Unknown role: " . $path->role ); my $path_length = 0; if ($path->isa('Slic3r::ExtrusionPath::Arc')) { - $path_length = $path->length; + $path_length = unscale $path->length; $gcode .= $self->G2_G3($path->points->[-1], $path->orientation, $path->center, $e * unscale $path_length, $description); } else { + my @moves = (); + my @last_moves = (); + my $speed_mms = $self->speeds->{$self->speed} / 60; foreach my $line ($path->lines) { - my $line_length = $line->length; + my $line_length = unscale $line->length; $path_length += $line_length; - $gcode .= $self->G1($line->[B], undef, $e * unscale $line_length, $description); + + # apply frequency limit + # http://hydraraptor.blogspot.it/2010/12/frequency-limit.html + if ($Slic3r::Config->vibration_limit && @{ $path->polyline } >= 4) { # optimization: resonance isn't triggered with less than three moves + my $freq = $speed_mms / $line_length; + if ($freq >= $Slic3r::Config->vibration_limit) { + my $vector = $line->vector; + if (@last_moves >= 1) { + if ($vector->[B][X] * $last_moves[-1][B][X] > 0 && $vector->[B][Y] * $last_moves[-1][B][Y] > 0) { + # if both X and Y have the same direction (sign), reset the buffer as there's no shaking + @last_moves = (); + } + } + push @last_moves, $vector; + if (@last_moves >= 3) { + my ($shortest_length) = reverse sort map $_->length, @last_moves; + my $speed = $Slic3r::Config->vibration_limit * unscale $shortest_length; + ###Slic3r::debugf "Reducing speed to %s mm/s (%s Hz vibration detected)\n", $speed, $freq; + $self->speed($speed * 60); + } + } else { + @last_moves = (); + } + } + + push @moves, [ $line->[B], $e * unscale $line_length ]; + } + + foreach my $move (@moves) { + $gcode .= $self->G1($move->[0], undef, $move->[1], $description); } } if ($Slic3r::Config->cooling) { - my $path_time = unscale($path_length) / $self->speeds->{$self->last_speed} * 60; + my $path_time = $path_length / $self->speeds->{$self->last_speed} * 60; if ($self->layer->id == 0) { $path_time = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ ? $path_time / ($1/100) - : unscale($path_length) / $Slic3r::Config->first_layer_speed * 60; + : $path_length / $Slic3r::Config->first_layer_speed * 60; } $self->elapsed_time($self->elapsed_time + $path_time); } @@ -366,7 +398,7 @@ sub _Gx { # apply the speed reduction for print moves on bottom layer my $speed_f = $speed eq 'retract' ? ($self->extruder->retract_speed_mm_min) - : $self->speeds->{$speed}; + : $self->speeds->{$speed} // $speed; if ($e && $self->layer && $self->layer->id == 0 && $comment !~ /retract/) { $speed_f = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ ? ($speed_f * $1/100) diff --git a/lib/Slic3r/Line.pm b/lib/Slic3r/Line.pm index 58c9479d3..d0d0a3da7 100644 --- a/lib/Slic3r/Line.pm +++ b/lib/Slic3r/Line.pm @@ -32,6 +32,11 @@ sub length { return Slic3r::Geometry::line_length($self); } +sub vector { + my $self = shift; + return (ref $self)->new([0,0], [map $self->[B][$_] - $self->[A][$_], X,Y]); +} + sub atan { my $self = shift; return Slic3r::Geometry::line_atan($self); diff --git a/slic3r.pl b/slic3r.pl index d540fcae4..6e011ec88 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -311,6 +311,7 @@ $j --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}) + --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: $config->{vibration_limit}) Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement From 72007c4f6a2d8f5a9f04be5fce1cab3eeb0ef50c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Nov 2012 18:07:13 +0100 Subject: [PATCH 093/172] Rewrite frequency limiting code --- lib/Slic3r/GCode.pm | 134 +++++++++++++++++++++++++++++++++----------- lib/Slic3r/Print.pm | 4 +- 2 files changed, 104 insertions(+), 34 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 8be61946d..9b4de6aba 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -184,42 +184,10 @@ sub extrude_path { $gcode .= $self->G2_G3($path->points->[-1], $path->orientation, $path->center, $e * unscale $path_length, $description); } else { - my @moves = (); - my @last_moves = (); - my $speed_mms = $self->speeds->{$self->speed} / 60; foreach my $line ($path->lines) { my $line_length = unscale $line->length; $path_length += $line_length; - - # apply frequency limit - # http://hydraraptor.blogspot.it/2010/12/frequency-limit.html - if ($Slic3r::Config->vibration_limit && @{ $path->polyline } >= 4) { # optimization: resonance isn't triggered with less than three moves - my $freq = $speed_mms / $line_length; - if ($freq >= $Slic3r::Config->vibration_limit) { - my $vector = $line->vector; - if (@last_moves >= 1) { - if ($vector->[B][X] * $last_moves[-1][B][X] > 0 && $vector->[B][Y] * $last_moves[-1][B][Y] > 0) { - # if both X and Y have the same direction (sign), reset the buffer as there's no shaking - @last_moves = (); - } - } - push @last_moves, $vector; - if (@last_moves >= 3) { - my ($shortest_length) = reverse sort map $_->length, @last_moves; - my $speed = $Slic3r::Config->vibration_limit * unscale $shortest_length; - ###Slic3r::debugf "Reducing speed to %s mm/s (%s Hz vibration detected)\n", $speed, $freq; - $self->speed($speed * 60); - } - } else { - @last_moves = (); - } - } - - push @moves, [ $line->[B], $e * unscale $line_length ]; - } - - foreach my $move (@moves) { - $gcode .= $self->G1($move->[0], undef, $move->[1], $description); + $gcode .= $self->G1($line->[B], undef, $e * $line_length, $description); } } @@ -500,4 +468,104 @@ sub set_bed_temperature { return $gcode; } +# http://hydraraptor.blogspot.it/2010/12/frequency-limit.html +sub limit_frequency { + my $self = shift; + my ($gcode) = @_; + + return $gcode if $Slic3r::Config->vibration_limit == 0; + + my $current_gcode = $gcode; + $gcode = ''; + my ($X, $Y, $F); + my @last_moves = (); + my $longest_move; + my $buffer = ''; + my $vibration_limit_min = $Slic3r::Config->vibration_limit * 60; + + my $flush_buffer = sub { + my ($line) = @_; + + if (@last_moves >= 3) { + $buffer =~ s/ F[0-9.]+//g; + my $new_speed = $vibration_limit_min * $longest_move; + $gcode .= "G1 F$new_speed ; limit vibrations\n"; + $gcode .= $buffer; + $gcode .= "G1 F$F; restore previous speed\n"; + } else { + $gcode .= $buffer; + } + $gcode .= "$line\n"; + @last_moves = (); + $buffer = ''; + }; + + my $append_to_buffer = sub { + my ($line, $move, $freq) = @_; + + ###printf "move x = %s, y = %s\n", map $_ // '/', @$move[X,Y]; + ###printf " freq x = %s, y = %s\n", map $_ // '/', @$freq[X,Y]; + + $buffer .= "$line\n"; + push @last_moves, [ map $freq->[$_] ? $_ : undef, @$move ]; + for (grep defined $freq->[$_], X,Y) { + $longest_move = abs($move->[$_]) if $move->[$_] && (!defined $longest_move || abs($move->[$_]) > $longest_move); + } + }; + + foreach my $line (split /\n/, $current_gcode) { + if ($line =~ /^G[01] /) { + my ($x, $y, $f); + $x = $1 if $line =~ /X([0-9.]+)/; + $y = $1 if $line =~ /Y([0-9.]+)/; + $f = $1 if $line =~ /F([0-9.]+)/; + + # calculate the move vector + my @move = ( + (defined $x && $X) ? ($x - $X) : 0, + (defined $y && $Y) ? ($x - $Y) : 0, + ); + + # calculate the frequency (how many times the move can happen in one minute) for each axis + # and only keep it for the axes exceeding the configured limit. + # (undef = infinite) + my @freq = (); + for (X,Y) { + next if !$move[$_]; + my $freq = ($f // $F) / abs($move[$_]); + $freq[$_] = $freq if $freq >= $vibration_limit_min; + } + + # does our move exceed the limit? + if (grep defined $_, @freq) { + # if so, do we have a buffer already? + if (@last_moves) { + # if we have a buffer, compare the direction (for each axis) with the previous one. + if (($move[X] // 0) * ($last_moves[-1][X] // 0) < 0 || ($move[Y] // 0) * ($last_moves[-1][Y] // 0) < 0 && ($f // $F) == $F) { + # this move has opposite direction on at least one axis, and has also same speed: + # we can add it to the buffer + $append_to_buffer->($line, \@move, \@freq); + } else { + $flush_buffer->($line); + } + } else { + # if we have no buffer, store this move inside it + $append_to_buffer->($line, \@move, \@freq); + } + } else { + # if the move does not exceed any limit, flush the buffer and output this line + $flush_buffer->($line); + } + + $X = $x if defined $x; + $Y = $y if defined $y; + $F = $f if defined $f; + } else { + $gcode .= "$line\n"; + } + } + + return $gcode; +} + 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 84140e9ee..dd877f9c5 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -3,7 +3,7 @@ use Moo; use File::Basename qw(basename fileparse); use File::Spec; -use List::Util qw(max first); +use List::Util qw(min max first); use Math::ConvexHull::MonotoneChain qw(convex_hull); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN PI scale unscale move_points nearest_point); @@ -851,6 +851,8 @@ sub write_gcode { $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; } + $gcode = $gcodegen->limit_frequency($gcode); + return $gcode; }; From f3164594eb2883d9d9cb2ce3bee4dd1455a45b60 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 11:33:53 +0100 Subject: [PATCH 094/172] More incomplete work --- lib/Slic3r/GCode.pm | 121 +++++++++++++++++++------------------------- lib/Slic3r/Print.pm | 2 +- 2 files changed, 53 insertions(+), 70 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 9b4de6aba..fca6fc8ea 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -1,7 +1,7 @@ package Slic3r::GCode; use Moo; -use List::Util qw(first); +use List::Util qw(min first); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y A B); @@ -477,94 +477,77 @@ sub limit_frequency { my $current_gcode = $gcode; $gcode = ''; - my ($X, $Y, $F); - my @last_moves = (); - my $longest_move; - my $buffer = ''; - my $vibration_limit_min = $Slic3r::Config->vibration_limit * 60; + my %last; + my $F; - my $flush_buffer = sub { - my ($line) = @_; - - if (@last_moves >= 3) { - $buffer =~ s/ F[0-9.]+//g; - my $new_speed = $vibration_limit_min * $longest_move; - $gcode .= "G1 F$new_speed ; limit vibrations\n"; - $gcode .= $buffer; - $gcode .= "G1 F$F; restore previous speed\n"; - } else { - $gcode .= $buffer; - } - $gcode .= "$line\n"; - @last_moves = (); - $buffer = ''; - }; - - my $append_to_buffer = sub { - my ($line, $move, $freq) = @_; - - ###printf "move x = %s, y = %s\n", map $_ // '/', @$move[X,Y]; - ###printf " freq x = %s, y = %s\n", map $_ // '/', @$freq[X,Y]; - - $buffer .= "$line\n"; - push @last_moves, [ map $freq->[$_] ? $_ : undef, @$move ]; - for (grep defined $freq->[$_], X,Y) { - $longest_move = abs($move->[$_]) if $move->[$_] && (!defined $longest_move || abs($move->[$_]) > $longest_move); - } - }; + my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); + my @buffer = (); + my %last_dir = (); + my %time = (); + my @axes = qw(X Y E); foreach my $line (split /\n/, $current_gcode) { if ($line =~ /^G[01] /) { - my ($x, $y, $f); - $x = $1 if $line =~ /X([0-9.]+)/; - $y = $1 if $line =~ /Y([0-9.]+)/; + my %cur; + my $f; + for (@axes) { + $cur{$_} = $1 if $line =~ /$_([0-9.]+)/; + } $f = $1 if $line =~ /F([0-9.]+)/; # calculate the move vector - my @move = ( - (defined $x && $X) ? ($x - $X) : 0, - (defined $y && $Y) ? ($x - $Y) : 0, + my %move = ( + map { $_ => (defined $cur{$_} && defined $last{$_}) ? ($cur{$_} - $last{$_}) : 0 } @axes ); - # calculate the frequency (how many times the move can happen in one minute) for each axis - # and only keep it for the axes exceeding the configured limit. - # (undef = infinite) - my @freq = (); - for (X,Y) { - next if !$move[$_]; - my $freq = ($f // $F) / abs($move[$_]); - $freq[$_] = $freq if $freq >= $vibration_limit_min; - } + # check move directions + my %dir = ( + map { $_ => ($move{$_}) ? ($move{$_} > 0 ? 1 : -1) : undef } @axes + ); - # does our move exceed the limit? - if (grep defined $_, @freq) { - # if so, do we have a buffer already? - if (@last_moves) { - # if we have a buffer, compare the direction (for each axis) with the previous one. - if (($move[X] // 0) * ($last_moves[-1][X] // 0) < 0 || ($move[Y] // 0) * ($last_moves[-1][Y] // 0) < 0 && ($f // $F) == $F) { - # this move has opposite direction on at least one axis, and has also same speed: - # we can add it to the buffer - $append_to_buffer->($line, \@move, \@freq); - } else { - $flush_buffer->($line); + my @slowdown = (); + foreach my $axis (@axes) { + # are we changing direction on this axis? + # (actually: are we going positive while last move was negative?) + if (($last_dir{$axis} // 0) < 0 && ($dir{$axis} // 0) > 0) { + # changing direction on this axis! + if (defined $time{$axis} && $time{$axis} < $min_time) { + # direction change was too fast! we need to slow down + push @slowdown, $time{$axis} / $min_time; } - } else { - # if we have no buffer, store this move inside it - $append_to_buffer->($line, \@move, \@freq); + $time{$axis} = 0; + } elsif ($move{$axis}) { + # not changing direction on this axis + # then just calculate move time and sum it + $time{$axis} //= 0; + $time{$axis} += abs($move{$axis}) / ($f // $F); } + } + if (@slowdown) { + my $factor = min(@slowdown); + printf "SLOWDOWN! (slowdown = %d%%)\n", $factor * 100; + $gcode .= "; START SLOWDOWN ($factor)\n"; + $gcode .= "$_\n" for @buffer; + @buffer = (); + $gcode .= "; END SLOWDOWN\n"; + $gcode .= "$line\n"; } else { - # if the move does not exceed any limit, flush the buffer and output this line - $flush_buffer->($line); + push @buffer, $line; } - $X = $x if defined $x; - $Y = $y if defined $y; + for (@axes) { + $last{$_} = $cur{$_} if defined $cur{$_}; + $last_dir{$_} = $dir{$_} if defined $dir{$_}; + } $F = $f if defined $f; + } else { - $gcode .= "$line\n"; + push @buffer, $line; } } + $gcode .= "$_\n" for @buffer; + return $gcode; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index dd877f9c5..15e02b1c6 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -3,7 +3,7 @@ use Moo; use File::Basename qw(basename fileparse); use File::Spec; -use List::Util qw(min max first); +use List::Util qw(max first); use Math::ConvexHull::MonotoneChain qw(convex_hull); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN PI scale unscale move_points nearest_point); From 008633f013db909a95c678dcc90fdb3ac1682561 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 12:23:11 +0100 Subject: [PATCH 095/172] Working implementation of frequency limit --- lib/Slic3r/GCode.pm | 71 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index fca6fc8ea..42bcd2a32 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -1,7 +1,7 @@ package Slic3r::GCode; use Moo; -use List::Util qw(min first); +use List::Util qw(min max first); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y A B); @@ -477,14 +477,15 @@ sub limit_frequency { my $current_gcode = $gcode; $gcode = ''; - my %last; - my $F; + + # the following code is inspired by Marlin frequency limit implementation my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); - my @buffer = (); - my %last_dir = (); - my %time = (); - my @axes = qw(X Y E); + my @axes = qw(X Y); + my %segment_time = (map { $_ => [0,0,0] } @axes); + my %last = (map { $_ => 0 } @axes); + my %last_dir = (map { $_ => 0 } @axes); + my $F; foreach my $line (split /\n/, $current_gcode) { if ($line =~ /^G[01] /) { @@ -502,52 +503,50 @@ sub limit_frequency { # check move directions my %dir = ( - map { $_ => ($move{$_}) ? ($move{$_} > 0 ? 1 : -1) : undef } @axes + map { $_ => ($move{$_}) ? ($move{$_} > 0 ? 1 : -1) : 0 } @axes ); - my @slowdown = (); - foreach my $axis (@axes) { - # are we changing direction on this axis? - # (actually: are we going positive while last move was negative?) - if (($last_dir{$axis} // 0) < 0 && ($dir{$axis} // 0) > 0) { - # changing direction on this axis! - if (defined $time{$axis} && $time{$axis} < $min_time) { - # direction change was too fast! we need to slow down - push @slowdown, $time{$axis} / $min_time; + my $factor = 1; + my $segment_time = abs(max(values %move)) / ($f // $F); + if ($segment_time > 0) { + my %max_segment_time = (); + foreach my $axis (@axes) { + # are we changing direction on this axis? + if ($last_dir{$axis} == $dir{$axis}) { + $segment_time{$axis}[0] += $segment_time; + } else { + @{ $segment_time{$axis} } = ($segment_time, @{ $segment_time{$axis} }[0,1]); } - $time{$axis} = 0; - } elsif ($move{$axis}) { - # not changing direction on this axis - # then just calculate move time and sum it - $time{$axis} //= 0; - $time{$axis} += abs($move{$axis}) / ($f // $F); + + $max_segment_time{$axis} = max($segment_time{$axis}[0], max($segment_time{$axis}[1], $segment_time{$axis}[2])); + } + + my $min_segment_time = min(values %max_segment_time); + if ($min_segment_time < $min_time) { + $factor = $min_segment_time / $min_time; } } - if (@slowdown) { - my $factor = min(@slowdown); - printf "SLOWDOWN! (slowdown = %d%%)\n", $factor * 100; - $gcode .= "; START SLOWDOWN ($factor)\n"; - $gcode .= "$_\n" for @buffer; - @buffer = (); - $gcode .= "; END SLOWDOWN\n"; + + if ($factor == 1) { $gcode .= "$line\n"; } else { - push @buffer, $line; + $line =~ s/ F[0-9.]+//; + my $new_speed = sprintf '%.3f', ($f // $F) * $factor; + $line =~ s/^(G[01]) /$1 F$new_speed /; + $gcode .= "$line\nG1 F" . ($f // $F) . "\n"; } for (@axes) { - $last{$_} = $cur{$_} if defined $cur{$_}; - $last_dir{$_} = $dir{$_} if defined $dir{$_}; + $last{$_} = $cur{$_} if $cur{$_}; + $last_dir{$_} = $dir{$_} if $dir{$_}; } $F = $f if defined $f; } else { - push @buffer, $line; + $gcode .= "$line\n"; } } - $gcode .= "$_\n" for @buffer; - return $gcode; } From 7a87a76391d3ac52f4a69dd5a3231ed3b919ebf0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 15:28:13 +0100 Subject: [PATCH 096/172] Refactor frequency limit to avoid processing G-code --- lib/Slic3r/GCode.pm | 140 ++++++++++++++++++-------------------------- lib/Slic3r/Print.pm | 2 - 2 files changed, 56 insertions(+), 86 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 42bcd2a32..466dfbf4f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -19,10 +19,16 @@ has 'total_extrusion_length' => (is => 'rw', default => sub {0} ); has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); has 'last_speed' => (is => 'rw', default => sub {""}); +has 'last_f' => (is => 'rw', default => sub {""}); +has 'force_f' => (is => 'rw', default => sub {0}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'last_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); +# used for vibration limit: +has 'last_dir' => (is => 'ro', default => sub { [0,0] }); +has 'segment_time' => (is => 'ro', default => sub { [ [0,0,0], [0,0,0] ] }); + # calculate speeds (mm/min) has 'speeds' => ( is => 'ro', @@ -69,6 +75,7 @@ sub move_z { my $current_z = $self->z; if (!defined $current_z || $current_z != ($z + $self->lifted)) { $gcode .= $self->retract(move_z => $z); + $self->speed('travel'); $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')')) unless ($current_z // -1) != ($self->z // -1); } @@ -148,6 +155,7 @@ sub extrude_path { my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $path->flow_spacing); bless $point, 'Slic3r::Point'; $point->rotate(PI/6, $last_line->[B]); + $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); } } @@ -157,6 +165,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]); @@ -267,6 +276,7 @@ sub unretract { my $gcode = ""; if ($self->lifted) { + $self->speed('travel'); $gcode .= $self->G0(undef, $self->z - $self->lifted, 0, 'restore layer Z'); $self->lifted(0); } @@ -311,10 +321,12 @@ sub _G0_G1 { my ($gcode, $point, $z, $e, $comment) = @_; my $dec = $self->dec; + my $speed_factor; if ($point) { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** + $speed_factor = $self->_limit_frequency($point); $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { @@ -322,7 +334,7 @@ sub _G0_G1 { $gcode .= sprintf " Z%.${dec}f", $z; } - return $self->_Gx($gcode, $e, $comment); + return $self->_Gx($gcode, $e, $speed_factor, $comment); } sub G2_G3 { @@ -342,39 +354,45 @@ sub G2_G3 { ($center->[Y] - $self->last_pos->[Y]) * &Slic3r::SCALING_FACTOR; $self->last_pos($point); - return $self->_Gx($gcode, $e, $comment); + return $self->_Gx($gcode, $e, undef, $comment); } sub _Gx { my $self = shift; - my ($gcode, $e, $comment) = @_; + my ($gcode, $e, $speed_factor, $comment) = @_; my $dec = $self->dec; - # determine speed - my $speed = ($e ? $self->speed : 'travel'); - # output speed if it's different from last one used # (goal: reduce gcode size) my $append_bridge_off = 0; - if ($speed ne $self->last_speed) { - if ($speed eq 'bridge') { + my $F; + if ($self->speed ne $self->last_speed) { + if ($self->speed eq 'bridge') { $gcode = ";_BRIDGE_FAN_START\n$gcode"; } elsif ($self->last_speed eq 'bridge') { $append_bridge_off = 1; } # apply the speed reduction for print moves on bottom layer - my $speed_f = $speed eq 'retract' + $F = $self->speed eq 'retract' ? ($self->extruder->retract_speed_mm_min) - : $self->speeds->{$speed} // $speed; + : $self->speeds->{$self->speed} // $self->speed; if ($e && $self->layer && $self->layer->id == 0 && $comment !~ /retract/) { - $speed_f = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? ($speed_f * $1/100) + $F = $Slic3r::Config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ + ? ($F * $1/100) : $Slic3r::Config->first_layer_speed * 60; } - $gcode .= sprintf " F%.${dec}f", $speed_f; - $self->last_speed($speed); + $self->last_speed($self->speed); + $self->last_f($F); + $F *= $speed_factor // 1; + } elsif (defined $speed_factor && $speed_factor != 1) { + $gcode .= sprintf " F%.${dec}f", ($self->last_f * $speed_factor); + $self->force_f(1); # next move will need explicit F + } elsif ($self->force_f) { + $gcode .= sprintf " F%.${dec}f", $self->last_f; + $self->force_f(0); } + $gcode .= sprintf " F%.${dec}f", $F if defined $F; # output extrusion distance if ($e && $Slic3r::Config->extrusion_axis) { @@ -469,85 +487,39 @@ sub set_bed_temperature { } # http://hydraraptor.blogspot.it/2010/12/frequency-limit.html -sub limit_frequency { +# the following implementation is inspired by Marlin code +sub _limit_frequency { my $self = shift; - my ($gcode) = @_; - - return $gcode if $Slic3r::Config->vibration_limit == 0; - - my $current_gcode = $gcode; - $gcode = ''; - - # the following code is inspired by Marlin frequency limit implementation + my ($point) = @_; + return if $Slic3r::Config->vibration_limit == 0; my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); - my @axes = qw(X Y); - my %segment_time = (map { $_ => [0,0,0] } @axes); - my %last = (map { $_ => 0 } @axes); - my %last_dir = (map { $_ => 0 } @axes); - my $F; - foreach my $line (split /\n/, $current_gcode) { - if ($line =~ /^G[01] /) { - my %cur; - my $f; - for (@axes) { - $cur{$_} = $1 if $line =~ /$_([0-9.]+)/; - } - $f = $1 if $line =~ /F([0-9.]+)/; - - # calculate the move vector - my %move = ( - map { $_ => (defined $cur{$_} && defined $last{$_}) ? ($cur{$_} - $last{$_}) : 0 } @axes - ); - - # check move directions - my %dir = ( - map { $_ => ($move{$_}) ? ($move{$_} > 0 ? 1 : -1) : 0 } @axes - ); - - my $factor = 1; - my $segment_time = abs(max(values %move)) / ($f // $F); - if ($segment_time > 0) { - my %max_segment_time = (); - foreach my $axis (@axes) { - # are we changing direction on this axis? - if ($last_dir{$axis} == $dir{$axis}) { - $segment_time{$axis}[0] += $segment_time; - } else { - @{ $segment_time{$axis} } = ($segment_time, @{ $segment_time{$axis} }[0,1]); - } - - $max_segment_time{$axis} = max($segment_time{$axis}[0], max($segment_time{$axis}[1], $segment_time{$axis}[2])); - } - - my $min_segment_time = min(values %max_segment_time); - if ($min_segment_time < $min_time) { - $factor = $min_segment_time / $min_time; - } - } - - if ($factor == 1) { - $gcode .= "$line\n"; + # calculate the move vector and move direction + my @move = map unscale $_, @{ Slic3r::Line->new($self->last_pos, $point)->vector->[B] }; + my @dir = map { $move[$_] ? (($move[$_] > 0) ? 1 : -1) : 0 } X,Y; + + my $factor = 1; + my $segment_time = abs(max(@move)) / $self->speeds->{$self->speed}; + if ($segment_time > 0) { + my @max_segment_time = (); + foreach my $axis (X,Y) { + if ($self->last_dir->[$axis] == $dir[$axis]) { + $self->segment_time->[$axis][0] += $segment_time; } else { - $line =~ s/ F[0-9.]+//; - my $new_speed = sprintf '%.3f', ($f // $F) * $factor; - $line =~ s/^(G[01]) /$1 F$new_speed /; - $gcode .= "$line\nG1 F" . ($f // $F) . "\n"; + @{ $self->segment_time->[$axis] } = ($segment_time, @{ $self->segment_time->[$axis] }[0,1]); } - - for (@axes) { - $last{$_} = $cur{$_} if $cur{$_}; - $last_dir{$_} = $dir{$_} if $dir{$_}; - } - $F = $f if defined $f; - - } else { - $gcode .= "$line\n"; + $max_segment_time[$axis] = max($self->segment_time->[$axis][0], max($self->segment_time->[$axis][1], $self->segment_time->[$axis][2])); + $self->last_dir->[$axis] = $dir[$axis] if $dir[$axis]; + } + + my $min_segment_time = min(@max_segment_time); + if ($min_segment_time < $min_time) { + $factor = $min_segment_time / $min_time; } } - return $gcode; + return $factor; } 1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 15e02b1c6..84140e9ee 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -851,8 +851,6 @@ sub write_gcode { $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; } - $gcode = $gcodegen->limit_frequency($gcode); - return $gcode; }; From 427e3c172042036a1119b0f08d91dda6650e4861 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 15:42:59 +0100 Subject: [PATCH 097/172] Keep bridge flow unchanged even with the new overlapping spacing --- lib/Slic3r/Fill.pm | 6 ++++-- lib/Slic3r/Flow.pm | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index e578b5494..20756bdd4 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -135,8 +135,7 @@ sub make_fill { $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - my $width = sqrt($Slic3r::Config->bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); - $flow_spacing = $width + &Slic3r::OVERLAP_FACTOR * ($width * PI / 4 - $width); # this should be moved to Flow.pm + $flow_spacing = $layer->infill_flow->bridge_spacing; } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -157,6 +156,9 @@ 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; + # save into layer next unless @paths; push @fills, Slic3r::ExtrusionPath::Collection->new( diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index a035b9d37..01992e788 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -8,6 +8,8 @@ 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'); @@ -67,6 +69,17 @@ 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; From 08700aa942507e9c323887acd183dcb1b2ffd13f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 15:42:59 +0100 Subject: [PATCH 098/172] Keep bridge flow unchanged even with the new overlapping spacing --- lib/Slic3r/Fill.pm | 6 ++++-- lib/Slic3r/Flow.pm | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index e578b5494..20756bdd4 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -135,8 +135,7 @@ sub make_fill { $filler = $Slic3r::Config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - my $width = sqrt($Slic3r::Config->bridge_flow_ratio * ($layer->infill_flow->nozzle_diameter**2)); - $flow_spacing = $width + &Slic3r::OVERLAP_FACTOR * ($width * PI / 4 - $width); # this should be moved to Flow.pm + $flow_spacing = $layer->infill_flow->bridge_spacing; } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -157,6 +156,9 @@ 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; + # save into layer next unless @paths; push @fills, Slic3r::ExtrusionPath::Collection->new( diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index a035b9d37..01992e788 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -8,6 +8,8 @@ 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'); @@ -67,6 +69,17 @@ 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; From c99ff0cfe2a49c1914662edbad22bd2fcc22f3a7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 17:38:08 +0100 Subject: [PATCH 099/172] Apply Douglas-Peucker to all paths before generating G-code --- lib/Slic3r.pm | 3 ++- lib/Slic3r/ExtrusionPath.pm | 2 +- lib/Slic3r/GCode.pm | 1 + lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print.pm | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index be105ed10..2cbe4437c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -57,7 +57,8 @@ use Slic3r::TriangleMesh; eval "use Slic3r::Build"; use constant SCALING_FACTOR => 0.000001; -use constant RESOLUTION => 0.01; +use constant RESOLUTION => 0.0125; +use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant OVERLAP_FACTOR => 1; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index bae6e61cd..d392b5c40 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -15,7 +15,7 @@ use Slic3r::Geometry qw(PI X Y epsilon deg2rad rotate_points); has 'polyline' => ( is => 'rw', required => 1, - handles => [qw(merge_continuous_lines lines length reverse clip_end)], + handles => [qw(merge_continuous_lines lines length reverse clip_end simplify)], ); # height is the vertical thickness of the extrusion expressed in mm diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index f725ee79d..2b3f384a2 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -119,6 +119,7 @@ sub extrude_path { my ($path, $description, $recursive) = @_; $path = $path->unpack if $path->isa('Slic3r::ExtrusionPath::Packed'); + $path->simplify(&Slic3r::SCALED_RESOLUTION); # detect arcs if ($Slic3r::Config->gcode_arcs && !$recursive) { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index e940d9ec8..3e4854ea1 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -228,7 +228,7 @@ sub make_perimeters { # create one more offset to be used as boundary for fill { my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets; - $_->simplify(scale &Slic3r::RESOLUTION) for @fill_boundaries; + $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; push @{ $self->surfaces }, @fill_boundaries; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 84140e9ee..1b41ccdbf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -341,7 +341,7 @@ sub export_gcode { # simplify slices (both layer and region slices), # we only need the max resolution for perimeters foreach my $layer (map @{$_->layers}, @{$self->objects}) { - $_->simplify(scale &Slic3r::RESOLUTION) + $_->simplify(&Slic3r::SCALED_RESOLUTION) for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); } From e119cee66cfcb7afd50bb6d6d8d1332ccb342ed1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 17:42:52 +0100 Subject: [PATCH 100/172] Don't emit warnings on unknown options --- lib/Slic3r/Config.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0bf7d7e95..3e1a612af 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -985,8 +985,12 @@ sub set { } if (!exists $Options->{$opt_key}) { - $opt_key = +(grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options)[0] - or warn "Unknown option $opt_key\n"; + my @keys = grep { $Options->{$_}{aliases} && grep $_ eq $opt_key, @{$Options->{$_}{aliases}} } keys %$Options; + if (!@keys) { + warn "Unknown option $opt_key\n"; + return; + } + $opt_key = $keys[0]; } # clone arrayrefs From a66e8e547d067602a77d382643eedb3f109754f4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 17:38:08 +0100 Subject: [PATCH 101/172] Apply Douglas-Peucker to all paths before generating G-code --- lib/Slic3r.pm | 3 ++- lib/Slic3r/ExtrusionPath.pm | 2 +- lib/Slic3r/GCode.pm | 1 + lib/Slic3r/Layer/Region.pm | 2 +- lib/Slic3r/Print.pm | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index be105ed10..2cbe4437c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -57,7 +57,8 @@ use Slic3r::TriangleMesh; eval "use Slic3r::Build"; use constant SCALING_FACTOR => 0.000001; -use constant RESOLUTION => 0.01; +use constant RESOLUTION => 0.0125; +use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant OVERLAP_FACTOR => 1; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index bae6e61cd..d392b5c40 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -15,7 +15,7 @@ use Slic3r::Geometry qw(PI X Y epsilon deg2rad rotate_points); has 'polyline' => ( is => 'rw', required => 1, - handles => [qw(merge_continuous_lines lines length reverse clip_end)], + handles => [qw(merge_continuous_lines lines length reverse clip_end simplify)], ); # height is the vertical thickness of the extrusion expressed in mm diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 466dfbf4f..42c679317 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -126,6 +126,7 @@ sub extrude_path { my ($path, $description, $recursive) = @_; $path = $path->unpack if $path->isa('Slic3r::ExtrusionPath::Packed'); + $path->simplify(&Slic3r::SCALED_RESOLUTION); # detect arcs if ($Slic3r::Config->gcode_arcs && !$recursive) { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index e940d9ec8..3e4854ea1 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -228,7 +228,7 @@ sub make_perimeters { # create one more offset to be used as boundary for fill { my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets; - $_->simplify(scale &Slic3r::RESOLUTION) for @fill_boundaries; + $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; push @{ $self->surfaces }, @fill_boundaries; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 84140e9ee..1b41ccdbf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -341,7 +341,7 @@ sub export_gcode { # simplify slices (both layer and region slices), # we only need the max resolution for perimeters foreach my $layer (map @{$_->layers}, @{$self->objects}) { - $_->simplify(scale &Slic3r::RESOLUTION) + $_->simplify(&Slic3r::SCALED_RESOLUTION) for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); } From de0640603ae088faa749a687184c56853edbe8d8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Nov 2012 19:53:52 +0100 Subject: [PATCH 102/172] Append full config when --gcode-comments is enabled --- lib/Slic3r/Print.pm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 1b41ccdbf..69581697d 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -909,6 +909,16 @@ sub write_gcode { printf $fh "; filament used = %.1fmm (%.1fcm3)\n", $self->total_extrusion_length, $self->total_extrusion_volume; + if ($Slic3r::Config->gcode_comments) { + # append full config + print $fh "\n"; + foreach my $opt_key (sort keys %{$Slic3r::Config}) { + next if $Slic3r::Config::Options->{$opt_key}{shortcut}; + next if $Slic3r::Config::Options->{$opt_key}{gui_only}; + printf $fh "; %s = %s\n", $opt_key, $Slic3r::Config->serialize($opt_key); + } + } + # close our gcode file close $fh; } From 183736dd9afc711d47a2cef67cd5df8024856076 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 12:37:05 +0100 Subject: [PATCH 103/172] Infill flow for bottom layer was calculated incorrectly when first_layer_height != layer_height --- lib/Slic3r/Fill.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 20756bdd4..7c6183083 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -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 * $Slic3r::Config->layer_height, + height => $surface->depth_layers * $layer->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, ], From 300734db385496a0a9281bd97a148eea56485af6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 15:30:55 +0100 Subject: [PATCH 104/172] Emit M106 S0 instead of M107 for Teacup --- lib/Slic3r/GCode.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 2b3f384a2..9dd181223 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -424,7 +424,10 @@ sub set_fan { if ($self->last_fan_speed != $speed || $dont_save) { $self->last_fan_speed($speed) if !$dont_save; if ($speed == 0) { - return sprintf "M107%s\n", ($Slic3r::Config->gcode_comments ? ' ; disable fan' : ''); + my $code = $Slic3r::Config->gcode_flavor eq 'teacup' + ? 'M106 S0' + : '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' : ''); From 3c7321ab80e18c1bc6f96e4282385d68f0b973e8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 15:34:52 +0100 Subject: [PATCH 105/172] Bugfix: some retractions between objects/copies were skipped. #786 --- 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 9dd181223..f65e6b11f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -54,7 +54,7 @@ sub set_shift { $self->shift_x($shift[X]); $self->shift_y($shift[Y]); - $self->last_pos->translate(map -$_, @shift); + $self->last_pos->translate(map -(scale $_), @shift); } # this method accepts Z in scaled coordinates From 5678cd562cb7a29535cdbde6e1689a82c95ba901 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 17:39:16 +0100 Subject: [PATCH 106/172] Fix preview after rotation in plater --- lib/Slic3r/GUI/Plater.pm | 19 +++++++++---------- lib/Slic3r/Geometry.pm | 10 +++++++++- lib/Slic3r/Polyline.pm | 4 +--- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 5a13a6e42..87bb12298 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -416,6 +416,9 @@ sub rotate { my ($obj_idx, $object) = $self->selected_object; + # we need thumbnail to be computed before allowing rotation + return if !$object->thumbnail; + if (!defined $angle) { $angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", $object->rotate, -364, 364, $self); return if !$angle || $angle == -1; @@ -446,7 +449,7 @@ sub arrange { my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return; my @size = (); for my $a (X,Y) { - $size[$a] = max(map $_->rotated_size->[$a], @{$self->{objects}}); + $size[$a] = max(map $_->size->[$a], @{$self->{objects}}); } eval { @@ -756,7 +759,7 @@ sub recenter { my $obj = $_; map { my $instance = $_; - $instance, [ map $instance->[$_] + $obj->rotated_size->[$_], X,Y ]; + $instance, [ map $instance->[$_] + $obj->size->[$_], X,Y ]; } @{$obj->instances}; } @{$self->{objects}}, ]); @@ -1059,6 +1062,7 @@ has 'scale' => (is => 'rw', default => sub { 1 }); has 'rotate' => (is => 'rw', default => sub { 0 }); has 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis has 'thumbnail' => (is => 'rw'); +has 'thumbnail_scaling_factor' => (is => 'rw'); # statistics has 'facets' => (is => 'rw'); @@ -1111,6 +1115,7 @@ sub make_thumbnail { my @points = map [ @$_[X,Y] ], @{$self->model_object->mesh->vertices}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); + $self->thumbnail_scaling_factor($params{scaling_factor}); for (@$convex_hull) { @$_ = map $_ * $params{scaling_factor}, @$_; } @@ -1132,6 +1137,8 @@ sub set_rotation { if ($self->thumbnail) { $self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate)); $self->thumbnail->align_to_origin; + my $z_size = $self->size->[Z]; + $self->size([ (map $_ / $self->thumbnail_scaling_factor, @{$self->thumbnail->size}), $z_size ]); } $self->rotate($angle); } @@ -1150,14 +1157,6 @@ sub set_scale { $self->scale($scale); } -sub rotated_size { - my $self = shift; - - return Slic3r::Polygon->new([0,0], [$self->size->[X], 0], [@{$self->size}], [0, $self->size->[Y]]) - ->rotate(Slic3r::Geometry::deg2rad($self->rotate)) - ->size; -} - package Slic3r::GUI::Plater::ObjectInfoDialog; use Wx qw(:dialog :id :misc :sizer :systemsettings); use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index c89faaf6f..c483fd6cc 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -20,7 +20,7 @@ our @EXPORT_OK = qw( shortest_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 + scaled_epsilon bounding_box_3D size_3D size_2D ); @@ -689,6 +689,14 @@ sub bounding_box_center { ); } +sub size_2D { + my @bounding_box = bounding_box(@_); + return ( + ($bounding_box[X2] - $bounding_box[X1]), + ($bounding_box[Y2] - $bounding_box[Y1]), + ); +} + # bounding_box_intersect($d, @a, @b) # Return true if the given bounding boxes @a and @b intersect # in $d dimensions. Used by line_intersection(). diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 8cdecf39f..692e8e3ba 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -126,9 +126,7 @@ sub bounding_box { sub size { my $self = shift; - - my @extents = $self->bounding_box; - return [$extents[X2] - $extents[X1], $extents[Y2] - $extents[Y1]]; + return [ Slic3r::Geometry::size_2D($self) ]; } sub align_to_origin { From de8a845d818c4ad1aaf3f6e900ee5f23b2371244 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 18:03:20 +0100 Subject: [PATCH 107/172] Disable retraction for support material --- lib/Slic3r/GCode.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index f5114cbd4..e25479e46 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -139,9 +139,9 @@ sub extrude_path { my $gcode = ""; - # retract if distance from previous position is greater or equal to the one - # specified by the user - { + # skip retract for support material + if ($path->role != EXTR_ROLE_SUPPORTMATERIAL) { + # 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) { # move travel back to original layer coordinates. From 124a1fd84ee947821e99f45e0a8bfb91c561becb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 18:03:49 +0100 Subject: [PATCH 108/172] Raise default frequency limit to 25Hz --- 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 8ee1708f9..ee82957cb 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -416,7 +416,7 @@ our $Options = { sidetext => 'Hz', cli => 'vibration-limit=f', type => 'f', - default => 15, + default => 25, }, # print options From e91320e031011582c538b01d39447b81fc38fc13 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 18:31:41 +0100 Subject: [PATCH 109/172] Fix rotation for threaded perls --- lib/Slic3r/GUI/Plater.pm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 87bb12298..38ae1e9cc 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -724,9 +724,10 @@ sub make_thumbnail { my $self = shift; my ($obj_idx) = @_; + my $object = $self->{objects}[$obj_idx]; + $object->thumbnail_scaling_factor($self->{scaling_factor}); my $cb = sub { - my $object = $self->{objects}[$obj_idx]; - my $thumbnail = $object->make_thumbnail(scaling_factor => $self->{scaling_factor}); + my $thumbnail = $object->make_thumbnail; if ($Slic3r::have_threads) { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $thumbnail ]))); @@ -1111,13 +1112,11 @@ sub instances_count { sub make_thumbnail { my $self = shift; - my %params = @_; my @points = map [ @$_[X,Y] ], @{$self->model_object->mesh->vertices}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); - $self->thumbnail_scaling_factor($params{scaling_factor}); for (@$convex_hull) { - @$_ = map $_ * $params{scaling_factor}, @$_; + @$_ = map $_ * $self->thumbnail_scaling_factor, @$_; } $convex_hull->simplify(0.3); $convex_hull->rotate(Slic3r::Geometry::deg2rad($self->rotate)); From 877708592893cf0b38726f7bbbe2041a8a57cf4e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 18:56:58 +0100 Subject: [PATCH 110/172] Update default vibration limit in README too --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 4fa28d7a3..102f59ce9 100644 --- a/README.markdown +++ b/README.markdown @@ -263,7 +263,7 @@ The author of the Silk icon set is Mark James. --support-material-extrusion-width Set a different extrusion width for support material --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1) - --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: 15) + --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: 25) Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement From cd892fdce813308bd5ed356547a18e9a6dd30d0b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Nov 2012 18:57:42 +0100 Subject: [PATCH 111/172] Connect zigzag gaps fill paths to benefit from simplification --- lib/Slic3r/Layer/Region.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 3e4854ea1..52b98abc5 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -273,7 +273,6 @@ sub make_perimeters { Slic3r::Surface->new(expolygon => $expolygon), density => 1, flow_spacing => $flow->spacing, - dont_connect => 1, # time-saver ); my $params = shift @paths; From fc399d60e287801fdc16b47437676c2aa94b9b8f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Nov 2012 17:43:20 +0100 Subject: [PATCH 112/172] Raise the miter limit to avoid squaring corners too easily. #801 --- 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 3382b9319..d91b185ea 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 //= 2; + $miterLimit //= 10; my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit); return @$offsets; From 8ae96a8868cd36e44c0520afccc38e42f0fabc4a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Nov 2012 19:00:43 +0100 Subject: [PATCH 113/172] Only apply vibration limit to gaps fill while it's not very mature to work with long segments --- lib/Slic3r/ExtrusionPath.pm | 3 ++- lib/Slic3r/GCode.pm | 7 ++++++- lib/Slic3r/Layer/Region.pm | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index d392b5c40..81d79fa97 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -6,7 +6,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_SMALLPERIMETER EXTR_ROLE_EXTERNAL_PERIMETER EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_BRIDGE EXTR_ROLE_SKIRT - EXTR_ROLE_SUPPORTMATERIAL); + EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_GAPFILL); our %EXPORT_TAGS = (roles => \@EXPORT_OK); use Slic3r::Geometry qw(PI X Y epsilon deg2rad rotate_points); @@ -33,6 +33,7 @@ use constant EXTR_ROLE_TOPSOLIDFILL => 6; use constant EXTR_ROLE_BRIDGE => 7; use constant EXTR_ROLE_SKIRT => 8; use constant EXTR_ROLE_SUPPORTMATERIAL => 9; +use constant EXTR_ROLE_GAPFILL => 10; use constant PACK_FMT => 'ffca*'; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index e25479e46..3daa3ca74 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -26,6 +26,7 @@ has 'last_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); # used for vibration limit: +has 'limit_frequency' => (is => 'rw', default => sub { 0 }); has 'last_dir' => (is => 'ro', default => sub { [0,0] }); has 'segment_time' => (is => 'ro', default => sub { [ [0,0,0], [0,0,0] ] }); @@ -51,6 +52,7 @@ my %role_speeds = ( &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_SKIRT => 'perimeter', &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', + &EXTR_ROLE_GAPFILL => 'solid_infill', ); sub set_shift { @@ -165,6 +167,9 @@ sub extrude_path { } } + # only apply vibration limiting to gap fill until the algorithm is more mature + $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL); + # go to first point of extrusion path $self->speed('travel'); $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") @@ -327,7 +332,7 @@ sub _G0_G1 { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - $speed_factor = $self->_limit_frequency($point); + $speed_factor = $self->_limit_frequency($point) if $self->limit_frequency; $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 52b98abc5..98bcc15a1 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -283,7 +283,7 @@ sub make_perimeters { } map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), - role => EXTR_ROLE_SOLIDFILL, + role => EXTR_ROLE_GAPFILL, height => $self->height, flow_spacing => $params->{flow_spacing}, ), @paths; From 2abf2be781499f4fb71cf00c3200fe5c0355bd46 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Nov 2012 20:41:14 +0100 Subject: [PATCH 114/172] New testing framework --- MANIFEST | 2 + lib/Slic3r/Print.pm | 33 ++++++++----- lib/Slic3r/Test.pm | 112 ++++++++++++++++++++++++++++++++++++++++++++ t/retraction.t | 42 +++++++++++++++++ 4 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 lib/Slic3r/Test.pm create mode 100644 t/retraction.t diff --git a/MANIFEST b/MANIFEST index e188472c3..d078e1209 100644 --- a/MANIFEST +++ b/MANIFEST @@ -45,6 +45,7 @@ lib/Slic3r/Print/Region.pm lib/Slic3r/Print/Object.pm lib/Slic3r/Surface.pm lib/Slic3r/SVG.pm +lib/Slic3r/Test.pm lib/Slic3r/TriangleMesh.pm MANIFEST This list of files README.markdown @@ -58,6 +59,7 @@ t/dynamic.t t/fill.t t/geometry.t t/polyclip.t +t/retract.t t/serialize.t t/stl.t utils/amf-to-stl.pl diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 69581697d..9ca18cbdf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -434,9 +434,9 @@ sub export_gcode { $self->make_brim; # must come after make_skirt # output everything to a G-code file - my $output_file = $self->expanded_output_filepath($params{output_file}); + my $output_file = $params{output_file} ? $self->expanded_output_filepath($params{output_file}) : ''; $status_cb->(90, "Exporting G-code to $output_file"); - $self->write_gcode($output_file); + $self->write_gcode($params{output_fh} || $output_file); # run post-processing scripts if (@{$Slic3r::Config->post_process}) { @@ -449,14 +449,16 @@ sub export_gcode { } # output some statistics - $self->processing_time(tv_interval($t0)); - printf "Done. Process took %d minutes and %.3f seconds\n", - int($self->processing_time/60), - $self->processing_time - int($self->processing_time/60)*60; - - # TODO: more statistics! - printf "Filament required: %.1fmm (%.1fcm3)\n", - $self->total_extrusion_length, $self->total_extrusion_volume; + unless ($params{quiet}) { + $self->processing_time(tv_interval($t0)); + printf "Done. Process took %d minutes and %.3f seconds\n", + int($self->processing_time/60), + $self->processing_time - int($self->processing_time/60)*60; + + # TODO: more statistics! + printf "Filament required: %.1fmm (%.1fcm3)\n", + $self->total_extrusion_length, $self->total_extrusion_volume; + } } sub export_svg { @@ -643,9 +645,14 @@ sub write_gcode { my $self = shift; my ($file) = @_; - # open output gcode file - open my $fh, ">", $file - or die "Failed to open $file for writing\n"; + # open output gcode file if we weren't supplied a file-handle + my $fh; + if (ref $file eq 'IO::Scalar') { + $fh = $file; + } else { + open $fh, ">", $file + or die "Failed to open $file for writing\n"; + } # write some information my @lt = localtime; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm new file mode 100644 index 000000000..f0210f4de --- /dev/null +++ b/lib/Slic3r/Test.pm @@ -0,0 +1,112 @@ +package Slic3r::Test; +use strict; +use warnings; + +use IO::Scalar; +use Slic3r::Geometry qw(epsilon); + +sub init_print { + my ($model_name, %params) = @_; + + my $model = Slic3r::Model->new; + { + my ($vertices, $facets); + if ($model_name eq '20mm_cube') { + $vertices = [ + [10,10,-10], [10,-10,-10], [-10,-10,-10], [-10,10,-10], [10,10,10], [-10,10,10], [-10,-10,10], [10,-10,10], + ]; + $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); + } + + my $config = Slic3r::Config->new_from_defaults; + $config->apply($params{config}) if $params{config}; + + my $print = Slic3r::Print->new(config => $config); + $print->add_model($model); + $print->validate; + + return $print; +} + +sub gcode { + my ($print) = @_; + + my $fh = IO::Scalar->new(\my $gcode); + $print->export_gcode(output_fh => $fh, quiet => 1); + $fh->close; + + return $gcode; +} + +sub compare { + my ($a, $b) = @_; + return abs($a - $b) < epsilon; +} + +package Slic3r::Test::GCodeReader; +use Moo; + +has 'gcode' => (is => 'ro', required => 1); +has 'X' => (is => 'rw', default => sub {0}); +has 'Y' => (is => 'rw', default => sub {0}); +has 'Z' => (is => 'rw', default => sub {0}); +has 'E' => (is => 'rw', default => sub {0}); +has 'F' => (is => 'rw', default => sub {0}); + +our $Verbose = 0; +my @AXES = qw(X Y Z E F); + +sub parse { + my $self = shift; + my ($cb) = @_; + + foreach my $line (split /\n/, $self->gcode) { + $line =~ s/\s*;(.*)//; # strip comment + next if $line eq ''; + my $comment = $1; + + # parse command + my ($command, @args) = split /\s+/, $line; + my %args = map { /([A-Z])(.*)/; ($1 => $2) } @args; + my %info = (); + + # check retraction + if ($command =~ /^G[01]$/) { + if (!exists $args{E}) { + $info{travel} = 1; + } + foreach my $axis (@AXES) { + if (!exists $args{$axis}) { + $info{"dist_$axis"} = 0; + next; + } + $info{"dist_$axis"} = $args{$axis} - $self->$axis; + } + $info{dist_XY} = Slic3r::Line->new([0,0], [@info{qw(dist_X dist_Y)}])->length; + if (exists $args{E}) { + ($info{dist_E} > 0) + ? ($info{extruding} = 1) + : ($info{retracting} = 1); + } + } + + # run callback + printf "$line\n" if $Verbose; + $cb->($self, $command, \%args, \%info); + + # update coordinates + if ($command =~ /^(?:G[01]|G92)$/) { + for (@AXES) { + $self->$_($args{$_}) if exists $args{$_}; + } + } + + # TODO: update temperatures + } +} + +1; diff --git a/t/retraction.t b/t/retraction.t new file mode 100644 index 000000000..68542fdc8 --- /dev/null +++ b/t/retraction.t @@ -0,0 +1,42 @@ +use Test::More; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Geometry qw(epsilon); +use Slic3r::Test; + +{ + my $config = Slic3r::Config->new( + retract_length => [1.5], + retract_before_travel => [3], + ); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + + my $retracted = 1; # ignore the first travel move from home to first point + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{retracting}) { + ok Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]), + 'retracted by the right amount'; + $retracted = 1; + } + if ($info->{extruding}) { + $retracted = 0; + } + if ($info->{travel} && $info->{dist_XY} >= $config->retract_before_travel->[0]) { + ok $retracted, + 'retracted before long travel move'; + } + }); +} + +done_testing; + +__END__ From 21a6219b62c7e6ecaf348221d8766d638f832092 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 11:14:06 +0100 Subject: [PATCH 115/172] Disable vibration limit. #785 --- README.markdown | 1 - lib/Slic3r/Config.pm | 2 +- lib/Slic3r/GCode.pm | 2 +- lib/Slic3r/GUI/Tab.pm | 4 ---- slic3r.pl | 1 - 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 102f59ce9..72e961a45 100644 --- a/README.markdown +++ b/README.markdown @@ -263,7 +263,6 @@ The author of the Silk icon set is Mark James. --support-material-extrusion-width Set a different extrusion width for support material --bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1) - --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: 25) Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index ee82957cb..b20f0ee17 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -416,7 +416,7 @@ our $Options = { sidetext => 'Hz', cli => 'vibration-limit=f', type => 'f', - default => 25, + default => 0, }, # print options diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 3daa3ca74..d11035e53 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -168,7 +168,7 @@ sub extrude_path { } # only apply vibration limiting to gap fill until the algorithm is more mature - $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL); + $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL) if 0; # go to first point of extrusion path $self->speed('travel'); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index b9c445c59..e1469053c 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -634,10 +634,6 @@ sub build { }, ], }, - { - title => 'Advanced', - options => [qw(vibration_limit)], - }, ]); $self->add_options_page('Custom G-code', 'cog.png', optgroups => [ diff --git a/slic3r.pl b/slic3r.pl index 6e011ec88..d540fcae4 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -311,7 +311,6 @@ $j --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}) - --vibration-limit Experimental frequency limit to avoid resonance (Hz, default: $config->{vibration_limit}) Multiple extruder options: --extruder-offset Offset of each extruder, if firmware doesn't handle the displacement From 68e302fe27653f2b3cff0c18f254ba59f02533a5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 11:24:04 +0100 Subject: [PATCH 116/172] Fix exporting G-code --- lib/Slic3r/Print.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 9ca18cbdf..0ac8c11ff 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -434,8 +434,8 @@ sub export_gcode { $self->make_brim; # must come after make_skirt # output everything to a G-code file - my $output_file = $params{output_file} ? $self->expanded_output_filepath($params{output_file}) : ''; - $status_cb->(90, "Exporting G-code to $output_file"); + my $output_file = $self->expanded_output_filepath($params{output_file}); + $status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); $self->write_gcode($params{output_fh} || $output_file); # run post-processing scripts @@ -943,6 +943,7 @@ sub expanded_output_filepath { # if no input file was supplied, take the first one from our objects $input_file ||= $self->objects->[0]->input_file; + return undef if !defined $input_file; # if output path is an existing directory, we take that and append # the specified filename format From 5dba02fdeb146759eedef3ca1ab003fae8698d8c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 11:25:02 +0100 Subject: [PATCH 117/172] New separate speed setting for gap fill. #785 --- README.markdown | 1 + lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/GCode.pm | 4 ++-- slic3r.pl | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 72e961a45..7a9553340 100644 --- a/README.markdown +++ b/README.markdown @@ -145,6 +145,7 @@ The author of the Silk icon set is Mark James. --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed (default: 50) --bridge-speed Speed of bridge print moves in mm/s (default: 60) + --gap-fill-speed Speed of gap fill print moves in mm/s (default: 20) --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%) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b20f0ee17..6ef3d8ca6 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -293,6 +293,14 @@ our $Options = { aliases => [qw(bridge_feed_rate)], default => 60, }, + 'gap_fill_speed' => { + label => 'Gap fill', + tooltip => 'Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues.', + sidetext => 'mm/s', + cli => 'gap-fill-speed=f', + type => 'f', + default => 20, + }, 'first_layer_speed' => { label => 'First layer speed', tooltip => 'If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds.', diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d11035e53..5cb86e56a 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -36,7 +36,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 bridge), + solid_infill top_solid_infill bridge gap_fill), }}, ); @@ -52,7 +52,7 @@ my %role_speeds = ( &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_SKIRT => 'perimeter', &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', - &EXTR_ROLE_GAPFILL => 'solid_infill', + &EXTR_ROLE_GAPFILL => 'gap_fill', ); sub set_shift { diff --git a/slic3r.pl b/slic3r.pl index d540fcae4..6f0938241 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -193,6 +193,7 @@ $j --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed (default: $config->{top_solid_infill_speed}) --bridge-speed Speed of bridge print moves in mm/s (default: $config->{bridge_speed}) + --gap-fill-speed Speed of gap fill print moves in mm/s (default: $config->{gap_fill_speed}) --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}) From e7b307df021b2e999c4a418a7537c79425c1e5e7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 17:09:59 +0100 Subject: [PATCH 118/172] Expose gap_fill_speed in GUI --- lib/Slic3r/GUI/Tab.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index e1469053c..31e2e103b 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -420,7 +420,7 @@ sub build { $self->add_options_page('Speed', 'time.png', optgroups => [ { title => 'Speed for print moves', - options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed)], + options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed gap_fill_speed)], }, { title => 'Speed for non-print moves', From d6d96391a909f74c176d1798ab3586f36dd78733 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 17:15:52 +0100 Subject: [PATCH 119/172] Avoid overextrusion when support material spacing is configured to < the extrusion spacing. #821 --- lib/Slic3r/Print/Object.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 9c5479f27..ba04c6005 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -514,6 +514,9 @@ sub generate_support_material { my $threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive 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 + : $flow->spacing; # determine support regions in each layer (for upper layers) Slic3r::debugf "Detecting regions\n"; @@ -577,7 +580,7 @@ sub generate_support_material { foreach my $expolygon (@support_material_areas) { my @paths = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon), - density => $flow->spacing / $Slic3r::Config->support_material_spacing, + density => $flow->spacing / $pattern_spacing, flow_spacing => $flow->spacing, ); my $params = shift @paths; From 0f0992e39ae8a4d08d375bf0609bf415875431d7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 17:20:26 +0100 Subject: [PATCH 120/172] Speed for support material. #813 --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 8 ++++++++ lib/Slic3r/GCode.pm | 6 +++--- lib/Slic3r/GUI/Tab.pm | 2 +- slic3r.pl | 2 ++ 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 7a9553340..67a4e7d0a 100644 --- a/README.markdown +++ b/README.markdown @@ -144,6 +144,8 @@ The author of the Silk icon set is Mark James. (default: 60) --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed (default: 50) + --support-material-speed + Speed of support material print moves in mm/s (default: 60) --bridge-speed Speed of bridge print moves in mm/s (default: 60) --gap-fill-speed Speed of gap fill print moves in mm/s (default: 20) --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 6ef3d8ca6..85b1c0149 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -284,6 +284,14 @@ our $Options = { ratio_over => 'solid_infill_speed', default => 50, }, + 'support_material_speed' => { + label => 'Support material', + tooltip => 'Speed for printing support material.', + sidetext => 'mm/s', + cli => 'support-material-speed=f', + type => 'f', + default => 60, + }, 'bridge_speed' => { label => 'Bridges', tooltip => 'Speed for printing bridges.', diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 5cb86e56a..bc5c32905 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -36,7 +36,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 bridge gap_fill), + solid_infill top_solid_infill support_material bridge), }}, ); @@ -51,8 +51,8 @@ my %role_speeds = ( &EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill', &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_SKIRT => 'perimeter', - &EXTR_ROLE_SUPPORTMATERIAL => 'perimeter', - &EXTR_ROLE_GAPFILL => 'gap_fill', + &EXTR_ROLE_SUPPORTMATERIAL => 'support_material', + &EXTR_ROLE_GAPFILL => 'solid_infill', ); sub set_shift { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 31e2e103b..bc215a049 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -420,7 +420,7 @@ sub build { $self->add_options_page('Speed', 'time.png', optgroups => [ { title => 'Speed for print moves', - options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed bridge_speed gap_fill_speed)], + options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed support_material_speed bridge_speed gap_fill_speed)], }, { title => 'Speed for non-print moves', diff --git a/slic3r.pl b/slic3r.pl index 6f0938241..98f4aef1c 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -192,6 +192,8 @@ $j (default: $config->{solid_infill_speed}) --top-solid-infill-speed Speed of print moves for top surfaces in mm/s or % over solid infill speed (default: $config->{top_solid_infill_speed}) + --support-material-speed + Speed of support material print moves in mm/s (default: $config->{support_material_speed}) --bridge-speed Speed of bridge print moves in mm/s (default: $config->{bridge_speed}) --gap-fill-speed Speed of gap fill print moves in mm/s (default: $config->{gap_fill_speed}) --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute From 556e59bbe4041aceebcab0be2ea345750c31236f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 17:41:15 +0100 Subject: [PATCH 121/172] Last commit erroneously reverted gap fill speed --- 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 bc5c32905..1c5374974 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -52,7 +52,7 @@ my %role_speeds = ( &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_SKIRT => 'perimeter', &EXTR_ROLE_SUPPORTMATERIAL => 'support_material', - &EXTR_ROLE_GAPFILL => 'solid_infill', + &EXTR_ROLE_GAPFILL => 'gap_fill', ); sub set_shift { From 12873e973bb719e1e3c4b5339727651be78ca2c7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 18:37:47 +0100 Subject: [PATCH 122/172] One more missing bit for gap_fill_speed --- 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 1c5374974..97ae89a0c 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -36,7 +36,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), + solid_infill top_solid_infill support_material bridge gap_fill), }}, ); From 452b62e53d449ebfca00922f7cc3319f291f0afb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 18:38:50 +0100 Subject: [PATCH 123/172] Releasing 0.9.7 --- lib/Slic3r.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 2cbe4437c..987244790 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.6-dev"; +our $VERSION = "0.9.7"; our $debug = 0; sub debugf { From 1df42fbe4699425753f138bfee5a399ef06caf40 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 18:45:29 +0100 Subject: [PATCH 124/172] Add IO::Scalar to build prerequisites --- Build.PL | 1 + 1 file changed, 1 insertion(+) diff --git a/Build.PL b/Build.PL index 361e1beee..53dccbbba 100644 --- a/Build.PL +++ b/Build.PL @@ -22,6 +22,7 @@ my $build = Module::Build->new( }, build_requires => { 'Test::More' => '0.10', + 'IO::Scalar' => '0.10', }, recommends => { 'Growl::GNTP' => '0.15', From e6afebb982be6e634b1ed91ff3b93cac6998b8d0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 23 Nov 2012 19:37:41 +0100 Subject: [PATCH 125/172] 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 987244790..4a0b1bb37 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -7,7 +7,7 @@ use strict; use warnings; require v5.10; -our $VERSION = "0.9.7"; +our $VERSION = "0.9.8-dev"; our $debug = 0; sub debugf { From 116ab446e3c2da1f1427bee030849e22090c5d9a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 24 Nov 2012 00:13:04 +0100 Subject: [PATCH 126/172] Show vertical projection instead of convex hull for objects with <= 2000 facets. #780 --- lib/Slic3r/ExPolygon.pm | 35 +++++++++++++++++++++++++++++++ lib/Slic3r/GUI/Plater.pm | 42 +++++++++++++++++++++++--------------- lib/Slic3r/TriangleMesh.pm | 15 ++++++++++++++ 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 9a82ba0e7..f19cfcd5d 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -164,6 +164,11 @@ sub simplify { $_->simplify(@_) for @$self; } +sub scale { + my $self = shift; + $_->scale(@_) for @$self; +} + sub translate { my $self = shift; $_->translate(@_) for @$self; @@ -300,4 +305,34 @@ sub medial_axis { return @result; } +package Slic3r::ExPolygon::Collection; +use Moo; +use Slic3r::Geometry qw(X1 Y1); + +has 'expolygons' => (is => 'ro', default => sub { [] }); + +sub clone { + my $self = shift; + return (ref $self)->new( + expolygons => [ map $_->clone, @{$self->expolygons} ], + ); +} + +sub align_to_origin { + my $self = shift; + + my @bb = Slic3r::Geometry::bounding_box([ map @$_, map @$_, @{$self->expolygons} ]); + $_->translate(-$bb[X1], -$bb[Y1]) for @{$self->expolygons}; +} + +sub rotate { + my $self = shift; + $_->rotate(@_) for @{$self->expolygons}; +} + +sub size { + my $self = shift; + return [ Slic3r::Geometry::size_2D([ map @$_, map @$_, @{$self->expolygons} ]) ]; +} + 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 38ae1e9cc..724a52591 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -727,6 +727,7 @@ sub make_thumbnail { my $object = $self->{objects}[$obj_idx]; $object->thumbnail_scaling_factor($self->{scaling_factor}); my $cb = sub { + $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new if $Slic3r::have_threads; my $thumbnail = $object->make_thumbnail; if ($Slic3r::have_threads) { @@ -857,7 +858,8 @@ sub repaint { for my $instance_idx (0 .. $#{$object->instances}) { my $instance = $object->instances->[$instance_idx]; push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $object->thumbnail->clone ]; - $parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y)); + $_->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y)) + for @{$parent->{object_previews}->[-1][2]->expolygons}; my $drag_object = $self->{drag_object}; if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) { @@ -867,11 +869,12 @@ sub repaint { } else { $dc->SetBrush($parent->{objects_brush}); } - $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); + $dc->DrawPolygon($parent->_y($_), 0, 0) for map $_->contour, @{ $parent->{object_previews}->[-1][2]->expolygons }; # if sequential printing is enabled and we have more than one object if ($parent->{config}->complete_objects && (map @{$_->instances}, @{$parent->{objects}}) > 1) { - my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; + my $convex_hull = Slic3r::Polygon->new(convex_hull([ map @{$_->contour}, @{$parent->{object_previews}->[-1][2]->expolygons} ])); + my $clearance = +($convex_hull->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; $dc->SetPen($parent->{clearance_pen}); $dc->SetBrush($parent->{transparent_brush}); $dc->DrawPolygon($parent->_y($clearance), 0, 0); @@ -881,7 +884,7 @@ sub repaint { # draw skirt if (@{$parent->{object_previews}} && $parent->{config}->skirts) { - my $convex_hull = Slic3r::Polygon->new(convex_hull([ map @{$_->[2]}, @{$parent->{object_previews}} ])); + my $convex_hull = Slic3r::Polygon->new(convex_hull([ map @{$_->contour}, map @{$_->[2]->expolygons}, @{$parent->{object_previews}} ])); $convex_hull = +($convex_hull->offset($parent->{config}->skirt_distance * $parent->{scaling_factor}, 1, JT_ROUND))[0]; $dc->SetPen($parent->{skirt_pen}); $dc->SetBrush($parent->{transparent_brush}); @@ -903,7 +906,7 @@ sub mouse_event { $parent->selection_changed(0); for my $preview (@{$parent->{object_previews}}) { my ($obj_idx, $instance_idx, $thumbnail) = @$preview; - if ($thumbnail->encloses_point($pos)) { + if (first { $_->contour->encloses_point($pos) } @{$thumbnail->expolygons}) { $parent->{selected_objects} = [ [$obj_idx, $instance_idx] ]; $parent->{list}->Select($obj_idx, 1); $parent->selection_changed(1); @@ -934,7 +937,7 @@ sub mouse_event { } elsif ($event->Moving) { my $cursor = wxSTANDARD_CURSOR; for my $preview (@{$parent->{object_previews}}) { - if ($preview->[2]->encloses_point($pos)) { + if (first { $_->contour->encloses_point($pos) } @{ $preview->[2]->expolygons }) { $cursor = Wx::Cursor->new(wxCURSOR_HAND); last; } @@ -1114,19 +1117,26 @@ sub make_thumbnail { my $self = shift; my @points = map [ @$_[X,Y] ], @{$self->model_object->mesh->vertices}; - my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); - for (@$convex_hull) { + my $mesh = $self->model_object->mesh; + my $thumbnail = Slic3r::ExPolygon::Collection->new( + expolygons => (@{$mesh->facets} <= 2000) + ? $mesh->horizontal_projection + : [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ], + ); + for (map @$_, map @$_, @{$thumbnail->expolygons}) { @$_ = map $_ * $self->thumbnail_scaling_factor, @$_; } - $convex_hull->simplify(0.3); - $convex_hull->rotate(Slic3r::Geometry::deg2rad($self->rotate)); - $convex_hull->scale($self->scale); - $convex_hull->align_to_origin; + for (@{$thumbnail->expolygons}) { + $_->simplify(0.3); + $_->rotate(Slic3r::Geometry::deg2rad($self->rotate)); + $_->scale($self->scale); + } + $thumbnail->align_to_origin; - $self->thumbnail($convex_hull); # ignored in multi-threaded environments + $self->thumbnail($thumbnail); # ignored in multi-threaded environments $self->free_model_object; - return $convex_hull; + return $thumbnail; } sub set_rotation { @@ -1150,8 +1160,8 @@ sub set_scale { return if $factor == 1; $self->size->[$_] *= $factor for X,Y,Z; if ($self->thumbnail) { - $self->thumbnail->scale($factor); - $self->thumbnail->align_to_origin; + $_->scale($factor) for @{$self->thumbnail->expolygons}; + $self->thumbnail->align_to_origin; } $self->scale($scale); } diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index a2a416b55..ea1204fed 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -2,6 +2,7 @@ package Slic3r::TriangleMesh; use Moo; use Slic3r::Geometry qw(X Y Z A B unscale same_point); +use Slic3r::Geometry::Clipper qw(union_ex); # public has 'vertices' => (is => 'ro', required => 1); # id => [$x,$y,$z] @@ -578,4 +579,18 @@ sub split_mesh { return @meshes; } +sub horizontal_projection { + my $self = shift; + + my @f = (); + foreach my $facet (@{$self->facets}) { + push @f, [ map [ @{$self->vertices->[$_]}[X,Y] ], @$facet ]; + } + + my $scale_vector = Math::Clipper::integerize_coordinate_sets({ bits => 32 }, @f); + my $union = union_ex([ Slic3r::Geometry::Clipper::offset(\@f, 1) ]); + Math::Clipper::unscale_coordinate_sets($scale_vector, $_) for @$union; + return $union; +} + 1; From 93310a6a817560b9a9979a384a0a096d33b2ba57 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 24 Nov 2012 00:15:02 +0100 Subject: [PATCH 127/172] Fix bounding_box_center() --- lib/Slic3r/Geometry.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index c483fd6cc..9bfb0bced 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -684,8 +684,8 @@ sub bounding_box { sub bounding_box_center { my @bounding_box = bounding_box(@_); return Slic3r::Point->new( - ($bounding_box[X2] - $bounding_box[X1]) / 2, - ($bounding_box[Y2] - $bounding_box[Y1]) / 2, + ($bounding_box[X2] + $bounding_box[X1]) / 2, + ($bounding_box[Y2] + $bounding_box[Y1]) / 2, ); } From b495e15d7f17604c62cee5083153e3aa1cb892e4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 29 Nov 2012 19:13:52 +0100 Subject: [PATCH 128/172] Fix plater slowness after the introduction of the vertical projection --- lib/Slic3r/GUI/Plater.pm | 13 +++++++------ lib/Slic3r/TriangleMesh.pm | 5 +++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 724a52591..a8b30cba2 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1119,20 +1119,21 @@ sub make_thumbnail { my @points = map [ @$_[X,Y] ], @{$self->model_object->mesh->vertices}; my $mesh = $self->model_object->mesh; my $thumbnail = Slic3r::ExPolygon::Collection->new( - expolygons => (@{$mesh->facets} <= 2000) + expolygons => (@{$mesh->facets} <= 5000) ? $mesh->horizontal_projection : [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ], ); for (map @$_, map @$_, @{$thumbnail->expolygons}) { @$_ = map $_ * $self->thumbnail_scaling_factor, @$_; } - for (@{$thumbnail->expolygons}) { - $_->simplify(0.3); - $_->rotate(Slic3r::Geometry::deg2rad($self->rotate)); - $_->scale($self->scale); + foreach my $expolygon (@{$thumbnail->expolygons}) { + @$expolygon = grep $_->area >= 1, @$expolygon; + $expolygon->simplify(0.5); + $expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate)); + $expolygon->scale($self->scale); } + @{$thumbnail->expolygons} = grep @$_, @{$thumbnail->expolygons}; $thumbnail->align_to_origin; - $self->thumbnail($thumbnail); # ignored in multi-threaded environments $self->free_model_object; diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index ea1204fed..599445967 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -584,11 +584,12 @@ sub horizontal_projection { my @f = (); foreach my $facet (@{$self->facets}) { - push @f, [ map [ @{$self->vertices->[$_]}[X,Y] ], @$facet ]; + push @f, Slic3r::Polygon->new([ map [ @{$self->vertices->[$_]}[X,Y] ], @$facet ]); } + $_->make_counter_clockwise for @f; my $scale_vector = Math::Clipper::integerize_coordinate_sets({ bits => 32 }, @f); - my $union = union_ex([ Slic3r::Geometry::Clipper::offset(\@f, 1) ]); + my $union = union_ex([ Slic3r::Geometry::Clipper::offset(\@f, 10000) ]); Math::Clipper::unscale_coordinate_sets($scale_vector, $_) for @$union; return $union; } From accc598eb565f20868a1f8afacdd42164f992fcb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 29 Nov 2012 19:16:07 +0100 Subject: [PATCH 129/172] =?UTF-8?q?Revert=20"Experimental=20feature:=20mak?= =?UTF-8?q?e=20a=20little=20move=20inwards=20by=2045=C2=B0=20after=20finis?= =?UTF-8?q?hing=20the=20external=20perimeter=20and=20before=20retracting.?= =?UTF-8?q?=20#186"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c57e94c06590f212e9fe75318728108efa9d024e. Conflicts: lib/Slic3r/GCode.pm --- lib/Slic3r/GCode.pm | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 97ae89a0c..3a20bbe27 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -3,7 +3,7 @@ use Moo; use List::Util qw(min max first); use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y A B); +use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y B); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); has 'layer' => (is => 'rw'); @@ -22,7 +22,6 @@ has 'last_speed' => (is => 'rw', default => sub {""}); has 'last_f' => (is => 'rw', default => sub {""}); has 'force_f' => (is => 'rw', default => sub {0}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); -has 'last_path' => (is => 'rw'); has 'dec' => (is => 'ro', default => sub { 3 } ); # used for vibration limit: @@ -151,17 +150,6 @@ sub extrude_path { # 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 ($self->last_path && $self->last_path->role == &EXTR_ROLE_EXTERNAL_PERIMETER) { - my @lines = $self->last_path->lines; - my $last_line = $lines[-1]; - if (points_coincide($last_line->[B], $self->last_pos)) { - my $point = Slic3r::Geometry::point_along_segment(@$last_line, $last_line->length + scale $path->flow_spacing); - bless $point, 'Slic3r::Point'; - $point->rotate(PI/6, $last_line->[B]); - $self->speed('travel'); - $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); - } - } $gcode .= $self->retract(travel_to => $path->points->[0]); } } @@ -216,8 +204,6 @@ sub extrude_path { $self->elapsed_time($self->elapsed_time + $path_time); } - $self->last_path($path); - return $gcode; } From 3a2ca92ec3a92fc73fe7c73d15e7d76e8a746ec3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 4 Dec 2012 23:52:53 +0100 Subject: [PATCH 130/172] Test shortest path algorithm --- t/fill.t | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/t/fill.t b/t/fill.t index 28d3b9cb7..11ed91e5f 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 4; +plan tests => 5; BEGIN { use FindBin; @@ -41,4 +41,15 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } } } +{ + my $collection = Slic3r::Polyline::Collection->new(polylines => [ + Slic3r::Polyline->new([0,15], [0,18], [0,20]), + Slic3r::Polyline->new([0,10], [0,8], [0,5]), + ]); + is_deeply + [ map $_->[Y], map @$_, $collection->shortest_path(Slic3r::Point->new(0,30)) ], + [20, 18, 15, 10, 8, 5], + 'shortest path'; +} + __END__ From cf90033fd0b357dc8a4ae92829ebf49a6d8da2c8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 00:39:40 +0100 Subject: [PATCH 131/172] Refactor and extend t/retraction.t --- t/retraction.t | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index 68542fdc8..bf1beb8f5 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More; +use Test::More tests => 2; use strict; use warnings; @@ -8,14 +8,11 @@ BEGIN { } use Slic3r; -use Slic3r::Geometry qw(epsilon); use Slic3r::Test; -{ - my $config = Slic3r::Config->new( - retract_length => [1.5], - retract_before_travel => [3], - ); +my $config = Slic3r::Config->new_from_defaults; + +my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $retracted = 1; # ignore the first travel move from home to first point @@ -23,20 +20,30 @@ use Slic3r::Test; my ($self, $cmd, $args, $info) = @_; if ($info->{retracting}) { - ok Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]), - 'retracted by the right amount'; + fail 'retracted by the correct amount' + if !Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]); $retracted = 1; } if ($info->{extruding}) { - $retracted = 0; + if ($retracted) { + fail 'unretracted by the correct amount' + if !Slic3r::Test::compare($info->{dist_E}, $config->retract_length->[0] + $config->retract_restart_extra->[0]); + $retracted = 0; + } } if ($info->{travel} && $info->{dist_XY} >= $config->retract_before_travel->[0]) { - ok $retracted, - 'retracted before long travel move'; + fail 'retracted before long travel move' if !$retracted; } }); -} + + 1; +}; -done_testing; +$config->set('retract_length', [1.5]); +$config->set('retract_before_travel', [3]); +ok $test->(), 'retraction'; + +$config->set('retract_restart_extra', [1]); +ok $test->(), 'retraction with restart extra length'; __END__ From ac6dc67e6adddfe9d59e8123010ce23dc80cd43a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 01:12:50 +0100 Subject: [PATCH 132/172] Tidy --- lib/Slic3r/Test.pm | 4 ++-- t/retraction.t | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index f0210f4de..ca95d5de4 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -58,7 +58,7 @@ has 'E' => (is => 'rw', default => sub {0}); has 'F' => (is => 'rw', default => sub {0}); our $Verbose = 0; -my @AXES = qw(X Y Z E F); +my @AXES = qw(X Y Z E); sub parse { my $self = shift; @@ -100,7 +100,7 @@ sub parse { # update coordinates if ($command =~ /^(?:G[01]|G92)$/) { - for (@AXES) { + for (@AXES, 'F') { $self->$_($args{$_}) if exists $args{$_}; } } diff --git a/t/retraction.t b/t/retraction.t index bf1beb8f5..d19365ae2 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -44,6 +44,6 @@ $config->set('retract_before_travel', [3]); ok $test->(), 'retraction'; $config->set('retract_restart_extra', [1]); -ok $test->(), 'retraction with restart extra length'; +ok $test->(), 'restart extra length'; __END__ From 2564317fb4ba3965eb4b00173d8cab3847969fa4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 01:27:21 +0100 Subject: [PATCH 133/172] Test retract lift --- t/retraction.t | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/t/retraction.t b/t/retraction.t index d19365ae2..addaa935c 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 2; +use Test::More tests => 3; use strict; use warnings; @@ -16,15 +16,31 @@ my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $retracted = 1; # ignore the first travel move from home to first point + my $lifted = 0; Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { my ($self, $cmd, $args, $info) = @_; + if ($info->{dist_Z}) { + # lift move or lift + change layer + if (Slic3r::Test::compare($info->{dist_Z}, $config->retract_lift->[0]) + || (Slic3r::Test::compare($info->{dist_Z}, $config->layer_height + $config->retract_lift->[0]) && $config->retract_lift->[0] > 0)) { + fail 'only lifting while retracted' if !$retracted; + $lifted = 1; + } + if ($info->{dist_Z} < 0) { + fail 'going down only after lifting' if !$lifted; + fail 'going down by the same amount of the lift' + if !Slic3r::Test::compare($info->{dist_Z}, -$config->retract_lift->[0]); + $lifted = 0; + } + } if ($info->{retracting}) { fail 'retracted by the correct amount' if !Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]); $retracted = 1; } if ($info->{extruding}) { + fail 'only extruding while not lifted' if $lifted; if ($retracted) { fail 'unretracted by the correct amount' if !Slic3r::Test::compare($info->{dist_E}, $config->retract_length->[0] + $config->retract_restart_extra->[0]); @@ -46,4 +62,7 @@ ok $test->(), 'retraction'; $config->set('retract_restart_extra', [1]); ok $test->(), 'restart extra length'; +$config->set('retract_lift', [1]); +ok $test->(), 'lift'; + __END__ From 283809f5c279e98df210ea19abda6e9f82a04a8b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 10:47:41 +0100 Subject: [PATCH 134/172] No need for a role to identify small perimeters --- lib/Slic3r/ExtrusionPath.pm | 3 +-- lib/Slic3r/GCode.pm | 10 ++++++++-- lib/Slic3r/Layer/Region.pm | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 81d79fa97..e52dc66ed 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -3,7 +3,7 @@ use Moo; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_SMALLPERIMETER EXTR_ROLE_EXTERNAL_PERIMETER +our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_EXTERNAL_PERIMETER EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_BRIDGE EXTR_ROLE_SKIRT EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_GAPFILL); @@ -24,7 +24,6 @@ has 'flow_spacing' => (is => 'rw'); has 'role' => (is => 'rw', required => 1); use constant EXTR_ROLE_PERIMETER => 0; -use constant EXTR_ROLE_SMALLPERIMETER => 1; use constant EXTR_ROLE_EXTERNAL_PERIMETER => 2; use constant EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER => 3; use constant EXTR_ROLE_FILL => 4; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 3a20bbe27..69d2765aa 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -42,7 +42,6 @@ has 'speeds' => ( # assign speeds to roles my %role_speeds = ( &EXTR_ROLE_PERIMETER => 'perimeter', - &EXTR_ROLE_SMALLPERIMETER => 'small_perimeter', &EXTR_ROLE_EXTERNAL_PERIMETER => 'external_perimeter', &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER => 'perimeter', &EXTR_ROLE_FILL => 'infill', @@ -179,8 +178,15 @@ sub extrude_path { # calculate extrusion length per distance unit my $e = $self->extruder->e_per_mm3 * $area; - # extrude arc or line + # set speed $self->speed( $role_speeds{$path->role} || die "Unknown role: " . $path->role ); + if ($path->role == EXTR_ROLE_PERIMETER || $path->role == EXTR_ROLE_EXTERNAL_PERIMETER || $path->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { + if (abs($path->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) { + $self->speed('small_perimeter'); + } + } + + # extrude arc or line my $path_length = 0; if ($path->isa('Slic3r::ExtrusionPath::Arc')) { $path_length = unscale $path->length; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 98bcc15a1..4226f1b2e 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -382,7 +382,7 @@ sub _add_perimeter { return unless $polygon->is_printable($self->perimeter_flow->width); push @{ $self->perimeters }, Slic3r::ExtrusionLoop->pack( polygon => $polygon, - role => (abs($polygon->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) ? EXTR_ROLE_SMALLPERIMETER : ($role // EXTR_ROLE_PERIMETER), #/ + role => ($role // EXTR_ROLE_PERIMETER), flow_spacing => $self->perimeter_flow->spacing, ); } From f001374c6e2a17640b70e5220c2b4fb4537892e2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 11:31:35 +0100 Subject: [PATCH 135/172] New implementation of the little inwards move before leaving a loop. #186 --- lib/Slic3r/GCode.pm | 24 ++++++++++++++++++++++-- lib/Slic3r/Geometry.pm | 1 + lib/Slic3r/Polygon.pm | 12 ++++++++++-- t/angles.t | 11 +++++++++-- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 69d2765aa..8062b7d6b 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -97,7 +97,7 @@ sub extrude_loop { # extrude all loops ccw $loop = $loop->unpack if $loop->isa('Slic3r::ExtrusionLoop::Packed'); - $loop->polygon->make_counter_clockwise; + my $was_clockwise = $loop->polygon->make_counter_clockwise; # find the point of the loop that is closest to the current extruder position # or randomize if requested @@ -118,7 +118,27 @@ sub extrude_loop { return '' if !@{$extrusion_path->polyline}; # extrude along the path - return $self->extrude_path($extrusion_path, $description); + my $gcode = $self->extrude_path($extrusion_path, $description); + + # make a little move inwards before leaving loop + if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER) { + # detect angle between last and first segment + # the side depends on the original winding order of the polygon (left for contours, right for holes) + my @points = $was_clockwise ? (-2, 1) : (1, -2); + my $angle = Slic3r::Geometry::angle3points(@{$extrusion_path->polyline}[0, @points]) / 3; + $angle *= -1 if $was_clockwise; + + # create the destination point along the first segment and rotate it + my $point = Slic3r::Geometry::point_along_segment(@{$extrusion_path->polyline}[0,1], scale $extrusion_path->flow_spacing); + bless $point, 'Slic3r::Point'; + $point->rotate($angle, $extrusion_path->polyline->[0]); + + # generate the travel move + $self->speed('travel'); + $gcode .= $self->G0($point, undef, 0, "move inwards before travel"); + } + + return $gcode; } sub extrude_path { diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 9bfb0bced..db3ecb6c8 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -736,6 +736,7 @@ sub size_3D { return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y,Z); } +# this assumes a CCW rotation from $p2 to $p3 around $p1 sub angle3points { my ($p1, $p2, $p3) = @_; # p1 is the center diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 50805ab47..eaaca1d0f 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -31,12 +31,20 @@ sub is_counter_clockwise { sub make_counter_clockwise { my $self = shift; - $self->reverse if !$self->is_counter_clockwise; + if (!$self->is_counter_clockwise) { + $self->reverse; + return 1; + } + return 0; } sub make_clockwise { my $self = shift; - $self->reverse if $self->is_counter_clockwise; + if ($self->is_counter_clockwise) { + $self->reverse; + return 1; + } + return 0; } sub merge_continuous_lines { diff --git a/t/angles.t b/t/angles.t index 0caac718d..dc1a671b0 100644 --- a/t/angles.t +++ b/t/angles.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 24; +plan tests => 26; BEGIN { use FindBin; @@ -10,7 +10,7 @@ BEGIN { } use Slic3r; -use Slic3r::Geometry qw(line_atan line_direction rad2deg_dir PI); +use Slic3r::Geometry qw(line_atan line_direction rad2deg_dir angle3points PI); #========================================================== @@ -54,3 +54,10 @@ use Slic3r::Geometry qw(line_atan line_direction rad2deg_dir PI); } #========================================================== + +{ + is angle3points([0,0], [10,0], [0,10]), PI/2, 'CW angle3points'; + is angle3points([0,0], [0,10], [10,0]), PI/2*3, 'CCW angle3points'; +} + +#========================================================== From d219d69625c027836e37e1e6fdfc66a6370d7a5c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 11:52:52 +0100 Subject: [PATCH 136/172] Test retraction with G0 --- t/retraction.t | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index addaa935c..8c262078a 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 3; +use Test::More tests => 6; use strict; use warnings; @@ -13,6 +13,9 @@ use Slic3r::Test; my $config = Slic3r::Config->new_from_defaults; my $test = sub { + my ($conf) = @_; + $conf ||= $config; + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my $retracted = 1; # ignore the first travel move from home to first point @@ -37,6 +40,8 @@ my $test = sub { if ($info->{retracting}) { fail 'retracted by the correct amount' if !Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]); + fail 'combining retraction and travel with G0' + if $cmd ne 'G0' && $config->g0 && ($info->{dist_Z} || $info->{dist_XY}); $retracted = 1; } if ($info->{extruding}) { @@ -57,12 +62,22 @@ my $test = sub { $config->set('retract_length', [1.5]); $config->set('retract_before_travel', [3]); -ok $test->(), 'retraction'; -$config->set('retract_restart_extra', [1]); -ok $test->(), 'restart extra length'; +my $retract_tests = sub { + my ($descr) = @_; + + ok $test->(), "retraction$descr"; + + my $conf = $config->clone; + $conf->set('retract_restart_extra', [1]); + ok $test->($conf), "restart extra length$descr"; + + $conf->set('retract_lift', [1]); + ok $test->($conf), "lift$descr"; +}; -$config->set('retract_lift', [1]); -ok $test->(), 'lift'; +$retract_tests->(''); +$config->set('g0', 1); +$retract_tests->(' (G0)'); __END__ From bfc3616c0c765c506cc74b31034d6de9901d8202 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 12:01:48 +0100 Subject: [PATCH 137/172] Fix test --- t/retraction.t | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index 8c262078a..c1381c671 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -16,7 +16,7 @@ my $test = sub { my ($conf) = @_; $conf ||= $config; - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $print = Slic3r::Test::init_print('20mm_cube', config => $conf); my $retracted = 1; # ignore the first travel move from home to first point my $lifted = 0; @@ -25,34 +25,34 @@ my $test = sub { if ($info->{dist_Z}) { # lift move or lift + change layer - if (Slic3r::Test::compare($info->{dist_Z}, $config->retract_lift->[0]) - || (Slic3r::Test::compare($info->{dist_Z}, $config->layer_height + $config->retract_lift->[0]) && $config->retract_lift->[0] > 0)) { - fail 'only lifting while retracted' if !$retracted; + if (Slic3r::Test::compare($info->{dist_Z}, $conf->retract_lift->[0]) + || (Slic3r::Test::compare($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[0]) && $conf->retract_lift->[0] > 0)) { + fail 'only lifting while retracted' if !$retracted && !($conf->g0 && $info->{retracting}); $lifted = 1; } if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift' - if !Slic3r::Test::compare($info->{dist_Z}, -$config->retract_lift->[0]); + if !Slic3r::Test::compare($info->{dist_Z}, -$conf->retract_lift->[0]); $lifted = 0; } } if ($info->{retracting}) { fail 'retracted by the correct amount' - if !Slic3r::Test::compare(-$info->{dist_E}, $config->retract_length->[0]); + if !Slic3r::Test::compare(-$info->{dist_E}, $conf->retract_length->[0]); fail 'combining retraction and travel with G0' - if $cmd ne 'G0' && $config->g0 && ($info->{dist_Z} || $info->{dist_XY}); + if $cmd ne 'G0' && $conf->g0 && ($info->{dist_Z} || $info->{dist_XY}); $retracted = 1; } if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; if ($retracted) { fail 'unretracted by the correct amount' - if !Slic3r::Test::compare($info->{dist_E}, $config->retract_length->[0] + $config->retract_restart_extra->[0]); + if !Slic3r::Test::compare($info->{dist_E}, $conf->retract_length->[0] + $conf->retract_restart_extra->[0]); $retracted = 0; } } - if ($info->{travel} && $info->{dist_XY} >= $config->retract_before_travel->[0]) { + if ($info->{travel} && $info->{dist_XY} >= $conf->retract_before_travel->[0]) { fail 'retracted before long travel move' if !$retracted; } }); From cf32cd65101ca623d8b94cfba588c53280edb9aa Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 15:03:36 +0100 Subject: [PATCH 138/172] Implement vibration limit using G4 pauses --- lib/Slic3r/Config.pm | 2 +- lib/Slic3r/GCode.pm | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 85b1c0149..cc8904f22 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -432,7 +432,7 @@ our $Options = { sidetext => 'Hz', cli => 'vibration-limit=f', type => 'f', - default => 0, + default => 15, }, # print options diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 8062b7d6b..96cce9f59 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -175,7 +175,7 @@ sub extrude_path { } # only apply vibration limiting to gap fill until the algorithm is more mature - $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL) if 0; + $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL); # go to first point of extrusion path $self->speed('travel'); @@ -344,7 +344,9 @@ sub _G0_G1 { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - $speed_factor = $self->_limit_frequency($point) if $self->limit_frequency; + if ($self->limit_frequency) { + $gcode = $self->_limit_frequency($point) . $gcode; + } $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { @@ -514,14 +516,13 @@ sub _limit_frequency { my ($point) = @_; return if $Slic3r::Config->vibration_limit == 0; - my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); + my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); # in minutes # calculate the move vector and move direction my @move = map unscale $_, @{ Slic3r::Line->new($self->last_pos, $point)->vector->[B] }; my @dir = map { $move[$_] ? (($move[$_] > 0) ? 1 : -1) : 0 } X,Y; - my $factor = 1; - my $segment_time = abs(max(@move)) / $self->speeds->{$self->speed}; + my $segment_time = abs(max(@move)) / $self->speeds->{$self->speed}; # in minutes if ($segment_time > 0) { my @max_segment_time = (); foreach my $axis (X,Y) { @@ -536,11 +537,11 @@ sub _limit_frequency { my $min_segment_time = min(@max_segment_time); if ($min_segment_time < $min_time) { - $factor = $min_segment_time / $min_time; + return sprintf "G4 P%d\n", ($min_time - $min_segment_time) * 60 * 1000; } } - return $factor; + return ''; } 1; From 25960a33e0104e1b68e6efd6ff8a64fe8897fbc6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 15:08:17 +0100 Subject: [PATCH 139/172] Put vibration limit back in GUI --- lib/Slic3r/GUI/Tab.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index bc215a049..646b62e3e 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -634,6 +634,10 @@ sub build { }, ], }, + { + title => 'Advanced', + options => [qw(vibration_limit)], + }, ]); $self->add_options_page('Custom G-code', 'cog.png', optgroups => [ From ca0aa5c2870cd40793facc72fcbca40911181a5f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 16:32:50 +0100 Subject: [PATCH 140/172] Apply vibration limit to every move except perimeters --- lib/Slic3r/GCode.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 96cce9f59..a5c46b2ea 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -174,8 +174,7 @@ sub extrude_path { } } - # only apply vibration limiting to gap fill until the algorithm is more mature - $self->limit_frequency($path->role == EXTR_ROLE_GAPFILL); + $self->limit_frequency($path->role != EXTR_ROLE_PERIMETER && $path->role != EXTR_ROLE_EXTERNAL_PERIMETER && $path->role != EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER); # go to first point of extrusion path $self->speed('travel'); From 142a6cc4eb2b8a44075087b079f246703c435954 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 5 Dec 2012 17:57:35 +0100 Subject: [PATCH 141/172] Add --vibration-limit back to CLI usage and disable it by default --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 2 +- slic3r.pl | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 67a4e7d0a..b355f935b 100644 --- a/README.markdown +++ b/README.markdown @@ -116,6 +116,8 @@ The author of the Silk icon set is Mark James. --g0 Use G0 commands for retraction (experimental, not supported by all firmwares) --gcode-comments Make G-code verbose by adding comments (default: no) + --vibration-limit Limit the frequency of moves on X and Y axes (Hz, set zero to disable; + default: 0) Filament options: --filament-diameter Diameter in mm of your raw filament (default: 3) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index cc8904f22..85b1c0149 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -432,7 +432,7 @@ our $Options = { sidetext => 'Hz', cli => 'vibration-limit=f', type => 'f', - default => 15, + default => 0, }, # print options diff --git a/slic3r.pl b/slic3r.pl index 98f4aef1c..b4325abf1 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -164,6 +164,8 @@ $j --g0 Use G0 commands for retraction (experimental, not supported by all firmwares) --gcode-comments Make G-code verbose by adding comments (default: no) + --vibration-limit Limit the frequency of moves on X and Y axes (Hz, set zero to disable; + default: $config->{vibration_limit}) Filament options: --filament-diameter Diameter in mm of your raw filament (default: $config->{filament_diameter}->[0]) From 4078bb0476c8556124790171b753316ab8a5e939 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 6 Dec 2012 10:54:28 +0100 Subject: [PATCH 142/172] Fix error when vibration limit is disabled --- 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 a5c46b2ea..6a45b61cc 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -514,7 +514,7 @@ sub _limit_frequency { my $self = shift; my ($point) = @_; - return if $Slic3r::Config->vibration_limit == 0; + return '' if $Slic3r::Config->vibration_limit == 0; my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); # in minutes # calculate the move vector and move direction From 6019836eefd50943e379e6713ab8447642ce11b4 Mon Sep 17 00:00:00 2001 From: Mark Hindess Date: Thu, 6 Dec 2012 20:39:03 +0000 Subject: [PATCH 143/172] Avoid uninitialized value in concatenation at line 347. --- 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 a5c46b2ea..6a45b61cc 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -514,7 +514,7 @@ sub _limit_frequency { my $self = shift; my ($point) = @_; - return if $Slic3r::Config->vibration_limit == 0; + return '' if $Slic3r::Config->vibration_limit == 0; my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); # in minutes # calculate the move vector and move direction From 1a3497b71daa035092ee7f44d2d1446b4866c451 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 9 Dec 2012 18:33:25 +0100 Subject: [PATCH 144/172] Add tests for vibration limiting and fix implementation. Also includes a fix in set_shift() --- lib/Slic3r/GCode.pm | 65 +++++++++++++------------------ lib/Slic3r/Test.pm | 12 ++++-- t/retraction.t | 6 ++- t/vibrationlimit.t | 95 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 42 deletions(-) create mode 100644 t/vibrationlimit.t diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 6a45b61cc..72bc89fa1 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -1,7 +1,7 @@ package Slic3r::GCode; use Moo; -use List::Util qw(min max first); +use List::Util qw(max first); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y B); @@ -20,14 +20,12 @@ has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); has 'last_speed' => (is => 'rw', default => sub {""}); has 'last_f' => (is => 'rw', default => sub {""}); -has 'force_f' => (is => 'rw', default => sub {0}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'dec' => (is => 'ro', default => sub { 3 } ); # used for vibration limit: -has 'limit_frequency' => (is => 'rw', default => sub { 0 }); has 'last_dir' => (is => 'ro', default => sub { [0,0] }); -has 'segment_time' => (is => 'ro', default => sub { [ [0,0,0], [0,0,0] ] }); +has 'dir_time' => (is => 'ro', default => sub { [0,0] }); # calculate speeds (mm/min) has 'speeds' => ( @@ -57,10 +55,13 @@ sub set_shift { my $self = shift; my @shift = @_; + $self->last_pos->translate( + scale ($shift[X] - $self->shift_x), + scale ($shift[Y] - $self->shift_y), + ); + $self->shift_x($shift[X]); $self->shift_y($shift[Y]); - - $self->last_pos->translate(map -(scale $_), @shift); } # this method accepts Z in scaled coordinates @@ -174,8 +175,6 @@ sub extrude_path { } } - $self->limit_frequency($path->role != EXTR_ROLE_PERIMETER && $path->role != EXTR_ROLE_EXTERNAL_PERIMETER && $path->role != EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER); - # go to first point of extrusion path $self->speed('travel'); $gcode .= $self->G0($path->points->[0], undef, 0, "move to first $description point") @@ -338,14 +337,11 @@ sub _G0_G1 { my ($gcode, $point, $z, $e, $comment) = @_; my $dec = $self->dec; - my $speed_factor; if ($point) { $gcode .= sprintf " X%.${dec}f Y%.${dec}f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - if ($self->limit_frequency) { - $gcode = $self->_limit_frequency($point) . $gcode; - } + $gcode = $self->_limit_frequency($point) . $gcode; $self->last_pos($point->clone); } if (defined $z && (!defined $self->z || $z != $self->z)) { @@ -353,7 +349,7 @@ sub _G0_G1 { $gcode .= sprintf " Z%.${dec}f", $z; } - return $self->_Gx($gcode, $e, $speed_factor, $comment); + return $self->_Gx($gcode, $e, $comment); } sub G2_G3 { @@ -373,12 +369,12 @@ sub G2_G3 { ($center->[Y] - $self->last_pos->[Y]) * &Slic3r::SCALING_FACTOR; $self->last_pos($point); - return $self->_Gx($gcode, $e, undef, $comment); + return $self->_Gx($gcode, $e, $comment); } sub _Gx { my $self = shift; - my ($gcode, $e, $speed_factor, $comment) = @_; + my ($gcode, $e, $comment) = @_; my $dec = $self->dec; # output speed if it's different from last one used @@ -403,13 +399,6 @@ sub _Gx { } $self->last_speed($self->speed); $self->last_f($F); - $F *= $speed_factor // 1; - } elsif (defined $speed_factor && $speed_factor != 1) { - $gcode .= sprintf " F%.${dec}f", ($self->last_f * $speed_factor); - $self->force_f(1); # next move will need explicit F - } elsif ($self->force_f) { - $gcode .= sprintf " F%.${dec}f", $self->last_f; - $self->force_f(0); } $gcode .= sprintf " F%.${dec}f", $F if defined $F; @@ -509,7 +498,6 @@ sub set_bed_temperature { } # http://hydraraptor.blogspot.it/2010/12/frequency-limit.html -# the following implementation is inspired by Marlin code sub _limit_frequency { my $self = shift; my ($point) = @_; @@ -518,25 +506,28 @@ sub _limit_frequency { my $min_time = 1 / ($Slic3r::Config->vibration_limit * 60); # in minutes # calculate the move vector and move direction - my @move = map unscale $_, @{ Slic3r::Line->new($self->last_pos, $point)->vector->[B] }; - my @dir = map { $move[$_] ? (($move[$_] > 0) ? 1 : -1) : 0 } X,Y; + my $vector = Slic3r::Line->new($self->last_pos, $point)->vector; + my @dir = map { $vector->[B][$_] <=> 0 } X,Y; - my $segment_time = abs(max(@move)) / $self->speeds->{$self->speed}; # in minutes - if ($segment_time > 0) { - my @max_segment_time = (); + my $time = (unscale $vector->length) / $self->speeds->{$self->speed}; # in minutes + if ($time > 0) { + my @pause = (); foreach my $axis (X,Y) { - if ($self->last_dir->[$axis] == $dir[$axis]) { - $self->segment_time->[$axis][0] += $segment_time; - } else { - @{ $self->segment_time->[$axis] } = ($segment_time, @{ $self->segment_time->[$axis] }[0,1]); + if ($dir[$axis] != 0 && $self->last_dir->[$axis] != $dir[$axis]) { + if ($self->last_dir->[$axis] != 0) { + # this axis is changing direction: check whether we need to pause + if ($self->dir_time->[$axis] < $min_time) { + push @pause, ($min_time - $self->dir_time->[$axis]); + } + } + $self->last_dir->[$axis] = $dir[$axis]; + $self->dir_time->[$axis] = 0; } - $max_segment_time[$axis] = max($self->segment_time->[$axis][0], max($self->segment_time->[$axis][1], $self->segment_time->[$axis][2])); - $self->last_dir->[$axis] = $dir[$axis] if $dir[$axis]; + $self->dir_time->[$axis] += $time; } - my $min_segment_time = min(@max_segment_time); - if ($min_segment_time < $min_time) { - return sprintf "G4 P%d\n", ($min_time - $min_segment_time) * 60 * 1000; + if (@pause) { + return sprintf "G4 P%d\n", max(@pause) * 60 * 1000; } } diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index ca95d5de4..7f5f4776a 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -5,15 +5,21 @@ use warnings; use IO::Scalar; use Slic3r::Geometry qw(epsilon); +my %cuboids = ( + '20mm_cube' => [20,20,20], + '2x20x10' => [2, 20,10], +); + sub init_print { my ($model_name, %params) = @_; my $model = Slic3r::Model->new; { my ($vertices, $facets); - if ($model_name eq '20mm_cube') { + if ($cuboids{$model_name}) { + my ($x, $y, $z) = @{ $cuboids{$model_name} }; $vertices = [ - [10,10,-10], [10,-10,-10], [-10,-10,-10], [-10,10,-10], [10,10,10], [-10,10,10], [-10,-10,10], [10,-10,10], + [$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], @@ -65,6 +71,7 @@ sub parse { my ($cb) = @_; foreach my $line (split /\n/, $self->gcode) { + print "$line\n" if $Verbose || $ENV{SLIC3R_TESTS_GCODE}; $line =~ s/\s*;(.*)//; # strip comment next if $line eq ''; my $comment = $1; @@ -95,7 +102,6 @@ sub parse { } # run callback - printf "$line\n" if $Verbose; $cb->($self, $command, \%args, \%info); # update coordinates diff --git a/t/retraction.t b/t/retraction.t index c1381c671..9c4506e8f 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 6; +use Test::More tests => 9; use strict; use warnings; @@ -77,7 +77,9 @@ my $retract_tests = sub { }; $retract_tests->(''); +$config->set('duplicate', 2); +$retract_tests->(' (duplicate)'); $config->set('g0', 1); -$retract_tests->(' (G0)'); +$retract_tests->(' (G0 and duplicate)'); __END__ diff --git a/t/vibrationlimit.t b/t/vibrationlimit.t new file mode 100644 index 000000000..b45409560 --- /dev/null +++ b/t/vibrationlimit.t @@ -0,0 +1,95 @@ +use Test::More tests => 9; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Geometry qw(epsilon); +use Slic3r::Test; + +my $config = Slic3r::Config->new_from_defaults; + +# tolerance, in minutes +# (our time estimation differs from the internal one because of decimals truncation) +my $epsilon = 0.002; + +my $test = sub { + my ($conf) = @_; + $conf ||= $config; + + my $print = Slic3r::Test::init_print('2x20x10', config => $conf); + + my $min_time = 1 / ($conf->vibration_limit * 60); # minimum time between direction changes in minutes + my %dir = (X => 0, Y => 0); + my %dir_time = (X => 0, Y => 0); + my %dir_sleep_time = (X => 0, Y => 0); + my $last_cmd_pause = 0; + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd !~ /^G[01]$/) { + if ($cmd eq 'G4') { + $last_cmd_pause = (($args->{P} // 0) / 1000 + ($args->{S} // 0)) / 60; # in minutes + $dir_sleep_time{$_} += $last_cmd_pause for qw(X Y); + $last_cmd_pause -= $epsilon; # error builds up + } + return; + } + + # Z moves are slow enough that we can consider any vibration interrupted + if ($info->{dist_Z}) { + $dir_time{$_} += 99999 for qw(X Y); + $last_cmd_pause = 0; + return; + } elsif ($info->{dist_E} != 0 && $info->{dist_XY} == 0) { + my $time = abs($info->{dist_E}) / ($args->{F} // $self->F); # in minutes + $dir_time{$_} += $time for qw(X Y); + $last_cmd_pause = 0; + return; + } + + # compute move time (this assumes that the speed is XY-bound, which happens very likely) + my $time = abs($info->{dist_XY}) / ($args->{F} // $self->F); # in minutes + + my $one_axis_would_trigger_limit_without_pause = 0; + foreach my $axis (qw(X Y)) { + # are we changing direction on this axis? + my $dir = $info->{"dist_$axis"} <=> 0; + if ($dir != 0 && $dir{$axis} != $dir) { + # this move changes direction on this axis + if ($dir{$axis} != 0) { + if (($dir_time{$axis} + $dir_sleep_time{$axis}) < ($min_time - $epsilon)) { + fail 'vibration limit exceeded'; + } + $one_axis_would_trigger_limit_without_pause = 1 + if ($dir_time{$axis} - $last_cmd_pause) < $min_time; + } + $dir{$axis} = $dir; + $dir_time{$axis} = 0; + $dir_sleep_time{$axis} = 0; + } + $dir_time{$axis} += $time; + } + fail 'no unnecessary pauses are added' + if $last_cmd_pause > $epsilon && !$one_axis_would_trigger_limit_without_pause; + + $last_cmd_pause = 0; + }); + + 1; +}; + +$config->set('gcode_comments', 1); +$config->set('perimeters', 1); +foreach my $frequency (5, 10, 15) { + foreach my $gapfillspeed (20, 50, 100) { + $config->set('vibration_limit', $frequency); + ok $test->(), "vibrations limited to ${frequency}Hz (gap fill speed = ${gapfillspeed} mm/s)"; + } +} + +__END__ From 494a1a84cf55f64206bf814315a0dff23dbdba8d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 10 Dec 2012 01:10:20 +0100 Subject: [PATCH 145/172] Incomplete work to test multiple extruders in t/retraction.t --- t/retraction.t | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index 9c4506e8f..f57b4d3fc 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -18,42 +18,53 @@ my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $conf); - my $retracted = 1; # ignore the first travel move from home to first point + my $tool = 0; + my @retracted = (1); # ignore the first travel move from home to first point my $lifted = 0; + my $changed_tool = 0; Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { my ($self, $cmd, $args, $info) = @_; + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + $changed_tool = 1; + } + if ($info->{dist_Z}) { # lift move or lift + change layer - if (Slic3r::Test::compare($info->{dist_Z}, $conf->retract_lift->[0]) - || (Slic3r::Test::compare($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[0]) && $conf->retract_lift->[0] > 0)) { - fail 'only lifting while retracted' if !$retracted && !($conf->g0 && $info->{retracting}); + if (Slic3r::Test::compare($info->{dist_Z}, $conf->retract_lift->[$tool]) + || (Slic3r::Test::compare($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[$tool]) && $conf->retract_lift->[$tool] > 0)) { + fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting}); $lifted = 1; } if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift' - if !Slic3r::Test::compare($info->{dist_Z}, -$conf->retract_lift->[0]); + if !Slic3r::Test::compare($info->{dist_Z}, -$conf->retract_lift->[$tool]); $lifted = 0; } } if ($info->{retracting}) { fail 'retracted by the correct amount' - if !Slic3r::Test::compare(-$info->{dist_E}, $conf->retract_length->[0]); + if !Slic3r::Test::compare(-$info->{dist_E}, $conf->retract_length->[$tool]); fail 'combining retraction and travel with G0' if $cmd ne 'G0' && $conf->g0 && ($info->{dist_Z} || $info->{dist_XY}); - $retracted = 1; + $retracted[$tool] = 1; } if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; - if ($retracted) { + if ($retracted[$tool]) { + my $expected_amount = $conf->retract_length->[$tool] + $conf->retract_restart_extra->[$tool]; + if ($changed_tool) { + $expected_amount = $conf->retract_length_toolchange->[$tool] + $conf->retract_restart_extra_toolchange->[$tool]; + } fail 'unretracted by the correct amount' - if !Slic3r::Test::compare($info->{dist_E}, $conf->retract_length->[0] + $conf->retract_restart_extra->[0]); - $retracted = 0; + if !Slic3r::Test::compare($info->{dist_E}, $expected_amount); + $retracted[$tool] = 0; } } - if ($info->{travel} && $info->{dist_XY} >= $conf->retract_before_travel->[0]) { - fail 'retracted before long travel move' if !$retracted; + if ($info->{travel} && $info->{dist_XY} >= $conf->retract_before_travel->[$tool]) { + fail 'retracted before long travel move' if !$retracted[$tool]; } }); @@ -77,9 +88,16 @@ my $retract_tests = sub { }; $retract_tests->(''); + $config->set('duplicate', 2); $retract_tests->(' (duplicate)'); + $config->set('g0', 1); $retract_tests->(' (G0 and duplicate)'); +$config->set('duplicate', 1); +$config->set('g0', 0); +$config->set('infill_extruder', 2); +$retract_tests->(' (dual extruder)'); + __END__ From cf8390b5d32ac1ae1afecdf01b9976e0316ae5c3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 11 Dec 2012 18:19:56 +0100 Subject: [PATCH 146/172] Update MANIFEST --- MANIFEST | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST b/MANIFEST index d078e1209..8c04cbc4f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -59,9 +59,10 @@ t/dynamic.t t/fill.t t/geometry.t t/polyclip.t -t/retract.t +t/retraction.t t/serialize.t t/stl.t +t/vibrationlimit.t utils/amf-to-stl.pl utils/file_info.pl utils/post-processing/filament-weight.pl From b70404bb236173de0e1cb0ea4573cde33e26632c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 17 Dec 2012 19:55:25 +0100 Subject: [PATCH 147/172] Disable gap filling if gap fill speed is set to zero --- lib/Slic3r/Config.pm | 2 +- lib/Slic3r/Layer/Region.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 85b1c0149..44d748955 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -303,7 +303,7 @@ our $Options = { }, 'gap_fill_speed' => { label => 'Gap fill', - tooltip => 'Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues.', + tooltip => 'Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues. Set zero to disable gaps filling.', sidetext => 'mm/s', cli => 'gap-fill-speed=f', type => 'f', diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 4226f1b2e..18b039ce0 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -233,7 +233,7 @@ sub make_perimeters { } # fill gaps - { + if ($Slic3r::Config->gap_fill_speed > 0) { my $filler = Slic3r::Fill::Rectilinear->new(layer_id => $self->layer->id); my $w = $self->perimeter_flow->width; From ea304a4803e316fb376f555826f24ada72517be3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Dec 2012 17:01:01 +0100 Subject: [PATCH 148/172] Bugfix: custom layer G-code was applied before Z change, and not after like it was documented. #869 --- MANIFEST | 1 + lib/Slic3r/GCode.pm | 2 ++ lib/Slic3r/Print.pm | 9 +++++++-- t/custom_gcode.t | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 t/custom_gcode.t diff --git a/MANIFEST b/MANIFEST index 8c04cbc4f..c5869c19c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -55,6 +55,7 @@ t/arcs.t t/clean_polylines.t t/clipper.t t/collinear.t +t/custom_gcode.t t/dynamic.t t/fill.t t/geometry.t diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 72bc89fa1..84622b80f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -7,6 +7,7 @@ use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y B); has 'multiple_extruders' => (is => 'ro', default => sub {0} ); has 'layer' => (is => 'rw'); +has 'move_z_callback' => (is => 'rw'); has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw'); @@ -79,6 +80,7 @@ sub move_z { $self->speed('travel'); $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')')) unless ($current_z // -1) != ($self->z // -1); + $gcode .= $self->move_z_callback->() if defined $self->move_z_callback; } return $gcode; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0ac8c11ff..a8cb82a83 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -738,8 +738,13 @@ 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]); $gcodegen->elapsed_time(0); - $gcode .= $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" - if $Slic3r::Config->layer_gcode; + + # prepare callback to call as soon as a Z command is generated + $gcodegen->move_z_callback(sub { + $gcodegen->move_z_callback(undef); # circular ref or not? + return $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" + if $Slic3r::Config->layer_gcode; + }); # extrude skirt if ($skirt_done < $Slic3r::Config->skirt_height) { diff --git a/t/custom_gcode.t b/t/custom_gcode.t new file mode 100644 index 000000000..ea36c0cf1 --- /dev/null +++ b/t/custom_gcode.t @@ -0,0 +1,41 @@ +use Test::More tests => 1; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Test; + +my $config = Slic3r::Config->new_from_defaults; + +my $test = sub { + my ($conf) = @_; + $conf ||= $config; + + my $print = Slic3r::Test::init_print('2x20x10', config => $conf); + + my $last_move_was_z_change = 0; + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { + fail 'custom layer G-code was not applied after Z change'; + } + if (!$last_move_was_z_change && $cmd eq $config->layer_gcode) { + fail 'custom layer G-code was not applied after Z change'; + } + + $last_move_was_z_change = (defined $info->{dist_Z} && $info->{dist_Z} > 0); + }); + + 1; +}; + +$config->set('layer_gcode', '_MY_CUSTOM_GCODE_'); +ok $test->(), "custom layer G-code is applied after Z move and before other moves"; + +__END__ From dcc0ce78dbcad651e3a6e34ea6b71dcff6d6669d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Dec 2012 17:03:50 +0100 Subject: [PATCH 149/172] Fix warning --- 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 a8cb82a83..85f7470ff 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -742,8 +742,8 @@ sub write_gcode { # prepare callback to call as soon as a Z command is generated $gcodegen->move_z_callback(sub { $gcodegen->move_z_callback(undef); # circular ref or not? - return $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n" - if $Slic3r::Config->layer_gcode; + return "" if !$Slic3r::Config->layer_gcode; + return $Slic3r::Config->replace_options($Slic3r::Config->layer_gcode) . "\n"; }); # extrude skirt From bf603b022172bf57784b6f60e5794414c5dbb7a3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Dec 2012 18:10:20 +0100 Subject: [PATCH 150/172] Fixed regression causing inefficient paths during infill --- lib/Slic3r/ExtrusionPath/Collection.pm | 8 ++++-- lib/Slic3r/Polyline.pm | 10 +++++-- t/fill.t | 37 +++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath/Collection.pm b/lib/Slic3r/ExtrusionPath/Collection.pm index 9abb1faf9..f36908150 100644 --- a/lib/Slic3r/ExtrusionPath/Collection.pm +++ b/lib/Slic3r/ExtrusionPath/Collection.pm @@ -12,11 +12,15 @@ sub shortest_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 + # 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 $_->unpack->polyline, @{$self->paths} ], + polylines => [ map $_->polyline, @paths ], ); - return $collection->shortest_path($start_near, $self->paths); + return $collection->shortest_path($start_near, \@paths); } sub cleanup { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 692e8e3ba..6e3497544 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -49,6 +49,11 @@ sub boost_linestring { return Boost::Geometry::Utils::linestring($self); } +sub wkt { + my $self = shift; + return sprintf "LINESTRING((%s))", join ',', map "$_->[0] $_->[1]", @$self; +} + sub merge_continuous_lines { my $self = shift; polyline_remove_parallel_continuous_edges($self); @@ -188,8 +193,9 @@ use Moo; 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 +# 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 { my $self = shift; my ($start_near, $items) = @_; diff --git a/t/fill.t b/t/fill.t index 11ed91e5f..eb7c8fdb8 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 5; +plan tests => 8; BEGIN { use FindBin; @@ -52,4 +52,39 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } 'shortest path'; } +{ + my $collection = Slic3r::Polyline::Collection->new(polylines => [ + Slic3r::Polyline->new([4,0], [10,0], [15,0]), + Slic3r::Polyline->new([10,5], [15,5], [20,5]), + ]); + is_deeply + [ map $_->[X], map @$_, $collection->shortest_path(Slic3r::Point->new(30,0)) ], + [reverse 4, 10, 15, 10, 15, 20], + 'shortest path'; +} + +{ + my $collection = Slic3r::ExtrusionPath::Collection->new(paths => [ + map Slic3r::ExtrusionPath->pack(polyline => $_, role => 0), + Slic3r::Polyline->new([0,15], [0,18], [0,20]), + Slic3r::Polyline->new([0,10], [0,8], [0,5]), + ]); + is_deeply + [ map $_->[Y], map @{$_->unpack->polyline}, $collection->shortest_path(Slic3r::Point->new(0,30)) ], + [20, 18, 15, 10, 8, 5], + 'shortest path'; +} + +{ + my $collection = Slic3r::ExtrusionPath::Collection->new(paths => [ + map Slic3r::ExtrusionPath->pack(polyline => $_, role => 0), + Slic3r::Polyline->new([15,0], [10,0], [4,0]), + Slic3r::Polyline->new([10,5], [15,5], [20,5]), + ]); + is_deeply + [ map $_->[X], map @{$_->unpack->polyline}, $collection->shortest_path(Slic3r::Point->new(30,0)) ], + [reverse 4, 10, 15, 10, 15, 20], + 'shortest path'; +} + __END__ From d5537e7797e24e34560f419d7ccb132b2cefcd92 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 20 Dec 2012 18:47:40 +0100 Subject: [PATCH 151/172] Enable retraction for support material too, but only when moving away from support islands. #831 --- lib/Slic3r/GCode.pm | 5 +++-- lib/Slic3r/Layer.pm | 8 ++++++++ lib/Slic3r/Print/Object.pm | 23 ++++++++++++----------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 84622b80f..ae2ae7aa2 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -163,10 +163,11 @@ sub extrude_path { my $gcode = ""; # skip retract for support material - if ($path->role != EXTR_ROLE_SUPPORTMATERIAL) { + { # 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) { + 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 diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 0a0213a0b..80b712a64 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -1,6 +1,7 @@ package Slic3r::Layer; use Moo; +use List::Util qw(first); use Slic3r::Geometry::Clipper qw(union_ex); has 'id' => (is => 'rw', required => 1, trigger => 1); # sequential number of layer, 0-based @@ -18,6 +19,7 @@ has 'flow' => (is => 'ro', default => sub { $Slic3r::flow }); 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'); @@ -102,4 +104,10 @@ sub make_perimeters { $_->make_perimeters for @{$self->regions}; } +sub support_islands_enclose_line { + my $self = shift; + my ($line) = @_; + return (first { $_->encloses_line($line) } @{$self->support_islands}) ? 1 : 0; +} + 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index ba04c6005..a860c625d 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -626,39 +626,40 @@ sub generate_support_material { }; return @paths; }; - my %layer_paths = (); - my %layer_interface_paths = (); + my %layer_paths = (); + my %layer_interface_paths = (); + 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) ]; - return ($paths, $interface_paths); + my $islands = union_ex([ map @$_, map @$_, $layers{$layer_id}, $layers_interfaces{$layer_id} ]); + return ($paths, $interface_paths, $islands); }; Slic3r::parallelize( items => [ keys %layers ], thread_cb => sub { my $q = shift; - my $paths = {}; - my $interface_paths = {}; + my $result = {}; while (defined (my $layer_id = $q->dequeue)) { - ($paths->{$layer_id}, $interface_paths->{$layer_id}) = $process_layer->($layer_id); + $result->{$layer_id} = [ $process_layer->($layer_id) ]; } - return [ $paths, $interface_paths ]; + return $result; }, collect_cb => sub { - my $paths = shift; - $layer_paths{$_} = $paths->[0]{$_} for keys %{$paths->[0]}; - $layer_interface_paths{$_} = $paths->[1]{$_} for keys %{$paths->[1]}; + my $result = shift; + ($layer_paths{$_}, $layer_interface_paths{$_}, $layer_islands{$_}) = @{$result->{$_}} for keys %$result; }, no_threads_cb => sub { - ($layer_paths{$_}, $layer_interface_paths{$_}) = $process_layer->($_) for keys %layers; + ($layer_paths{$_}, $layer_interface_paths{$_}, $layer_islands{$_}) = $process_layer->($_) for keys %layers; }, ); foreach my $layer_id (keys %layer_paths) { 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); push @{$layer->support_fills->paths}, @{$layer_paths{$layer_id}}; From 701c98c5a7825991461be67698d7a49ee2b70f61 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Dec 2012 13:25:03 +0100 Subject: [PATCH 152/172] Export Slic3r::Test::_eq() --- lib/Slic3r/Test.pm | 6 +++++- t/retraction.t | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 7f5f4776a..99a273127 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -2,6 +2,10 @@ package Slic3r::Test; use strict; use warnings; +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(_eq); + use IO::Scalar; use Slic3r::Geometry qw(epsilon); @@ -48,7 +52,7 @@ sub gcode { return $gcode; } -sub compare { +sub _eq { my ($a, $b) = @_; return abs($a - $b) < epsilon; } diff --git a/t/retraction.t b/t/retraction.t index f57b4d3fc..bf3327d48 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -8,7 +8,7 @@ BEGIN { } use Slic3r; -use Slic3r::Test; +use Slic3r::Test qw(_eq); my $config = Slic3r::Config->new_from_defaults; @@ -32,21 +32,21 @@ my $test = sub { if ($info->{dist_Z}) { # lift move or lift + change layer - if (Slic3r::Test::compare($info->{dist_Z}, $conf->retract_lift->[$tool]) - || (Slic3r::Test::compare($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[$tool]) && $conf->retract_lift->[$tool] > 0)) { + if (_eq($info->{dist_Z}, $conf->retract_lift->[$tool]) + || (_eq($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[$tool]) && $conf->retract_lift->[$tool] > 0)) { fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting}); $lifted = 1; } if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift' - if !Slic3r::Test::compare($info->{dist_Z}, -$conf->retract_lift->[$tool]); + if !_eq($info->{dist_Z}, -$conf->retract_lift->[$tool]); $lifted = 0; } } if ($info->{retracting}) { fail 'retracted by the correct amount' - if !Slic3r::Test::compare(-$info->{dist_E}, $conf->retract_length->[$tool]); + if !_eq(-$info->{dist_E}, $conf->retract_length->[$tool]); fail 'combining retraction and travel with G0' if $cmd ne 'G0' && $conf->g0 && ($info->{dist_Z} || $info->{dist_XY}); $retracted[$tool] = 1; @@ -59,7 +59,7 @@ my $test = sub { $expected_amount = $conf->retract_length_toolchange->[$tool] + $conf->retract_restart_extra_toolchange->[$tool]; } fail 'unretracted by the correct amount' - if !Slic3r::Test::compare($info->{dist_E}, $expected_amount); + if !_eq($info->{dist_E}, $expected_amount); $retracted[$tool] = 0; } } From dc4ada2374f5077ca929dea947c04af1bbe2a79f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Dec 2012 15:14:44 +0100 Subject: [PATCH 153/172] Fix retraction tests and fix retract_extra_length and lift in multiple extruders environments --- lib/Slic3r/Extruder.pm | 1 + lib/Slic3r/GCode.pm | 15 ++++++++++----- lib/Slic3r/Test.pm | 1 + t/retraction.t | 33 +++++++++++++++++++++++---------- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 8bac9d0d9..9cfc4dcf7 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -14,6 +14,7 @@ has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; 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 {{}}); diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 72bc89fa1..b5e944551 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -181,7 +181,7 @@ sub extrude_path { if !points_coincide($self->last_pos, $path->points->[0]); # compensate retraction - $gcode .= $self->unretract if $self->extruder->retracted; + $gcode .= $self->unretract; my $area; # mm^3 of extrudate per mm of tool movement if ($path->role == EXTR_ROLE_BRIDGE) { @@ -276,7 +276,8 @@ sub retract { $gcode .= $self->G1(@$lift); } } - $self->extruder->retracted($self->extruder->retracted + $length + $restart_extra); + $self->extruder->retracted($self->extruder->retracted + $length); + $self->extruder->restart_extra($restart_extra); $self->lifted($self->extruder->retract_lift) if $lift; # reset extrusion distance during retracts @@ -297,9 +298,13 @@ sub unretract { $self->lifted(0); } - $self->speed('retract'); - $gcode .= $self->G0(undef, undef, $self->extruder->retracted, "compensate retraction"); - $self->extruder->retracted(0); + my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra; + if ($to_unretract) { + $self->speed('retract'); + $gcode .= $self->G0(undef, undef, $to_unretract, "compensate retraction"); + $self->extruder->retracted(0); + $self->extruder->restart_extra(0); + } return $gcode; } diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 99a273127..3c92f2eab 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -34,6 +34,7 @@ sub init_print { 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); diff --git a/t/retraction.t b/t/retraction.t index bf3327d48..23192ce89 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 9; +use Test::More tests => 12; use strict; use warnings; @@ -19,34 +19,46 @@ my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $conf); 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 $lifted = 0; my $changed_tool = 0; + my $wait_for_toolchange = 0; Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { my ($self, $cmd, $args, $info) = @_; if ($cmd =~ /^T(\d+)/) { $tool = $1; $changed_tool = 1; + $wait_for_toolchange = 0; + $toolchange_count[$tool] //= 0; + $toolchange_count[$tool]++; + } elsif ($cmd =~ /^G[01]$/ && !$args->{Z}) { # ignore lift taking place after retraction + fail 'toolchange happens right after retraction' if $wait_for_toolchange; } if ($info->{dist_Z}) { # lift move or lift + change layer - if (_eq($info->{dist_Z}, $conf->retract_lift->[$tool]) - || (_eq($info->{dist_Z}, $conf->layer_height + $conf->retract_lift->[$tool]) && $conf->retract_lift->[$tool] > 0)) { + if (_eq($info->{dist_Z}, $print->extruders->[$tool]->retract_lift) + || (_eq($info->{dist_Z}, $conf->layer_height + $print->extruders->[$tool]->retract_lift) && $print->extruders->[$tool]->retract_lift > 0)) { fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting}); $lifted = 1; } if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift' - if !_eq($info->{dist_Z}, -$conf->retract_lift->[$tool]); + if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift); $lifted = 0; } } if ($info->{retracting}) { - fail 'retracted by the correct amount' - if !_eq(-$info->{dist_E}, $conf->retract_length->[$tool]); + if (_eq(-$info->{dist_E}, $print->extruders->[$tool]->retract_length)) { + # okay + } elsif (_eq(-$info->{dist_E}, $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; @@ -54,16 +66,17 @@ my $test = sub { if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; if ($retracted[$tool]) { - my $expected_amount = $conf->retract_length->[$tool] + $conf->retract_restart_extra->[$tool]; - if ($changed_tool) { - $expected_amount = $conf->retract_length_toolchange->[$tool] + $conf->retract_restart_extra_toolchange->[$tool]; + my $expected_amount = $print->extruders->[$tool]->retract_length + $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; } fail 'unretracted by the correct amount' if !_eq($info->{dist_E}, $expected_amount); $retracted[$tool] = 0; } } - if ($info->{travel} && $info->{dist_XY} >= $conf->retract_before_travel->[$tool]) { + if ($info->{travel} && $info->{dist_XY} >= $print->extruders->[$tool]->retract_before_travel) { fail 'retracted before long travel move' if !$retracted[$tool]; } }); From e372372a5d40f0cbdf1a765cd43eb571ad0f8baa Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Dec 2012 15:25:08 +0100 Subject: [PATCH 154/172] Bugfix: extra length on restart didn't work correctly for negative values. #654 --- t/retraction.t | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/t/retraction.t b/t/retraction.t index 23192ce89..8ad1fbc79 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 12; +use Test::More tests => 16; use strict; use warnings; @@ -96,6 +96,9 @@ my $retract_tests = sub { $conf->set('retract_restart_extra', [1]); ok $test->($conf), "restart extra length$descr"; + $conf->set('retract_restart_extra', [-1]); + ok $test->($conf), "negative restart extra length$descr"; + $conf->set('retract_lift', [1]); ok $test->($conf), "lift$descr"; }; From 5930267de9c2cb731d0e641a74e294b67900c537 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 21 Dec 2012 20:25:48 +0100 Subject: [PATCH 155/172] Bugfix: some top/bottom solid shells are not treated as solid. #689 --- lib/Slic3r/Print/Object.pm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a860c625d..a7f82168a 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -336,10 +336,8 @@ sub discover_horizontal_shells { } foreach my $type (S_TYPE_TOP, S_TYPE_BOTTOM) { - # find surfaces of current type for current layer - # and offset them to take perimeters into account - my @surfaces = map $_->offset($Slic3r::Config->perimeters * $layerm->perimeter_flow->scaled_width), - grep $_->surface_type == $type, @{$layerm->fill_surfaces} or next; + # find slices of current type for current layer + my @surfaces = grep $_->surface_type == $type, @{$layerm->slices} 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'); From 15f07197d8bbdabdad3b1d57907983ffc7108252 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 22 Dec 2012 23:57:39 +0100 Subject: [PATCH 156/172] Don't store ->surfaces anymore. Save memory, save time --- lib/Slic3r/Layer/Region.pm | 49 ++++++++++---------------------------- lib/Slic3r/Print.pm | 7 ++---- lib/Slic3r/Print/Object.pm | 9 ++++--- 3 files changed, 19 insertions(+), 46 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 18b039ce0..63e41c8c4 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -32,13 +32,7 @@ has 'thin_walls' => (is => 'rw', default => sub { [] }); # need to be filled with a medial axis has 'thin_fills' => (is => 'rw', default => sub { [] }); -# collection of expolygons generated by offsetting the innermost perimeter(s) -# they represent boundaries of areas to fill, typed (top/bottom/internal) -has 'surfaces' => (is => 'rw', default => sub { [] }); - -# collection of surfaces for infill generation. the difference between surfaces -# fill_surfaces is that this one honors fill_density == 0 and turns small internal -# surfaces into solid ones +# collection of surfaces for infill generation has 'fill_surfaces' => (is => 'rw', default => sub { [] }); # ordered collection of extrusion paths/loops to build all perimeters @@ -160,7 +154,7 @@ sub make_perimeters { ])}; $self->perimeters([]); - $self->surfaces([]); + $self->fill_surfaces([]); $self->thin_fills([]); # for each island: @@ -227,9 +221,14 @@ sub make_perimeters { # create one more offset to be used as boundary for fill { - my @fill_boundaries = map $_->offset_ex(-$distance), @last_offsets; + my @fill_boundaries = @{union_ex([ + Slic3r::Geometry::Clipper::offset( + [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -1.5*$distance)], + +0.5*$distance, + ), + ])}; $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; - push @{ $self->surfaces }, @fill_boundaries; + push @{ $self->fill_surfaces }, @fill_boundaries; } # fill gaps @@ -390,48 +389,26 @@ sub _add_perimeter { sub prepare_fill_surfaces { my $self = shift; - my @surfaces = @{$self->surfaces}; - # if no solid layers are requested, turn top/bottom surfaces to internal - # note that this modifies $self->surfaces in place if ($Slic3r::Config->top_solid_layers == 0) { - $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @surfaces; + $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_TOP, @{$self->fill_surfaces}; } if ($Slic3r::Config->bottom_solid_layers == 0) { - $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @surfaces; + $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}; } # if hollow object is requested, remove internal surfaces if ($Slic3r::Config->fill_density == 0) { - @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces; - } - - # remove unprintable regions (they would slow down the infill process and also cause - # some weird failures during bridge neighbor detection) - { - my $distance = $self->infill_flow->scaled_spacing / 2; - @surfaces = map { - my $surface = $_; - - # offset inwards - my @offsets = $surface->expolygon->offset_ex(-$distance); - @offsets = @{union_ex([ Slic3r::Geometry::Clipper::offset([ map @$_, @offsets ], $distance)] )}; # isn't the union_ex useless? - map Slic3r::Surface->new( - expolygon => $_, - surface_type => $surface->surface_type, - ), @offsets; - } @surfaces; + @{$self->fill_surfaces} = grep $_->surface_type != S_TYPE_INTERNAL, @{$self->fill_surfaces}; } # turn too small internal regions into solid regions { 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, @surfaces; + my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @{$self->fill_surfaces}; $_->surface_type(S_TYPE_INTERNALSOLID) for @small; Slic3r::debugf "identified %d small solid surfaces at layer %d\n", scalar(@small), $self->id if @small > 0; } - - $self->fill_surfaces([@surfaces]); } # make bridges printable diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 85f7470ff..fcbfd580b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -345,8 +345,8 @@ sub export_gcode { for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); } - # this will clip $layer->surfaces to the infill boundaries - # and split them in top/bottom/internal surfaces; + # this will transform $layer->fill_surfaces from expolygon + # to typed top/bottom/internal surfaces; $status_cb->(30, "Detecting solid surfaces"); $_->detect_surfaces_type for @{$self->objects}; @@ -364,9 +364,6 @@ sub export_gcode { $status_cb->(60, "Generating horizontal shells"); $_->discover_horizontal_shells for @{$self->objects}; - # free memory - $_->surfaces(undef) for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; - # combine fill surfaces to honor the "infill every N layers" option $status_cb->(70, "Combining infill"); $_->combine_infill for @{$self->objects}; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a7f82168a..2e1930ca0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -304,14 +304,14 @@ sub detect_surfaces_type { # clip surfaces to the fill boundaries foreach my $layer (@{$self->layers}) { my $layerm = $layer->regions->[$region_id]; - my $fill_boundaries = [ map @$_, @{$layerm->surfaces} ]; - @{$layerm->surfaces} = (); + my $fill_boundaries = [ map @$_, @{$layerm->fill_surfaces} ]; + @{$layerm->fill_surfaces} = (); foreach my $surface (@{$layerm->slices}) { my $intersection = intersection_ex( [ $surface->p ], $fill_boundaries, ); - push @{$layerm->surfaces}, map Slic3r::Surface->new + push @{$layerm->fill_surfaces}, map Slic3r::Surface->new (expolygon => $_, surface_type => $surface->surface_type), @$intersection; } @@ -352,14 +352,13 @@ sub discover_horizontal_shells { next if $n < 0 || $n >= $self->layer_count; Slic3r::debugf " looking for neighbors on layer %d...\n", $n; - my @neighbor_surfaces = @{$self->layers->[$n]->regions->[$region_id]->surfaces}; my @neighbor_fill_surfaces = @{$self->layers->[$n]->regions->[$region_id]->fill_surfaces}; # find intersection between neighbor and current layer's surfaces # intersections have contours and holes my $new_internal_solid = intersection_ex( $surfaces_p, - [ map $_->p, grep { $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALSOLID } @neighbor_surfaces ], + [ map $_->p, grep { $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALSOLID } @neighbor_fill_surfaces ], undef, 1, ); next if !@$new_internal_solid; From db754dca4d8fb2811b5c89e0a6a6e485a1aca019 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 23 Dec 2012 15:56:13 +0100 Subject: [PATCH 157/172] Add unit test to check that nested config options work --- t/custom_gcode.t | 57 +++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/t/custom_gcode.t b/t/custom_gcode.t index ea36c0cf1..69ab8fac8 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 1; +use Test::More tests => 2; use strict; use warnings; @@ -10,32 +10,43 @@ BEGIN { use Slic3r; use Slic3r::Test; -my $config = Slic3r::Config->new_from_defaults; - -my $test = sub { - my ($conf) = @_; - $conf ||= $config; +{ + my $config = Slic3r::Config->new_from_defaults; - my $print = Slic3r::Test::init_print('2x20x10', config => $conf); - - my $last_move_was_z_change = 0; - Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { - my ($self, $cmd, $args, $info) = @_; + my $test = sub { + my ($conf) = @_; + $conf ||= $config; - if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } - if (!$last_move_was_z_change && $cmd eq $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } + my $print = Slic3r::Test::init_print('2x20x10', config => $conf); - $last_move_was_z_change = (defined $info->{dist_Z} && $info->{dist_Z} > 0); - }); + my $last_move_was_z_change = 0; + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { + fail 'custom layer G-code was not applied after Z change'; + } + if (!$last_move_was_z_change && $cmd eq $config->layer_gcode) { + fail 'custom layer G-code was not applied after Z change'; + } + + $last_move_was_z_change = (defined $info->{dist_Z} && $info->{dist_Z} > 0); + }); + + 1; + }; - 1; -}; + $config->set('layer_gcode', '_MY_CUSTOM_GCODE_'); + ok $test->(), "custom layer G-code is applied after Z move and before other moves"; +} -$config->set('layer_gcode', '_MY_CUSTOM_GCODE_'); -ok $test->(), "custom layer G-code is applied after Z move and before other moves"; +#========================================================== + +{ + my $config = Slic3r::Config->new_from_defaults; + is $config->replace_options('[temperature_[foo]]', { foo => '0' }), + 200, + "nested config options"; +} __END__ From e59ed7efb726810c07a6b0028d67532ee9f680e7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 23 Dec 2012 16:29:08 +0100 Subject: [PATCH 158/172] New toolchange_gcode option. #547 --- README.markdown | 1 + lib/Slic3r/Config.pm | 14 +++++++++++++- lib/Slic3r/GCode.pm | 8 ++++++++ lib/Slic3r/GUI/Tab.pm | 5 +++++ slic3r.pl | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index b355f935b..43bf64017 100644 --- a/README.markdown +++ b/README.markdown @@ -176,6 +176,7 @@ The author of the Silk icon set is Mark James. the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). --layer-gcode Load layer-change G-code from the supplied file (default: nothing). + --toolchange-gcode Load tool-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) --only-retract-when-crossing-perimeters diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 44d748955..c9594cac3 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -607,6 +607,18 @@ END deserialize => sub { join "\n", split /\\n/, $_[0] }, default => '', }, + 'toolchange_gcode' => { + label => 'Tool change G-code', + tooltip => 'This custom code is inserted at every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder].', + cli => 'toolchange-gcode=s', + type => 's', + multiline => 1, + full_width => 1, + height => 50, + serialize => sub { join '\n', split /\R+/, $_[0] }, + deserialize => sub { join "\n", split /\\n/, $_[0] }, + default => '', + }, 'post_process' => { label => 'Post-processing scripts', tooltip => 'If you want to process the output G-code through custom scripts, just list their absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed the absolute path to the G-code file as the first argument, and they can access the Slic3r config settings by reading environment variables.', @@ -925,7 +937,7 @@ sub new_from_cli { delete $args{$_} for grep !defined $args{$_}, keys %args; - for (qw(start end layer)) { + for (qw(start end layer toolchange)) { my $opt_key = "${_}_gcode"; if ($args{$opt_key}) { die "Invalid value for --${_}-gcode: file does not exist\n" diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index d71be3040..2bf141556 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -442,6 +442,14 @@ sub set_extruder { my $gcode = ""; $gcode .= $self->retract(toolchange => 1) if defined $self->extruder; + # append custom toolchange G-code + if (defined $self->extruder && $Slic3r::Config->toolchange_gcode) { + $gcode .= sprintf "%s\n", $Slic3r::Config->replace_options($Slic3r::Config->toolchange_gcode, { + previous_extruder => $self->extruder->id, + next_extruder => $extruder->id, + }); + } + # set the new extruder $self->extruder($extruder); $gcode .= sprintf "T%d%s\n", $extruder->id, ($Slic3r::Config->gcode_comments ? ' ; change extruder' : ''); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 646b62e3e..d4479d6bd 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -656,6 +656,11 @@ sub build { no_labels => 1, options => [qw(layer_gcode)], }, + { + title => 'Tool change G-code', + no_labels => 1, + options => [qw(toolchange_gcode)], + }, ]); $self->{extruder_pages} = []; diff --git a/slic3r.pl b/slic3r.pl index b4325abf1..51a58271d 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -224,6 +224,7 @@ $j the default commands (turn off temperature [M104 S0], home X axis [G28 X], disable motors [M84]). --layer-gcode Load layer-change G-code from the supplied file (default: nothing). + --toolchange-gcode Load tool-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) --only-retract-when-crossing-perimeters From 0da71dbdfa2ebdf35a34fc8354f71689e6f32b23 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 23 Dec 2012 20:20:17 +0100 Subject: [PATCH 159/172] Fix regression causing wrong number of solid shells when using fill_density = 0 (includes regression test) --- MANIFEST | 1 + lib/Slic3r/Layer/Region.pm | 5 ---- lib/Slic3r/Print/Object.pm | 9 +++++++ t/shells.t | 48 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 t/shells.t diff --git a/MANIFEST b/MANIFEST index c5869c19c..5dbd832e8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -62,6 +62,7 @@ t/geometry.t t/polyclip.t t/retraction.t t/serialize.t +t/shells.t t/stl.t t/vibrationlimit.t utils/amf-to-stl.pl diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 63e41c8c4..beb579fc3 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -396,11 +396,6 @@ sub prepare_fill_surfaces { if ($Slic3r::Config->bottom_solid_layers == 0) { $_->surface_type(S_TYPE_INTERNAL) for grep $_->surface_type == S_TYPE_BOTTOM, @{$self->fill_surfaces}; } - - # 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}; - } # turn too small internal regions into solid regions { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2e1930ca0..78c637aac 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -410,6 +410,15 @@ sub discover_horizontal_shells { @{$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}; + } + } } } diff --git a/t/shells.t b/t/shells.t new file mode 100644 index 000000000..ae5b7670a --- /dev/null +++ b/t/shells.t @@ -0,0 +1,48 @@ +use Test::More tests => 2; +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('perimeters', 0); + + my $test = sub { + my ($conf) = @_; + $conf ||= $config; + + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + + my %layers_with_shells = (); # Z => $count + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($self->Z > 0) { + $layers_with_shells{$self->Z} //= 0; + $layers_with_shells{$self->Z} = 1 if $info->{extruding} && $info->{dist_XY} > 0; + } + }); + my @shells = @layers_with_shells{sort { $a <=> $b } keys %layers_with_shells}; + fail "wrong number of bottom solid layers" + unless !defined(first { !$_ } @shells[0..$config->bottom_solid_layers-1]); + fail "wrong number of top solid layers" + unless !defined(first { !$_ } @shells[-$config->top_solid_layers..-1]); + 1; + }; + + 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"; +} + +__END__ From 547e62d0a8b7c8717b9975f7b0eee3dd59d7c832 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 27 Dec 2012 14:16:50 +0100 Subject: [PATCH 160/172] Added a couple Clipper tests. #858 --- t/clipper.t | 108 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/t/clipper.t b/t/clipper.t index 59051f156..7d316dde9 100644 --- a/t/clipper.t +++ b/t/clipper.t @@ -2,52 +2,72 @@ use Test::More; use strict; use warnings; -plan tests => 1; +plan tests => 3; use Math::Clipper ':all'; -my $clipper = Math::Clipper->new; -my $square = [ # ccw - [10, 10], - [20, 10], - [20, 20], - [10, 20], -]; - -my $hole_in_square = [ # cw - [14, 14], - [14, 16], - [16, 16], - [16, 14], -]; - -my $square2 = [ # ccw - [5, 12], - [25, 12], - [25, 18], - [5, 18], -]; - -$clipper->add_subject_polygons([ $square, $hole_in_square ]); -$clipper->add_clip_polygons([ $square2 ]); -my $intersection = $clipper->ex_execute(CT_INTERSECTION, PFT_NONZERO, PFT_NONZERO); - -is_deeply $intersection, [ - { - holes => [ - [ - [14, 16], - [16, 16], - [16, 14], - [14, 14], +{ + my $square = [ # ccw + [10, 10], + [20, 10], + [20, 20], + [10, 20], + ]; + my $hole_in_square = [ # cw + [14, 14], + [14, 16], + [16, 16], + [16, 14], + ]; + my $square2 = [ # ccw + [5, 12], + [25, 12], + [25, 18], + [5, 18], + ]; + my $clipper = Math::Clipper->new; + $clipper->add_subject_polygons([ $square, $hole_in_square ]); + $clipper->add_clip_polygons([ $square2 ]); + my $intersection = $clipper->ex_execute(CT_INTERSECTION, PFT_NONZERO, PFT_NONZERO); + is_deeply $intersection, [ + { + holes => [ + [ + [14, 16], + [16, 16], + [16, 14], + [14, 14], + ], ], - ], - outer => [ - [10, 18], - [10, 12], - [20, 12], - [20, 18], - ], - }, -], 'hole is preserved after intersection'; + outer => [ + [10, 18], + [10, 12], + [20, 12], + [20, 18], + ], + }, + ], 'hole is preserved after intersection'; +} + +#========================================================== + +{ + my $contour1 = [ [0,0], [40,0], [40,40], [0,40] ]; # ccw + my $contour2 = [ [10,10], [30,10], [30,30], [10,30] ]; # ccw + my $hole = [ [15,15], [15,25], [25,25], [25,15] ]; # cw + + my $clipper = Math::Clipper->new; + $clipper->add_subject_polygons([ $contour1, $contour2, $hole ]); + my $union = $clipper->ex_execute(CT_UNION, PFT_NONZERO, PFT_NONZERO); + is_deeply $union, [{ holes => [], outer => [ [0,40], [0,0], [40,0], [40,40] ] }], + 'union of two ccw and one cw is a contour with no holes'; + + $clipper->clear; + $clipper->add_subject_polygons([ $contour1, $contour2 ]); + $clipper->add_clip_polygons([ $hole ]); + my $diff = $clipper->ex_execute(CT_DIFFERENCE, PFT_NONZERO, PFT_NONZERO); + is_deeply $diff, [{ holes => [[ [15,25], [25,25], [25,15], [15,15] ]], outer => [ [0,40], [0,0], [40,0], [40,40] ] }], + 'difference of a cw from two ccw is a contour with one hole'; +} + From de5b8b9f4d47b2913d411a54b072380882dd9e65 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 29 Dec 2012 19:29:22 +0100 Subject: [PATCH 161/172] Rename t/stl.t to t/slice.t --- MANIFEST | 2 +- t/{stl.t => slice.t} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename t/{stl.t => slice.t} (100%) diff --git a/MANIFEST b/MANIFEST index 5dbd832e8..094ed02e9 100644 --- a/MANIFEST +++ b/MANIFEST @@ -63,7 +63,7 @@ t/polyclip.t t/retraction.t t/serialize.t t/shells.t -t/stl.t +t/slice.t t/vibrationlimit.t utils/amf-to-stl.pl utils/file_info.pl diff --git a/t/stl.t b/t/slice.t similarity index 100% rename from t/stl.t rename to t/slice.t From 6e6bc746367b4f50dfdac108ce067ac94ff31a30 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 30 Dec 2012 16:27:20 +0100 Subject: [PATCH 162/172] Added failing test case for troubleshooting unexpected filled holes. #858 --- MANIFEST | 1 + lib/Slic3r/Layer/Region.pm | 29 +++++++++++------------ lib/Slic3r/Test.pm | 17 +++++++++++++- t/loops.t | 47 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 t/loops.t diff --git a/MANIFEST b/MANIFEST index 094ed02e9..83d89ef25 100644 --- a/MANIFEST +++ b/MANIFEST @@ -59,6 +59,7 @@ t/custom_gcode.t t/dynamic.t t/fill.t t/geometry.t +t/loops.t t/polyclip.t t/retraction.t t/serialize.t diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index beb579fc3..3a1d2b0fa 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -69,20 +69,8 @@ sub make_surfaces { my $self = shift; my ($loops) = @_; - return if !@$loops; - { - my $safety_offset = scale 0.1; - # merge everything - my $expolygons = [ map $_->offset_ex(-$safety_offset), @{union_ex(safety_offset($loops, $safety_offset))} ]; - - Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n", - scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops); - - $self->slices([ - map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), - @$expolygons - ]); - } + return if !@$loops; + $self->slices([ _merge_loops($loops) ]); # the contours must be offsetted by half extrusion width inwards { @@ -129,6 +117,19 @@ sub make_surfaces { } } +sub _merge_loops { + my ($loops) = @_; + + my $safety_offset = scale 0.1; + # merge everything + my $expolygons = [ map $_->offset_ex(-$safety_offset), @{union_ex(safety_offset($loops, $safety_offset))} ]; + + Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n", + scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops); + + return map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), @$expolygons; +} + sub make_perimeters { my $self = shift; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 3c92f2eab..1e4531cd4 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -7,7 +7,8 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw(_eq); use IO::Scalar; -use Slic3r::Geometry qw(epsilon); +use List::Util qw(first); +use Slic3r::Geometry qw(epsilon X Y Z); my %cuboids = ( '20mm_cube' => [20,20,20], @@ -58,6 +59,20 @@ sub _eq { return abs($a - $b) < epsilon; } +sub add_facet { + my ($facet, $vertices, $facets) = @_; + + push @$facets, []; + for my $i (0..2) { + my $v = first { $vertices->[$_][X] == $facet->[$i][X] && $vertices->[$_][Y] == $facet->[$i][Y] && $vertices->[$_][Z] == $facet->[$i][Z] } 0..$#$vertices; + if (!defined $v) { + push @$vertices, [ @{$facet->[$i]}[X,Y,Z] ]; + $v = $#$vertices; + } + $facets->[-1][$i] = $v; + } +} + package Slic3r::Test::GCodeReader; use Moo; diff --git a/t/loops.t b/t/loops.t new file mode 100644 index 000000000..c36505e4e --- /dev/null +++ b/t/loops.t @@ -0,0 +1,47 @@ +use Test::More; +use strict; +use warnings; + +plan tests => 4; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use Slic3r; +use Slic3r::Test; + +{ + my (@vertices, @facets) = (); + Slic3r::Test::add_facet($_, \@vertices, \@facets) for + # external surface below the slicing Z + [ [0,0,0], [20,0,10], [0,0,10] ], + [ [20,0,0], [20,20,10], [20,0,10] ], + [ [20,20,0], [0,20,10], [20,20,10] ], + [ [0,20,0], [0,0,10], [0,20,10] ], + + # external insetted surface above the slicing Z + [ [2,2,10], [18,2,10], [2,2,20] ], + [ [18,2,10], [18,18,10], [18,2,20] ], + [ [18,18,10], [2,18,10], [18,18,20] ], + [ [2,18,10], [2,2,10], [2,18,20] ], + + # insetted hole below the slicing Z + [ [15,5,0], [5,5,10], [15,5,10] ], + [ [15,15,0], [15,5,10], [15,15,10] ], + [ [5,15,0], [15,15,10], [5,15,10] ], + [ [5,5,0], [5,15,10], [5,5,10] ]; + + my $mesh = Slic3r::TriangleMesh->new(vertices => \@vertices, facets => \@facets); + my @lines = map $mesh->intersect_facet($_, 10), 0..$#facets; + my $loops = Slic3r::TriangleMesh::make_loops(\@lines); + is scalar(@$loops), 3, 'correct number of loops detected'; + is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected'; + + my @surfaces = Slic3r::Layer::Region::_merge_loops($loops); + is scalar(@surfaces), 1, 'one surface detected'; + is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole'; +} + +__END__ From 141a8d39898b3b933a422b4b6f70201f356aed54 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 30 Dec 2012 17:57:30 +0100 Subject: [PATCH 163/172] Bugfix: some holes being closed incorrectly. #858 --- lib/Slic3r/Geometry/Clipper.pm | 2 +- lib/Slic3r/Layer/Region.pm | 25 +++++++++++++++++++++---- t/loops.t | 8 +++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index d91b185ea..9dedc7a96 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -14,7 +14,7 @@ our $clipper = Math::Clipper->new; sub safety_offset { my ($polygons, $factor) = @_; - return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100000, JT_MITER, 2); + return Math::Clipper::offset($polygons, $factor // (scale 1e-05), 100000, JT_MITER, 2); } sub offset { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 3a1d2b0fa..2a2736719 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -118,11 +118,28 @@ sub make_surfaces { } sub _merge_loops { - my ($loops) = @_; + my ($loops, $safety_offset) = @_; - my $safety_offset = scale 0.1; - # merge everything - my $expolygons = [ map $_->offset_ex(-$safety_offset), @{union_ex(safety_offset($loops, $safety_offset))} ]; + # Input loops are not suitable for evenodd nor nonzero fill types, as we might get + # two consecutive concentric loops having the same winding order - and we have to + # respect such order. In that case, evenodd would create wrong inversions, and nonzero + # would ignore holes inside two concentric contours. + # So we're ordering loops and collapse consecutive concentric loops having the same + # winding order. + # TODO: find a faster algorithm for this. + my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first + $safety_offset //= scale 0.1; + @loops = @{ safety_offset(\@loops, $safety_offset) }; + my $expolygons = []; + while (my $loop = shift @loops) { + bless $loop, 'Slic3r::Polygon'; + if ($loop->is_counter_clockwise) { + $expolygons = union_ex([ $loop, map @$_, @$expolygons ]); + } else { + $expolygons = diff_ex([ map @$_, @$expolygons ], [$loop]); + } + } + $expolygons = [ map $_->offset_ex(-$safety_offset), @$expolygons ]; Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n", scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops); diff --git a/t/loops.t b/t/loops.t index c36505e4e..d0340b200 100644 --- a/t/loops.t +++ b/t/loops.t @@ -13,6 +13,12 @@ use Slic3r; use Slic3r::Test; { + # We only need to slice at one height, so we'll build a non-manifold mesh + # that still produces complete loops at that height. Triangular walls are + # enough for this purpose. + # Basically we want to check what happens when three concentric loops happen + # to be at the same height, the two external ones being ccw and the other being + # a hole, thus cw. my (@vertices, @facets) = (); Slic3r::Test::add_facet($_, \@vertices, \@facets) for # external surface below the slicing Z @@ -39,7 +45,7 @@ use Slic3r::Test; is scalar(@$loops), 3, 'correct number of loops detected'; is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected'; - my @surfaces = Slic3r::Layer::Region::_merge_loops($loops); + my @surfaces = Slic3r::Layer::Region::_merge_loops($loops, 0); is scalar(@surfaces), 1, 'one surface detected'; is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole'; } From 177414301139704cb643560a2346c7f0917d5951 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 31 Dec 2012 14:11:15 +0100 Subject: [PATCH 164/172] Bugfix: PlanePath fill patterns were not working anymore. #880 --- lib/Slic3r/Fill/PlanePath.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm index acc1e3318..174c36e1b 100644 --- a/lib/Slic3r/Fill/PlanePath.pm +++ b/lib/Slic3r/Fill/PlanePath.pm @@ -3,7 +3,7 @@ use Moo; extends 'Slic3r::Fill::Base'; -use Slic3r::Geometry qw(scale bounding_box X1 Y1 X2 Y2); +use Slic3r::Geometry qw(scale X1 Y1 X2 Y2); sub multiplier () { 1 } @@ -27,7 +27,7 @@ sub fill_surface { $self->rotate_points($expolygon, $rotate_vector); my $distance_between_lines = scale $params{flow_spacing} / $params{density} * $self->multiplier; - my $bounding_box = [ bounding_box(map @$_, $expolygon) ]; + my $bounding_box = [ Slic3r::Geometry::bounding_box([map @$_, @$expolygon]) ]; my $bounding_box_polygon = Slic3r::Polygon->new([ [ $bounding_box->[X1], $bounding_box->[Y1] ], [ $bounding_box->[X2], $bounding_box->[Y1] ], From a633180518ebfe2ec1805aa9d63cc9c9f4c00ccb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 31 Dec 2012 15:05:32 +0100 Subject: [PATCH 165/172] Add regression test to trap fatal errors during hilbertcurve infill generation. #880 --- t/fill.t | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/t/fill.t b/t/fill.t index eb7c8fdb8..5576374a8 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 8; +plan tests => 9; BEGIN { use FindBin; @@ -12,6 +12,7 @@ BEGIN { use Slic3r; use Slic3r::Geometry qw(scale X Y); use Slic3r::Surface qw(:types); +use Slic3r::Test; sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } @@ -87,4 +88,11 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } 'shortest path'; } +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('fill_pattern', 'hilbertcurve'); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok Slic3r::Test::gcode($print), 'successful hilbertcurve infill generation'; +} + __END__ From 0e9d96100b28be2794804fc73c200423b8b9ef8e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 1 Jan 2013 23:16:51 +0100 Subject: [PATCH 166/172] New unit test for layer heights --- MANIFEST | 1 + t/layers.t | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 t/layers.t diff --git a/MANIFEST b/MANIFEST index 83d89ef25..45612a57b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -59,6 +59,7 @@ t/custom_gcode.t t/dynamic.t t/fill.t t/geometry.t +t/layers.t t/loops.t t/polyclip.t t/retraction.t diff --git a/t/layers.t b/t/layers.t new file mode 100644 index 000000000..3d381788a --- /dev/null +++ b/t/layers.t @@ -0,0 +1,58 @@ +use Test::More tests => 4; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use List::Util qw(first); +use Slic3r; +use Slic3r::Test qw(_eq); + +my $config = Slic3r::Config->new_from_defaults; + +my $test = sub { + my ($conf) = @_; + $conf ||= $config; + + my $print = Slic3r::Test::init_print('20mm_cube', config => $conf); + + my @z = (); + my @increments = (); + Slic3r::Test::GCodeReader->new(gcode => Slic3r::Test::gcode($print))->parse(sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{dist_Z}) { + push @z, 1*$args->{Z}; + push @increments, $info->{dist_Z}; + } + }); + + fail 'wrong first layer height' + if $z[0] ne $config->get_value('first_layer_height') + $config->z_offset; + + fail 'wrong second layer height' + if $z[1] ne $config->get_value('first_layer_height') + $config->get_value('layer_height') + $config->z_offset; + + fail 'wrong layer height' + if first { !_eq($_, $config->layer_height) } @increments[1..$#increments]; + + 1; +}; + +$config->set('layer_height', 0.3); +$config->set('first_layer_height', 0.2); +ok $test->(), "absolute first layer height"; + +$config->set('first_layer_height', '60%'); +ok $test->(), "relative first layer height"; + +$config->set('z_offset', 0.9); +ok $test->(), "positive Z offset"; + +$config->set('z_offset', -0.8); +ok $test->(), "negative Z offset"; + +__END__ From 30d9b2e508471b06e9b081723ef5698c37716c3b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 1 Jan 2013 23:28:48 +0100 Subject: [PATCH 167/172] Refactoring: initialize all layers at once and avoid duplication of slicing height math. #637 --- lib/Slic3r/Layer.pm | 4 +++- lib/Slic3r/Print.pm | 2 +- lib/Slic3r/Print/Object.pm | 35 +++++++++++++++++++++++++---------- lib/Slic3r/TriangleMesh.pm | 6 ++---- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 80b712a64..d22188ab5 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -12,7 +12,7 @@ has 'slicing_errors' => (is => 'rw'); has 'slice_z' => (is => 'lazy'); has 'print_z' => (is => 'lazy'); has 'height' => (is => 'lazy'); -has 'flow' => (is => 'ro', default => sub { $Slic3r::flow }); +has 'flow' => (is => 'lazy'); # collection of expolygons generated by slicing the original geometry; # also known as 'islands' (all regions are merged here) @@ -51,6 +51,8 @@ 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 interface paths in unscaled coordinates sub support_material_interface_height { my $self = shift; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index fcbfd580b..fe9f164b7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -554,7 +554,7 @@ sub make_skirt { $skirt_height = $self->layer_count if $skirt_height > $self->layer_count; my @points = (); foreach my $obj_idx (0 .. $#{$self->objects}) { - my @layers = map $self->objects->[$obj_idx]->layer($_), 0..($skirt_height-1); + my @layers = map $self->objects->[$obj_idx]->layers->[$_], 0..($skirt_height-1); my @layer_points = ( (map @$_, map @$_, map @{$_->slices}, @layers), (map @$_, map @{$_->thin_walls}, map @{$_->regions}, @layers), diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 78c637aac..86cebb675 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -2,7 +2,7 @@ package Slic3r::Print::Object; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale unscale deg2rad scaled_epsilon); +use Slic3r::Geometry qw(Z scale unscale deg2rad scaled_epsilon); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); use Slic3r::Surface ':types'; @@ -13,21 +13,36 @@ has 'size' => (is => 'rw', required => 1); has 'copies' => (is => 'rw', default => sub {[ [0,0] ]}); has 'layers' => (is => 'rw', default => sub { [] }); +sub BUILD { + my $self = shift; + + # make layers + while (!@{$self->layers} || $self->layers->[-1]->slice_z < $self->size->[Z]) { + push @{$self->layers}, Slic3r::Layer->new( + object => $self, + id => $#{$self->layers} + 1, + ); + } +} + sub layer_count { my $self = shift; return scalar @{ $self->layers }; } -sub layer { +sub get_layer_range { my $self = shift; - my ($layer_id) = @_; + my ($min_z, $max_z) = @_; - # extend our print by creating all necessary layers - for (my $i = $self->layer_count; $i <= $layer_id; $i++) { - push @{ $self->layers }, Slic3r::Layer->new(id => $i, object => $self); + 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; + last; + } } - - return $self->layers->[$layer_id]; + return ($min_layer, $max_layer); } sub slice { @@ -41,7 +56,7 @@ sub slice { my $apply_lines = sub { my $lines = shift; foreach my $layer_id (keys %$lines) { - my $layerm = $self->layer($layer_id)->region($region_id); + my $layerm = $self->layers->[$layer_id]->region($region_id); push @{$layerm->lines}, @{$lines->{$layer_id}}; } }; @@ -441,7 +456,7 @@ sub combine_infill { # 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->layer($i - 1)->regions->[$region_id]; + my $lower_layerm = $self->layers->[$i - 1]->regions->[$region_id]; # 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, diff --git a/lib/Slic3r/TriangleMesh.pm b/lib/Slic3r/TriangleMesh.pm index 599445967..d830ba54c 100644 --- a/lib/Slic3r/TriangleMesh.pm +++ b/lib/Slic3r/TriangleMesh.pm @@ -415,14 +415,12 @@ sub slice_facet { } # calculate the layer extents - my $min_layer = int((unscale($min_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) - 2; - $min_layer = 0 if $min_layer < 0; - my $max_layer = int((unscale($max_z) - ($Slic3r::Config->get_value('first_layer_height') + $Slic3r::Config->layer_height / 2)) / $Slic3r::Config->layer_height) + 2; + my ($min_layer, $max_layer) = $print_object->get_layer_range($min_z, $max_z); Slic3r::debugf "layers: min = %s, max = %s\n", $min_layer, $max_layer; my $lines = {}; # layer_id => [ lines ] for (my $layer_id = $min_layer; $layer_id <= $max_layer; $layer_id++) { - my $layer = $print_object->layer($layer_id); + my $layer = $print_object->layers->[$layer_id]; $lines->{$layer_id} ||= []; push @{ $lines->{$layer_id} }, $self->intersect_facet($facet_id, $layer->slice_z); } From cbd298bc36cd1d40ce5d30b59a649d02b35330ee Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 2 Jan 2013 19:22:51 +0100 Subject: [PATCH 168/172] Alter extrusion width automatic calculation to take "native flow" into account --- lib/Slic3r/Flow.pm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 01992e788..dbce589fb 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -1,6 +1,7 @@ package Slic3r::Flow; use Moo; +use List::Util qw(max); use Slic3r::Geometry qw(PI scale); has 'nozzle_diameter' => (is => 'ro', required => 1); @@ -37,8 +38,11 @@ sub _build_width { $width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1); } - my $min = $self->nozzle_diameter * 1.05; - my $max = $self->nozzle_diameter * 1.4; + my $min = max( + ((($self->nozzle_diameter/2) ** 2) / $self->layer_height * 0.8), + ($self->nozzle_diameter * 1.05), + ); + my $max = $self->nozzle_diameter * 1.6; $width = $max if $width > $max; $width = $min if $width < $min; From 34e047205a43c6ac9e0f47b5416b29cbef8cf9fa Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 2 Jan 2013 19:40:48 +0100 Subject: [PATCH 169/172] Automatic detection of support material threshold angle --- README.markdown | 3 ++- lib/Slic3r/Config.pm | 4 ++-- lib/Slic3r/Print/Object.pm | 8 ++++++-- slic3r.pl | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.markdown b/README.markdown index 43bf64017..f335450a8 100644 --- a/README.markdown +++ b/README.markdown @@ -189,7 +189,8 @@ The author of the Silk icon set is Mark James. Support material options: --support-material Generate support material for overhangs --support-material-threshold - Overhang threshold angle (range: 0-90, default: 45) + Overhang threshold angle (range: 0-90, set 0 for automatic detection, + default: 0) --support-material-pattern Pattern to use for support material (default: rectilinear) --support-material-spacing diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index c9594cac3..90f981364 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -536,11 +536,11 @@ our $Options = { }, 'support_material_threshold' => { label => 'Overhang threshold', - tooltip => 'Support material will not generated for overhangs whose slope angle is above the given threshold.', + tooltip => 'Support material will not generated for overhangs whose slope angle is above the given threshold. Set to zero for automatic detection.', sidetext => '°', cli => 'support-material-threshold=i', type => 'i', - default => 45, + default => 0, }, 'support_material_pattern' => { label => 'Pattern', diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 86cebb675..bf4f77aa0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -2,7 +2,7 @@ package Slic3r::Print::Object; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(Z scale unscale deg2rad scaled_epsilon); +use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex); use Slic3r::Surface ':types'; @@ -531,8 +531,12 @@ sub combine_infill { sub generate_support_material { my $self = shift; + 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 $flow = $self->print->support_material_flow; - my $threshold_rad = deg2rad($Slic3r::Config->support_material_threshold + 1); # +1 makes the threshold inclusive 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) diff --git a/slic3r.pl b/slic3r.pl index 51a58271d..bafd98749 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -237,7 +237,8 @@ $j Support material options: --support-material Generate support material for overhangs --support-material-threshold - Overhang threshold angle (range: 0-90, default: $config->{support_material_threshold}) + Overhang threshold angle (range: 0-90, set 0 for automatic detection, + default: $config->{support_material_threshold}) --support-material-pattern Pattern to use for support material (default: $config->{support_material_pattern}) --support-material-spacing From 32fd58d531de7835883a85e9bb9753a7af76da35 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 10 Jan 2013 15:29:40 +0100 Subject: [PATCH 170/172] 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 f335450a8..bf7ff336b 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 90f981364..a310361d5 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 2bf141556..0b9ff8bc6 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 d4479d6bd..4bb530350 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 fe9f164b7..bf67efadc 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 bafd98749..d109f9ff4 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 171/172] 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 bf67efadc..e3f3c3adf 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 172/172] 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 2a2736719..238a8e98e 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; }