From 33b40eda1848c757b8e659c722683f9c10d35d9f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 18:42:56 +0100 Subject: [PATCH 01/15] Always fix self-intersecting polygons that Douglas-Peucker might return --- lib/Slic3r/ExPolygon.pm | 9 +++++++-- lib/Slic3r/ExtrusionPath.pm | 5 +++++ lib/Slic3r/GCode/MotionPlanner.pm | 16 +++++++--------- lib/Slic3r/GUI/Plater.pm | 3 +-- lib/Slic3r/Geometry.pm | 11 +---------- lib/Slic3r/Geometry/Clipper.pm | 10 ++++++++++ lib/Slic3r/Layer/Region.pm | 16 ++++++++-------- lib/Slic3r/Polygon.pm | 5 +++++ lib/Slic3r/Polyline.pm | 4 ++-- lib/Slic3r/Print.pm | 4 ++-- lib/Slic3r/Print/Object.pm | 18 ++++++++++++------ lib/Slic3r/Surface.pm | 22 ++++++++++++++++------ t/clean_polylines.t | 13 +++++++------ t/combineinfill.t | 4 ++-- 14 files changed, 85 insertions(+), 55 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 202258ac9..558f9fe21 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -172,8 +172,13 @@ sub clip_line { sub simplify { my $self = shift; - $_->simplify(@_) for @$self; - $self; + my ($tolerance) = @_; + + # it would be nice to have a multilinestring_simplify method in B::G::U + my @simplified = Slic3r::Geometry::Clipper::simplify_polygons( + [ map Boost::Geometry::Utils::linestring_simplify($_, $tolerance), @$self ], + ); + return @{ Slic3r::Geometry::Clipper::union_ex([ @simplified ]) }; } sub scale { diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 825d23e6e..3de639649 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -81,6 +81,11 @@ sub clip_with_expolygon { return @paths; } +sub simplify { + my $self = shift; + $self->polyline($self->polyline->simplify(@_)); +} + sub points { my $self = shift; return $self->polyline; diff --git a/lib/Slic3r/GCode/MotionPlanner.pm b/lib/Slic3r/GCode/MotionPlanner.pm index d55faeb2a..3708a65e3 100644 --- a/lib/Slic3r/GCode/MotionPlanner.pm +++ b/lib/Slic3r/GCode/MotionPlanner.pm @@ -56,22 +56,20 @@ sub BUILD { } }; + # simplify islands + @{$self->islands} = map $_->simplify($self->_inner_margin), @{$self->islands}; + # process individual islands - for my $i (0 .. $#{$self->islands}) { - # simplify the island's contours - $self->islands->[$i]->simplify($self->_inner_margin); - + for my $i (0 .. $#{$self->islands}) { # offset the island inwards to make the boundaries for internal movements # so that no motion along external perimeters happens - $self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ] - if !$self->no_internal; + $self->_inner->[$i] = $self->no_internal + ? [] + : [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ]; # offset the island outwards to make the boundaries for external movements $self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ]; - # further simplification (isn't this a duplication of the one above?) - $_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]}; - # if internal motion is enabled, build a set of utility expolygons representing # the outer boundaries (as contours) and the inner boundaries (as holes). whenever # we jump from a hole to a contour or viceversa, we know we're crossing a perimeter diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3b86a597c..0cdd5d04b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1137,9 +1137,8 @@ sub make_thumbnail { for (map @$_, map @$_, @{$thumbnail->expolygons}) { @$_ = map $_ * $self->thumbnail_scaling_factor, @$_; } + @{$thumbnail->expolygons} = map $_->simplify(0.5), grep $_->area >= 1, @{$thumbnail->expolygons}; 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); } diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 6a91a2046..0d2d9df4d 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -12,7 +12,7 @@ our @EXPORT_OK = qw( point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_along_segment polygon_segment_having_point polygon_has_subsegment polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg - rotate_points move_points remove_coinciding_points clip_segment_polygon + rotate_points move_points clip_segment_polygon sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility line_intersection bounding_box bounding_box_intersect same_point same_line longest_segment angle3points three_points_aligned line_direction @@ -375,15 +375,6 @@ sub move_points { return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points; } -# preserves order -sub remove_coinciding_points { - my ($points) = @_; - - my %p = map { sprintf('%f,%f', @$_) => "$_" } @$points; - %p = reverse %p; - @$points = grep $p{"$_"}, @$points; -} - # implementation of Liang-Barsky algorithm # polygon must be convex and ccw sub clip_segment_polygon { diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 6b259e0bc..0cc78b188 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -92,4 +92,14 @@ sub collapse_ex { return union_ex([@result]); } +sub simplify_polygon { + my ($polygon, $pft) = @_; + return @{ Math::Clipper::simplify_polygon($polygon, $pft // PFT_NONZERO) }; +} + +sub simplify_polygons { + my ($polygons, $pft) = @_; + return @{ Math::Clipper::simplify_polygons($polygons, $pft // PFT_NONZERO) }; +} + 1; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index a3b6abe06..99bdbfc15 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -263,14 +263,14 @@ sub make_perimeters { # we offset by half the perimeter spacing (to get to the actual infill boundary) # and then we offset back and forth by the infill spacing to only consider the # non-collapsing regions - my @fill_boundaries = @{union_ex([ - Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))], - +$infill_spacing, - ), - ])}; - $_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries; - push @{ $self->fill_surfaces }, @fill_boundaries; + push @{ $self->fill_surfaces }, + map $_->simplify(&Slic3r::SCALED_RESOLUTION), + @{union_ex([ + Slic3r::Geometry::Clipper::offset( + [Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))], + +$infill_spacing, + ), + ])}; } # fill gaps diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index f5b275510..1d759a06b 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -97,6 +97,11 @@ sub grow { return $self->split_at_first_point->grow(@_); } +sub simplify { + my $self = shift; + return Slic3r::Geometry::Clipper::simplify_polygon( $self->SUPER::simplify(@_) ); +} + # this method subdivides the polygon segments to that no one of them # is longer than the length provided sub subdivide { diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index 631030604..2578e505d 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -68,8 +68,8 @@ sub simplify { my $self = shift; my $tolerance = shift || 10; - @$self = @{ Boost::Geometry::Utils::linestring_simplify($self, $tolerance) }; - bless $_, 'Slic3r::Point' for @$self; + my $simplified = Boost::Geometry::Utils::linestring_simplify($self, $tolerance); + return (ref $self)->new($simplified); } sub reverse { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e69ed7563..075b4edc2 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -335,8 +335,8 @@ 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(&Slic3r::SCALED_RESOLUTION) - for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); + @$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_ + for $layer->slices, (map $_->slices, @{$layer->regions}); } # this will assign a type (top/bottom/internal) to $layerm->slices diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 947daee56..51527d0f6 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -774,8 +774,10 @@ sub generate_support_material { [ map @$_, @{ $upper_layers_overhangs[-1] || [] } ], [ map @$_, @current_layer_offsetted_slices ], ); - $layers_contact_areas{$i} = collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width); - $_->simplify($flow->scaled_spacing) for @{$layers_contact_areas{$i}}; + $layers_contact_areas{$i} = [ + map $_->simplify($flow->scaled_spacing), + collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width), + ]; # to define interface regions of this layer we consider the overhangs of all the upper layers # minus the first one @@ -786,8 +788,10 @@ sub generate_support_material { (map @$_, @{ $layers_contact_areas{$i} }), ], ); - $layers_interfaces{$i} = collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width); - $_->simplify($flow->scaled_spacing) for @{$layers_interfaces{$i}}; + $layers_interfaces{$i} = [ + map $_->simplify($flow->scaled_spacing), + collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width), + ]; # generate support material in current layer (for upper layers) @current_support_regions = @{diff_ex( @@ -806,8 +810,10 @@ sub generate_support_material { (map @$_, @{ $layers_interfaces{$i} }), ], ); - $layers{$i} = collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width); - $_->simplify($flow->scaled_spacing) for @{$layers{$i}}; + $layers{$i} = [ + map $_->simplify($flow->scaled_spacing), + collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width), + ]; # get layer overhangs and put them into queue for adding support inside lower layers; # we need an angle threshold for this diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 2864a8643..2272c6d38 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -81,12 +81,22 @@ sub group { sub offset { my $self = shift; - return map { - (ref $self)->new( - expolygon => $_, - map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle), - ) - } $self->expolygon->offset_ex(@_); + return map $self->_inflate_expolygon($_), $self->expolygon->offset_ex(@_); +} + +sub simplify { + my $self = shift; + return map $self->_inflate_expolygon($_), $self->expolygon->simplify(@_); +} + +sub _inflate_expolygon { + my $self = shift; + my ($expolygon) = @_; + + return (ref $self)->new( + expolygon => $expolygon, + map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle), + ); } sub clipper_polygon { diff --git a/t/clean_polylines.t b/t/clean_polylines.t index 064b7bd3a..9f7b3ce27 100644 --- a/t/clean_polylines.t +++ b/t/clean_polylines.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 6; +plan tests => 7; BEGIN { use FindBin; @@ -24,7 +24,7 @@ use Slic3r; my $polyline = Slic3r::Polyline->new([ [0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0], ]); - $polyline->simplify(1); + $polyline = $polyline->simplify(1); is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker'; } @@ -33,7 +33,7 @@ use Slic3r; [0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5], ]); $polyline->scale(100); - $polyline->simplify(25); + $polyline = $polyline->simplify(25); is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker'; } @@ -75,9 +75,10 @@ use Slic3r; ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines'; my $num_points = scalar @$polygon; - $polygon->simplify; - note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@$polygon); - ok @$polygon < $num_points, 'gear was further simplified using Douglas-Peucker'; + my @simplified = $polygon->simplify; + ok @simplified == 1, 'gear simplified to a single polygon'; + note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@{$simplified[0]}); + ok @{$simplified[0]} < $num_points, 'gear was further simplified using Douglas-Peucker'; } { diff --git a/t/combineinfill.t b/t/combineinfill.t index e2e63f4a0..f5f54fa85 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -43,8 +43,8 @@ use Slic3r::Test; $_->slice(keep_meshes => 1) for @{$self->objects}; $_->make_perimeters for @{$self->objects}; foreach my $layer (map @{$_->layers}, @{$self->objects}) { - $_->simplify(&Slic3r::SCALED_RESOLUTION) - for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions}); + @$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_ + for $layer->slices, (map $_->slices, @{$layer->regions}); } $_->detect_surfaces_type for @{$self->objects}; $_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects}; From db2e76a0df1d030b48fa66d44f384dffd2bd4802 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 18:56:49 +0100 Subject: [PATCH 02/15] New option to change perimeter order. #534 --- README.markdown | 1 + lib/Slic3r/Config.pm | 7 +++++++ lib/Slic3r/GUI/Tab.pm | 2 +- lib/Slic3r/Layer/Region.pm | 8 ++++++-- slic3r.pl | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 5015e2314..df651b6a0 100644 --- a/README.markdown +++ b/README.markdown @@ -197,6 +197,7 @@ The author of the Silk icon set is Mark James. --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) + --external-perimeters-first Reverse perimeter order. (default: no) --only-retract-when-crossing-perimeters Disable retraction when travelling between infill paths inside the same island. (default: yes) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0017d9257..454afd40c 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -562,6 +562,13 @@ our $Options = { type => 'bool', default => 0, }, + 'external_perimeters_first' => { + label => 'External perimeters first', + tooltip => 'Print contour perimeters from the outermost one to the innermost one instead of the default inverse order.', + cli => 'external-perimeters-first!', + type => 'bool', + default => 0, + }, 'only_retract_when_crossing_perimeters' => { label => 'Only retract when crossing perimeters', tooltip => 'Disables retraction when travelling between infill paths inside the same island.', diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 35637c886..79f9af96e 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -407,7 +407,7 @@ sub build { }, { title => 'Advanced', - options => [qw(avoid_crossing_perimeters)], + options => [qw(avoid_crossing_perimeters external_perimeters_first)], }, ]); diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 99bdbfc15..1c365dd42 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -404,10 +404,14 @@ sub make_perimeters { } } - # do holes, then contours starting from innermost one + # first do holes $self->_add_perimeter($holes[$_], $is_external{$_} ? EXTR_ROLE_EXTERNAL_PERIMETER : undef) for reverse 0 .. $#holes; - for my $depth (reverse 0 .. $#$island) { + + # then do contours according to the user settings + my @contour_order = 0 .. $#$island; + @contour_order = reverse @contour_order if !$Slic3r::Config->external_perimeters_first; + for my $depth (@contour_order) { my $role = $depth == $#$island ? EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER : $depth == 0 ? EXTR_ROLE_EXTERNAL_PERIMETER : EXTR_ROLE_PERIMETER; diff --git a/slic3r.pl b/slic3r.pl index 301c6b85d..4290ceb8e 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -250,6 +250,7 @@ $j --extra-perimeters Add more perimeters when needed (default: yes) --randomize-start Randomize starting point across layers (default: yes) --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no) + --external-perimeters-first Reverse perimeter order. (default: no) --only-retract-when-crossing-perimeters Disable retraction when travelling between infill paths inside the same island. (default: no) From f4b81856988456984f71457367696557e2a8c6f3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 19:11:49 +0100 Subject: [PATCH 03/15] Fixed typo preventing support material to work with new simplify() syntax --- lib/Slic3r/Print/Object.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 51527d0f6..6ab07bd6b 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -776,7 +776,7 @@ sub generate_support_material { ); $layers_contact_areas{$i} = [ map $_->simplify($flow->scaled_spacing), - collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width), + @{collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width)}, ]; # to define interface regions of this layer we consider the overhangs of all the upper layers @@ -790,7 +790,7 @@ sub generate_support_material { ); $layers_interfaces{$i} = [ map $_->simplify($flow->scaled_spacing), - collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width), + @{collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width)}, ]; # generate support material in current layer (for upper layers) @@ -812,7 +812,7 @@ sub generate_support_material { ); $layers{$i} = [ map $_->simplify($flow->scaled_spacing), - collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width), + @{collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width)}, ]; # get layer overhangs and put them into queue for adding support inside lower layers; From 10b6bc9d15e8a284cb8c98abb6aadb8d5724e9fb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 19:39:00 +0100 Subject: [PATCH 04/15] Removed keep_meshes --- lib/Slic3r/Print.pm | 4 ++-- lib/Slic3r/Print/Object.pm | 2 +- t/combineinfill.t | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 075b4edc2..e5025cc09 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -324,7 +324,7 @@ sub export_gcode { # skein the STL into layers # each layer has surfaces with holes $status_cb->(10, "Processing triangulated mesh"); - $_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects}; + $_->slice for @{$self->objects}; # make perimeters # this will add a set of extrusion loops to each layer @@ -477,7 +477,7 @@ sub export_svg { # calls ->perimeter_flow $self->init_extruders; - $_->slice(keep_meshes => $params{keep_meshes}) for @{$self->objects}; + $_->slice for @{$self->objects}; $self->arrange_objects; my $output_file = $self->expanded_output_filepath($params{output_file}); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 6ab07bd6b..3abc5c2d7 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -121,7 +121,7 @@ sub slice { die "Invalid input file\n" if !@{$self->layers}; # free memory - $self->meshes(undef) unless $params{keep_meshes}; + $self->meshes(undef); # remove last layer if empty # (we might have created it because of the $max_layer = ... + 1 code in TriangleMesh) diff --git a/t/combineinfill.t b/t/combineinfill.t index f5f54fa85..77eabcc02 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -40,7 +40,7 @@ use Slic3r::Test; # copy of Print::export_gcode() up to the point # after fill surfaces are combined $self->init_extruders; - $_->slice(keep_meshes => 1) for @{$self->objects}; + $_->slice for @{$self->objects}; $_->make_perimeters for @{$self->objects}; foreach my $layer (map @{$_->layers}, @{$self->objects}) { @$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_ From cf5adca928d246802bc47bf3d4df648538ca80c3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 19:58:34 +0100 Subject: [PATCH 05/15] New --resolution option to simplify input models --- README.markdown | 1 + lib/Slic3r/Config.pm | 9 +++++++++ lib/Slic3r/GUI/Tab.pm | 6 +++--- lib/Slic3r/Print.pm | 20 ++++++++++++++++---- slic3r.pl | 1 + 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index df651b6a0..ffebd1481 100644 --- a/README.markdown +++ b/README.markdown @@ -289,6 +289,7 @@ The author of the Silk icon set is Mark James. Miscellaneous options: --notes Notes to be added as comments to the output file + --resolution Minimum detail resolution (mm, set zero for full resolution, default: 0) Flow options (advanced): --extrusion-width Set extrusion width manually; it accepts either an absolute value in mm diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 454afd40c..1bd9657a0 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -37,6 +37,15 @@ our $Options = { default => $Slic3r::have_threads ? 2 : 1, readonly => !$Slic3r::have_threads, }, + 'resolution' => { + label => 'Resolution', + tooltip => 'Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input.', + sidetext => 'mm', + cli => 'resolution=f', + type => 'f', + min => 0, + default => 0, + }, # output options 'output_filename_format' => { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 79f9af96e..fa10383e5 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -517,10 +517,10 @@ sub build { title => 'Flow', options => [qw(bridge_flow_ratio)], }, - $Slic3r::have_threads ? { + { title => 'Other', - options => [qw(threads)], - } : (), + options => [($Slic3r::have_threads ? qw(threads) : ()), qw(resolution)], + }, ]); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index e5025cc09..3be7fe87b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -313,6 +313,16 @@ sub size { return [ $bb[X2] - $bb[X1], $bb[Y2] - $bb[Y1] ]; } +sub _simplify_slices { + my $self = shift; + my ($distance) = @_; + + foreach my $layer (map @{$_->layers}, @{$self->objects}) { + @$_ = map $_->simplify($distance), @$_ + for $layer->slices, (map $_->slices, @{$layer->regions}); + } +} + sub export_gcode { my $self = shift; my %params = @_; @@ -326,6 +336,11 @@ sub export_gcode { $status_cb->(10, "Processing triangulated mesh"); $_->slice for @{$self->objects}; + if ($Slic3r::Config->resolution) { + $status_cb->(15, "Simplifying input"); + $self->_simplify_slices(scale $Slic3r::Config->resolution); + } + # make perimeters # this will add a set of extrusion loops to each layer # as well as generate infill boundaries @@ -334,10 +349,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}) { - @$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_ - for $layer->slices, (map $_->slices, @{$layer->regions}); - } + $self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); # this will assign a type (top/bottom/internal) to $layerm->slices # and transform $layerm->fill_surfaces from expolygon diff --git a/slic3r.pl b/slic3r.pl index 4290ceb8e..c4cfd8487 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -342,6 +342,7 @@ $j Miscellaneous options: --notes Notes to be added as comments to the output file + --resolution Minimum detail resolution (mm, set zero for full resolution, default: $config->{resolution}) Flow options (advanced): --extrusion-width Set extrusion width manually; it accepts either an absolute value in mm From 25554a345f8a0be9aa7da27543bbfc2f13c35554 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 20:56:14 +0100 Subject: [PATCH 06/15] Reorder object copies using a nearest neighbor search --- lib/Slic3r/Print.pm | 2 +- lib/Slic3r/Print/Object.pm | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 3be7fe87b..0753a2768 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -134,7 +134,7 @@ sub add_model { if ($object->instances) { # replace the default [0,0] instance with the custom ones - @{$print_object->copies} = map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances}; + $print_object->copies([ map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances} ]); } } } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 3abc5c2d7..618cf6950 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -3,7 +3,7 @@ use Moo; use List::Util qw(min sum first); use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon); +use Slic3r::Geometry qw(Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path_points); use Slic3r::Geometry::Clipper qw(diff_ex intersection_ex union_ex offset collapse_ex); use Slic3r::Surface ':types'; @@ -11,7 +11,7 @@ has 'print' => (is => 'ro', weak_ref => 1, required => 1); has 'input_file' => (is => 'rw', required => 0); has 'meshes' => (is => 'rw', default => sub { [] }); # by region_id has 'size' => (is => 'rw', required => 1); -has 'copies' => (is => 'rw', default => sub {[ [0,0] ]}); +has 'copies' => (is => 'rw', default => sub {[ [0,0] ]}, trigger => 1); has 'layers' => (is => 'rw', default => sub { [] }); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] @@ -50,6 +50,14 @@ sub BUILD { } } +sub _trigger_copies { + my $self = shift; + return unless @{$self->copies} > 1; + + # order copies with a nearest neighbor search + @{$self->copies} = @{chained_path_points($self->copies)} +} + sub layer_count { my $self = shift; return scalar @{ $self->layers }; From 0f5064dd32c9ef08f1e23854846d6a30f7a4dbd6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 21:10:12 +0100 Subject: [PATCH 07/15] Untested fix for incomplete honeycomb support material. #1032 --- lib/Slic3r/Fill/Honeycomb.pm | 13 ++++--------- lib/Slic3r/Polygon.pm | 15 ++++++++++++++- lib/Slic3r/Print/Object.pm | 6 +++++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 4d450370e..95754471e 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -26,7 +26,7 @@ sub fill_surface { my $cache_id = sprintf "d%s_s%s_a%s", $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; - if (!$self->cache->{$cache_id}) { + if (!$self->cache->{$cache_id} || !defined $self->bounding_box) { # hexagons math my $hex_side = $distance / (sqrt(3)/2); @@ -40,15 +40,10 @@ sub fill_surface { # adjust actual bounding box to the nearest multiple of our hex pattern # and align it so that it matches across layers - $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 $bounding_box = [ $self->bounding_box ? @{$self->bounding_box} : $expolygon->bounding_box ]; + $bounding_box->[$_] = 0 for X1, Y1; { - my $bb_polygon = Slic3r::Polygon->new([ - [ $bounding_box->[X1], $bounding_box->[Y1] ], - [ $bounding_box->[X2], $bounding_box->[Y1] ], - [ $bounding_box->[X2], $bounding_box->[Y2] ], - [ $bounding_box->[X1], $bounding_box->[Y2] ], - ]); + my $bb_polygon = Slic3r::Polygon->new_from_bounding_box($bounding_box); $bb_polygon->rotate($rotate_vector->[0][0], $hex_center); $bounding_box = [ Slic3r::Geometry::bounding_box($bb_polygon) ]; # $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 1d759a06b..e9bfcf81a 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -6,9 +6,22 @@ use warnings; use parent 'Slic3r::Polyline'; use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges - polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon); + polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon + X1 X2 Y1 Y2); use Slic3r::Geometry::Clipper qw(JT_MITER); +sub new_from_bounding_box { + my $class = shift; + my ($bounding_box) = @_; + + return $class->new([ + [ $bounding_box->[X1], $bounding_box->[Y1] ], + [ $bounding_box->[X2], $bounding_box->[Y1] ], + [ $bounding_box->[X2], $bounding_box->[Y2] ], + [ $bounding_box->[X1], $bounding_box->[Y2] ], + ]); +} + sub lines { my $self = shift; return polygon_lines($self); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 618cf6950..4d1161882 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -857,7 +857,7 @@ sub generate_support_material { { # 0.5 ensures the paths don't get clipped externally when applying them to layers my @areas = map $_->offset_ex(- 0.5 * $flow->scaled_width), - @{union_ex([ map $_->contour, map @$_, values %layers ])}; + @{union_ex([ map $_->contour, map @$_, values %layers, values %layers_interfaces, values %layers_contact_areas ])}; my $pattern = $Slic3r::Config->support_material_pattern; my @angles = ($Slic3r::Config->support_material_angle); @@ -865,7 +865,11 @@ sub generate_support_material { $pattern = 'rectilinear'; push @angles, $angles[0] + 90; } + my $filler = Slic3r::Fill->filler($pattern); + $filler->bounding_box([ Slic3r::Geometry::bounding_box([ map @$_, map @$_, @areas ]) ]) + if $filler->can('bounding_box'); + my $make_pattern = sub { my ($expolygon, $density) = @_; From c13d9e9fb3dc47b3892f926e1001d3363fc551be Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 16 Mar 2013 21:51:38 +0100 Subject: [PATCH 08/15] Don't collapse overhangs too early --- lib/Slic3r/Print/Object.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 4d1161882..6f47a7808 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -834,8 +834,8 @@ sub generate_support_material { ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) : $self->layers->[1]->regions->[0]->overhang_width; - @overhangs = map $_->offset_ex(2 * $distance), @{diff_ex( - [ map @$_, map $_->offset_ex(-$distance), @{$layer->slices} ], + @overhangs = map $_->offset_ex(+$distance), @{diff_ex( + [ map @$_, @{$layer->slices} ], [ map @$_, @{$lower_layer->slices} ], 1, )}; From 35bd5a3423d689d22ff4e3e9ae59c15854d70a6b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:02:31 +0100 Subject: [PATCH 09/15] New --solid-infill-extrusion-width option --- README.markdown | 2 ++ lib/Slic3r/Config.pm | 10 +++++++++- lib/Slic3r/Fill.pm | 11 +++++++---- lib/Slic3r/GUI/Tab.pm | 2 +- lib/Slic3r/Layer/Region.pm | 17 +++++++++-------- lib/Slic3r/Print.pm | 5 +++-- lib/Slic3r/Print/Object.pm | 6 +++--- slic3r.pl | 2 ++ 8 files changed, 36 insertions(+), 19 deletions(-) diff --git a/README.markdown b/README.markdown index ffebd1481..95c565c98 100644 --- a/README.markdown +++ b/README.markdown @@ -300,6 +300,8 @@ The author of the Silk icon set is Mark James. Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill + --solid-infill-extrusion-width + Set a different extrusion width for solid infill --top-infill-extrusion-width Set a different extrusion width for top infill --support-material-extrusion-width diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 1bd9657a0..b7833462f 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -446,8 +446,16 @@ our $Options = { type => 'f', default => 0, }, + 'solid_infill_extrusion_width' => { + label => 'Solid infill', + tooltip => 'Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) if will be computed over layer height.', + sidetext => 'mm or % (leave 0 for default)', + cli => 'solid-infill-extrusion-width=s', + type => 'f', + default => 0, + }, 'top_infill_extrusion_width' => { - label => 'Top infill', + label => 'Top solid infill', tooltip => 'Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) if will be computed over layer height.', sidetext => 'mm or % (leave 0 for default)', cli => 'top-infill-extrusion-width=s', diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 85c189cfe..9040c0233 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -100,7 +100,7 @@ sub make_fill { # add spacing between surfaces { - my $distance = $layerm->infill_flow->scaled_spacing / 2; + my $distance = $layerm->solid_infill_flow->scaled_spacing / 2; @surfaces = map $_->offset(-$distance), @surfaces; } @@ -109,9 +109,12 @@ sub make_fill { SURFACE: foreach my $surface (@surfaces) { my $filler = $Slic3r::Config->fill_pattern; my $density = $Slic3r::Config->fill_density; - my $flow_spacing = ($surface->surface_type == S_TYPE_TOP) - ? $layerm->top_infill_flow->spacing - : $layerm->infill_flow->spacing; + my $flow = ($surface->surface_type == S_TYPE_TOP) + ? $layerm->top_infill_flow + : $surface->is_solid + ? $layerm->solid_infill_flow + : $layerm->infill_flow; + my $flow_spacing = $flow->spacing; my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fa10383e5..8fdacf6f6 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -511,7 +511,7 @@ sub build { { title => 'Extrusion width', label_width => 180, - options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width)], + options => [qw(extrusion_width first_layer_extrusion_width perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width top_infill_extrusion_width support_material_extrusion_width)], }, { title => 'Flow', diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 1c365dd42..fd9b187eb 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -16,6 +16,7 @@ has 'layer' => ( has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'perimeter_flow' => (is => 'rw'); has 'infill_flow' => (is => 'rw'); +has 'solid_infill_flow' => (is => 'rw'); has 'top_infill_flow' => (is => 'rw'); has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); @@ -59,15 +60,15 @@ sub _update_flows { return if !$self->region; if ($self->id == 0) { - $self->perimeter_flow - ($self->region->first_layer_flows->{perimeter} || $self->region->flows->{perimeter}); - $self->infill_flow - ($self->region->first_layer_flows->{infill} || $self->region->flows->{infill}); - $self->top_infill_flow - ($self->region->first_layer_flows->{top_infill} || $self->region->flows->{top_infill}); + for (qw(perimeter infill solid_infill top_infill)) { + my $method = "${_}_flow"; + $self->$method + ($self->region->first_layer_flows->{$_} || $self->region->flows->{$_}); + } } else { $self->perimeter_flow($self->region->flows->{perimeter}); $self->infill_flow($self->region->flows->{infill}); + $self->solid_infill_flow($self->region->flows->{solid_infill}); $self->top_infill_flow($self->region->flows->{top_infill}); } } @@ -80,7 +81,7 @@ sub _build_overhang_width { sub _build_infill_area_threshold { my $self = shift; - return $self->infill_flow->scaled_spacing ** 2; + return $self->solid_infill_flow->scaled_spacing ** 2; } # build polylines from lines @@ -160,7 +161,7 @@ sub make_perimeters { my $self = shift; my $perimeter_spacing = $self->perimeter_flow->scaled_spacing; - my $infill_spacing = $self->infill_flow->scaled_spacing; + my $infill_spacing = $self->solid_infill_flow->scaled_spacing; my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2; # this array will hold one arrayref per original surface (island); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0753a2768..0ee365074 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -201,8 +201,9 @@ sub init_extruders { my $region = $self->regions->[$region_id]; # per-role extruders and flows - for (qw(perimeter infill top_infill)) { - my $extruder_name = $_ eq 'top_infill' ? 'infill' : $_; + for (qw(perimeter infill solid_infill top_infill)) { + my $extruder_name = $_; + $extruder_name =~ s/^(?:solid|top)_//; $region->extruders->{$_} = ($self->regions_count > 1) ? $self->extruders->[$extruder_mapping{$region_id}] : $self->extruders->[$self->config->get("${extruder_name}_extruder")-1]; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 6f47a7808..0431de756 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -581,7 +581,7 @@ sub discover_horizontal_shells { # make sure the new internal solid is wide enough, as it might get collapsed when # spacing is added in Fill.pm { - my $margin = 3 * $layerm->infill_flow->scaled_width; # require at least this size + my $margin = 3 * $layerm->solid_infill_flow->scaled_width; # require at least this size my $too_narrow = diff_ex( [ map @$_, @$new_internal_solid ], [ offset([ offset([ map @$_, @$new_internal_solid ], -$margin) ], +$margin) ], @@ -708,12 +708,12 @@ sub combine_infill { # so let's remove those areas from all layers my @intersection_with_clearance = map $_->offset( - $layerms[-1]->infill_flow->scaled_width / 2 + $layerms[-1]->solid_infill_flow->scaled_width / 2 + $layerms[-1]->perimeter_flow->scaled_width / 2 # Because fill areas for rectilinear and honeycomb are grown # later to overlap perimeters, we need to counteract that too. + (($type == S_TYPE_INTERNALSOLID || $Slic3r::Config->fill_pattern =~ /(rectilinear|honeycomb)/) - ? $layerms[-1]->infill_flow->scaled_width * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING + ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING : 0) ), @$intersection; diff --git a/slic3r.pl b/slic3r.pl index c4cfd8487..5c5c80b16 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -353,6 +353,8 @@ $j Set a different extrusion width for perimeters --infill-extrusion-width Set a different extrusion width for infill + --solid-infill-extrusion-width + Set a different extrusion width for solid infill --top-infill-extrusion-width Set a different extrusion width for top infill --support-material-extrusion-width From 78a4ae483582a591117a4df9cb3c73246c63264b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:21:17 +0100 Subject: [PATCH 10/15] Use full flow for sparse infill by default --- lib/Slic3r/Flow.pm | 15 ++++++++------- lib/Slic3r/Print.pm | 6 ++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index e2e780921..b17b1991a 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -1,11 +1,11 @@ package Slic3r::Flow; use Moo; -use List::Util qw(max); use Slic3r::Geometry qw(PI scale); has 'nozzle_diameter' => (is => 'ro', required => 1); has 'layer_height' => (is => 'ro', default => sub { $Slic3r::Config->layer_height }); +has 'role' => (is => 'ro', default => sub { '' }); has 'width' => (is => 'rwp', builder => 1); has 'spacing' => (is => 'lazy'); @@ -36,12 +36,13 @@ sub _build_width { $width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1); } - my $min = max( - ($volume / $self->layer_height), - ($self->nozzle_diameter * 1.05), - ); - my $max = $self->nozzle_diameter * 2; - $width = $max if $width > $max; + my $min = $self->nozzle_diameter * 1.05; + my $max; + if ($self->role ne 'infill') { + # do not limit width for sparse infill so that we use full native flow for it + $max = $self->nozzle_diameter * 1.7; + } + $width = $max if defined($max) && $width > $max; $width = $min if $width < $min; return $width; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0ee365074..e374a6cfa 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -209,10 +209,12 @@ sub init_extruders { : $self->extruders->[$self->config->get("${extruder_name}_extruder")-1]; $region->flows->{$_} = $region->extruders->{$_}->make_flow( width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width, + role => $_, ); $region->first_layer_flows->{$_} = $region->extruders->{$_}->make_flow( layer_height => $self->config->get_value('first_layer_height'), width => $self->config->first_layer_extrusion_width, + role => $_, ) if $self->config->first_layer_extrusion_width; } } @@ -222,10 +224,12 @@ sub init_extruders { my $extruder = $self->extruders->[$self->config->support_material_extruder-1]; $self->support_material_flow($extruder->make_flow( width => $self->config->support_material_extrusion_width || $self->config->extrusion_width, + role => 'support_material', )); $self->first_layer_support_material_flow($extruder->make_flow( layer_height => $self->config->get_value('first_layer_height'), width => $self->config->first_layer_extrusion_width, + role => 'support_material', )); } } @@ -697,6 +701,8 @@ sub write_gcode { } 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 "; solid infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{solid_infill}->width; + printf $fh "; top infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{top_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", $self->regions->[0]->first_layer_flows->{perimeter}->width From c999d0c44fb3b020cbfe08eb37e86347ddde6522 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:22:53 +0100 Subject: [PATCH 11/15] Set default value for first_layer_height to 0.35mm so that first_layer_extrusion_width = 200% works fine even when user sets layer_height to 0.1. A first layer height of 0.35mm works fine for both 0.35 and 0.5 nozzles so it should be fine for most users --- 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 b7833462f..e3e9d6392 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -377,7 +377,7 @@ our $Options = { cli => 'first-layer-height=s', type => 'f', ratio_over => 'layer_height', - default => '100%', + default => 0.35, }, 'infill_every_layers' => { label => 'Infill every', From 700fee14aa7a1ddb488c04b5e1376c05212829a1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:24:53 +0100 Subject: [PATCH 12/15] Set honeycomb as default fill pattern, as it works better at low layer heights and makes stronger parts --- 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 e3e9d6392..3e147a440 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -523,7 +523,7 @@ our $Options = { type => 'select', values => [qw(rectilinear line concentric honeycomb hilbertcurve archimedeanchords octagramspiral)], labels => [qw(rectilinear line concentric honeycomb), 'hilbertcurve (slow)', 'archimedeanchords (slow)', 'octagramspiral (slow)'], - default => 'rectilinear', + default => 'honeycomb', }, 'solid_fill_pattern' => { label => 'Top/bottom fill pattern', From 537fca47c56e134c261f113e9160858434f6ceb6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:42:53 +0100 Subject: [PATCH 13/15] Use solid infill speed for internal bridges; apply slowdown if any; don't use bridge fan settings. #240 --- lib/Slic3r/ExtrusionPath.pm | 11 ++++++----- lib/Slic3r/Fill.pm | 12 +++++++----- lib/Slic3r/GCode.pm | 5 +++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index 3de639649..95a65c709 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -5,8 +5,8 @@ require Exporter; our @ISA = qw(Exporter); 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); + EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_BRIDGE + EXTR_ROLE_INTERNALBRIDGE EXTR_ROLE_SKIRT EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_GAPFILL); our %EXPORT_TAGS = (roles => \@EXPORT_OK); use Slic3r::Geometry qw(PI X Y epsilon deg2rad rotate_points); @@ -30,9 +30,10 @@ use constant EXTR_ROLE_FILL => 4; use constant EXTR_ROLE_SOLIDFILL => 5; 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 EXTR_ROLE_INTERNALBRIDGE => 8; +use constant EXTR_ROLE_SKIRT => 9; +use constant EXTR_ROLE_SUPPORTMATERIAL => 10; +use constant EXTR_ROLE_GAPFILL => 11; use constant PACK_FMT => 'ffca*'; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 9040c0233..86fe5de50 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -156,11 +156,13 @@ sub make_fill { paths => [ map Slic3r::ExtrusionPath->pack( polyline => Slic3r::Polyline->new(@$_), - role => ($is_bridge - ? EXTR_ROLE_BRIDGE - : $is_solid - ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) - : EXTR_ROLE_FILL), + role => ($surface->surface_type == S_TYPE_INTERNALBRIDGE + ? EXTR_ROLE_INTERNALBRIDGE + : $is_bridge + ? EXTR_ROLE_BRIDGE + : $is_solid + ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) + : EXTR_ROLE_FILL), height => $surface->depth_layers * $layerm->height, flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"), ), @paths, diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index c3501d437..52efe6b39 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -53,6 +53,7 @@ my %role_speeds = ( &EXTR_ROLE_SOLIDFILL => 'solid_infill', &EXTR_ROLE_TOPSOLIDFILL => 'top_solid_infill', &EXTR_ROLE_BRIDGE => 'bridge', + &EXTR_ROLE_INTERNALBRIDGE => 'solid_infill', &EXTR_ROLE_SKIRT => 'perimeter', &EXTR_ROLE_SUPPORTMATERIAL => 'support_material', &EXTR_ROLE_GAPFILL => 'gap_fill', @@ -203,13 +204,13 @@ sub extrude_path { $acceleration = $Slic3r::Config->perimeter_acceleration; } elsif ($Slic3r::Config->infill_acceleration && $path->is_fill) { $acceleration = $Slic3r::Config->infill_acceleration; - } elsif ($Slic3r::Config->infill_acceleration && $path->role == EXTR_ROLE_BRIDGE) { + } elsif ($Slic3r::Config->infill_acceleration && ($path->role == EXTR_ROLE_BRIDGE || $path->role == EXTR_ROLE_INTERNALBRIDGE)) { $acceleration = $Slic3r::Config->bridge_acceleration; } $gcode .= $self->set_acceleration($acceleration) if $acceleration; my $area; # mm^3 of extrudate per mm of tool movement - if ($path->role == EXTR_ROLE_BRIDGE) { + if ($path->role == EXTR_ROLE_BRIDGE || $path->role == EXTR_ROLE_INTERNALBRIDGE) { my $s = $path->flow_spacing; $area = ($s**2) * PI/4; } else { From 04c0caad0b42c2cfc23df8aac5c703b287263ed0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 00:57:58 +0100 Subject: [PATCH 14/15] Fix honeycomb infill overlap with perimeters --- lib/Slic3r.pm | 2 +- lib/Slic3r/Fill.pm | 2 +- lib/Slic3r/Fill/Honeycomb.pm | 3 +-- lib/Slic3r/Fill/Rectilinear.pm | 6 ------ lib/Slic3r/Print/Object.pm | 19 ++++++++++--------- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 7d1c4dd67..c5bb81bfc 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -67,7 +67,7 @@ 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; -use constant PERIMETER_INFILL_OVERLAP_OVER_SPACING => 0.45; +use constant INFILL_OVERLAP_OVER_SPACING => 0.45; our $Config; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 86fe5de50..105da93b3 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -101,7 +101,7 @@ sub make_fill { # add spacing between surfaces { my $distance = $layerm->solid_infill_flow->scaled_spacing / 2; - @surfaces = map $_->offset(-$distance), @surfaces; + @surfaces = map $_->offset(-$distance * &Slic3r::INFILL_OVERLAP_OVER_SPACING), @surfaces; } my @fills = (); diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 95754471e..9e4ff8a27 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -22,7 +22,6 @@ sub fill_surface { # infill math my $min_spacing = scale $params{flow_spacing}; my $distance = $min_spacing / $params{density}; - 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]; @@ -84,7 +83,7 @@ sub fill_surface { # path is more straight my @paths = map Slic3r::Polyline->new(@$_), map @$_, @{intersection_ex( $self->cache->{$cache_id}, - [ map @$_, $expolygon->offset_ex($overlap_distance) ], + $expolygon, )}; return { flow_spacing => $params{flow_spacing} }, diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index c074d3b1c..e1dcab0e6 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -31,8 +31,6 @@ sub fill_surface { $flow_spacing = unscale $distance_between_lines; } - 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'); my @vertical_lines = (); @@ -52,10 +50,6 @@ sub fill_surface { +($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object [ @vertical_lines ], ) }; - for (@paths) { - $_->[0][Y] += $overlap_distance; - $_->[-1][Y] -= $overlap_distance; - } # connect lines unless ($params{dont_connect}) { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 0431de756..622117123 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -707,15 +707,16 @@ sub combine_infill { # $intersection now contains the regions that can be combined across the full amount of layers # so let's remove those areas from all layers - my @intersection_with_clearance = map $_->offset( - $layerms[-1]->solid_infill_flow->scaled_width / 2 - + $layerms[-1]->perimeter_flow->scaled_width / 2 - # Because fill areas for rectilinear and honeycomb are grown - # later to overlap perimeters, we need to counteract that too. - + (($type == S_TYPE_INTERNALSOLID || $Slic3r::Config->fill_pattern =~ /(rectilinear|honeycomb)/) - ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::PERIMETER_INFILL_OVERLAP_OVER_SPACING - : 0) - ), @$intersection; + my @intersection_with_clearance = map $_->offset( + $layerms[-1]->solid_infill_flow->scaled_width / 2 + + $layerms[-1]->perimeter_flow->scaled_width / 2 + # Because fill areas for rectilinear and honeycomb are grown + # later to overlap perimeters, we need to counteract that too. + + (($type == S_TYPE_INTERNALSOLID || $Slic3r::Config->fill_pattern =~ /(rectilinear|honeycomb)/) + ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING + : 0) + ), @$intersection; + foreach my $layerm (@layerms) { my @this_type = grep $_->surface_type == $type, @{$layerm->fill_surfaces}; From 83065b0789e3f9b56f89ae528b3d7935ea792540 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 17 Mar 2013 01:10:40 +0100 Subject: [PATCH 15/15] Bugfix: wrong flow and bad pattern rotation when infill-every-layers was set to an arbitrary high value --- lib/Slic3r/Fill.pm | 11 ++++++----- lib/Slic3r/Fill/Base.pm | 2 +- lib/Slic3r/Print/Object.pm | 9 +++++++-- lib/Slic3r/Surface.pm | 35 +++++++++++++++++++---------------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 105da93b3..76c264c65 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -90,10 +90,11 @@ sub make_fill { ); push @surfaces, map Slic3r::Surface->new( - expolygon => $_, - surface_type => $group->[0]->surface_type, - bridge_angle => $group->[0]->bridge_angle, - depth_layers => $group->[0]->depth_layers, + expolygon => $_, + surface_type => $group->[0]->surface_type, + bridge_angle => $group->[0]->bridge_angle, + thickness => $group->[0]->thickness, + thickness_layers => $group->[0]->thickness_layers, ), @$union; } } @@ -163,7 +164,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 * $layerm->height, + height => $surface->thickness, 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/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 93af733d4..9c6996be3 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -20,7 +20,7 @@ sub infill_direction { if (defined $self->layer_id) { # alternate fill direction - my $layer_num = $self->layer_id / $surface->depth_layers; + my $layer_num = $self->layer_id / $surface->thickness_layers; my $angle = $self->angles->[$layer_num % @{$self->angles}]; $rotate[0] = Slic3r::Geometry::deg2rad($self->angle) + $angle if $angle; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 622117123..782c997cd 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -659,7 +659,7 @@ sub combine_infill { my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter; # define the combinations - my @combine = (); # layer_id => depth + my @combine = (); # layer_id => thickness in layers { my $current_height = my $layers = 0; for my $layer_id (1 .. $#layer_heights) { @@ -731,7 +731,12 @@ sub combine_infill { # apply surfaces back with adjusted depth to the uppermost layer if ($layerm->id == $layer_id) { push @this_type, - map Slic3r::Surface->new(expolygon => $_, surface_type => $type, depth_layers => $every), + map Slic3r::Surface->new( + expolygon => $_, + surface_type => $type, + thickness => sum(map $_->height, @layerms), + thickness_layers => scalar(@layerms), + ), @$intersection; } diff --git a/lib/Slic3r/Surface.pm b/lib/Slic3r/Surface.pm index 2272c6d38..139daf5f5 100644 --- a/lib/Slic3r/Surface.pm +++ b/lib/Slic3r/Surface.pm @@ -7,11 +7,12 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw(S_TYPE_TOP S_TYPE_BOTTOM S_TYPE_INTERNAL S_TYPE_INTERNALSOLID S_TYPE_INTERNALBRIDGE); our %EXPORT_TAGS = (types => \@EXPORT_OK); -use constant S_EXPOLYGON => 0; -use constant S_SURFACE_TYPE => 1; -use constant S_DEPTH_LAYERS => 2; -use constant S_BRIDGE_ANGLE => 3; -use constant S_EXTRA_PERIMETERS => 4; +use constant S_EXPOLYGON => 0; +use constant S_SURFACE_TYPE => 1; +use constant S_THICKNESS => 2; # in mm +use constant S_THICKNESS_LAYERS => 3; # in layers +use constant S_BRIDGE_ANGLE => 4; +use constant S_EXTRA_PERIMETERS => 5; use constant S_TYPE_TOP => 0; use constant S_TYPE_BOTTOM => 1; @@ -24,9 +25,9 @@ sub new { my %args = @_; my $self = [ - map delete $args{$_}, qw(expolygon surface_type depth_layers bridge_angle extra_perimeters), + map delete $args{$_}, qw(expolygon surface_type thickness thickness_layers bridge_angle extra_perimeters), ]; - $self->[S_DEPTH_LAYERS] //= 1; #/ + $self->[$_] //= 1 for S_THICKNESS, S_THICKNESS_LAYERS; bless $self, $class; $self; @@ -34,7 +35,8 @@ sub new { sub expolygon { $_[0][S_EXPOLYGON] } sub surface_type { $_[0][S_SURFACE_TYPE] = $_[1] if defined $_[1]; $_[0][S_SURFACE_TYPE] } -sub depth_layers { $_[0][S_DEPTH_LAYERS] } # this integer represents the thickness of the surface expressed in layers +sub thickness { $_[0][S_THICKNESS] } +sub thickness_layers { $_[0][S_THICKNESS_LAYERS] } sub bridge_angle { $_[0][S_BRIDGE_ANGLE] = $_[1] if defined $_[1]; $_[0][S_BRIDGE_ANGLE] } sub extra_perimeters { $_[0][S_EXTRA_PERIMETERS] = $_[1] if defined $_[1]; $_[0][S_EXTRA_PERIMETERS] } @@ -45,7 +47,8 @@ if (eval "use Class::XSAccessor::Array; 1") { }, accessors => { surface_type => S_SURFACE_TYPE, - depth_layers => S_DEPTH_LAYERS, + thickness => S_THICKNESS, + thickness_layers => S_THICKNESS_LAYERS, bridge_angle => S_BRIDGE_ANGLE, extra_perimeters => S_EXTRA_PERIMETERS, }, @@ -59,7 +62,7 @@ sub lines { $_[0]->expolygon->lines } sub contour { $_[0]->expolygon->contour } sub holes { $_[0]->expolygon->holes } -# static method to group surfaces having same surface_type, bridge_angle and depth_layers +# static method to group surfaces having same surface_type, bridge_angle and thickness* sub group { my $class = shift; my $params = ref $_[0] eq 'HASH' ? shift(@_) : {}; @@ -67,11 +70,11 @@ sub group { my %unique_types = (); foreach my $surface (@surfaces) { - my $type = ($params->{merge_solid} && $surface->is_solid) - ? 'solid' - : $surface->surface_type; - $type .= "_" . ($surface->bridge_angle // ''); #/ - $type .= "_" . $surface->depth_layers; + my $type = join '_', + ($params->{merge_solid} && $surface->is_solid) ? 'solid' : $surface->surface_type, + ($surface->bridge_angle // ''), + $surface->thickness, + $surface->thickness_layers; $unique_types{$type} ||= []; push @{ $unique_types{$type} }, $surface; } @@ -95,7 +98,7 @@ sub _inflate_expolygon { return (ref $self)->new( expolygon => $expolygon, - map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle), + map { $_ => $self->$_ } qw(surface_type thickness thickness_layers bridge_angle), ); }