From 26a3cd554249354603aff6ee53fb41bb27051d26 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 6 Feb 2013 12:03:53 +0100 Subject: [PATCH] Refactor chained_path(), add chained_path_items() and chained_path_points() --- lib/Slic3r/Fill.pm | 6 ++---- lib/Slic3r/Geometry.pm | 36 +++++++++++++++++++++++++----------- lib/Slic3r/Layer/Region.pm | 14 ++++++++++---- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index f3f371270..2c3882499 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -182,10 +182,8 @@ sub make_fill { push @fills, @{$layer->thin_fills}; push @fills_ordering_points, map $_->unpack->points->[0], @{$layer->thin_fills}; - # organize infill paths using a shortest path search - @fills = @{chained_path([ - map [ $fills_ordering_points[$_], $fills[$_] ], 0..$#fills, - ])}; + # organize infill paths using a nearest-neighbor search + @fills = @fills[ chained_path(\@fills_ordering_points) ]; return @fills; } diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 2132a978e..723345906 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -7,7 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw( PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel line_point_belongs_to_segment points_coincide distance_between_points - comparable_distance_between_points + comparable_distance_between_points chained_path_items chained_path_points line_length midpoint point_in_polygon point_in_segment segment_in_segment point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_along_segment polygon_segment_having_point polygon_has_subsegment @@ -799,29 +799,43 @@ sub polyline_remove_short_segments { } } -# accepts an arrayref; each item should be an arrayref whose first -# item is the point to be used for the shortest path, and the second -# one is the value to be returned in output (if the second item -# is not provided, the point will be returned) +# accepts an arrayref of points; it returns a list of indices +# according to a nearest-neighbor walk sub chained_path { my ($items, $start_near) = @_; - my %values = map +($_->[0] => $_->[1] || $_->[0]), @$items; - my @points = map $_->[0], @$items; + my @points = @$items; + my %indices = map { $points[$_] => $_ } 0 .. $#points; - my $result = []; + my @result = (); my $last_point; if (!$start_near) { $start_near = shift @points; - push @$result, $values{$start_near} if $start_near; + push @result, $indices{$start_near} if $start_near; } while (@points) { $start_near = nearest_point($start_near, [@points]); @points = grep $_ ne $start_near, @points; - push @$result, $values{$start_near}; + push @result, $indices{$start_near}; } - return $result; + return @result; +} + +# accepts an arrayref; each item should be an arrayref whose first +# item is the point to be used for the shortest path, and the second +# one is the value to be returned in output (if the second item +# is not provided, the point will be returned) +sub chained_path_items { + my ($items, $start_near) = @_; + + my @indices = chained_path([ map $_->[0], @$items ], $start_near); + return [ map $_->[1], @$items[@indices] ]; +} + +sub chained_path_points { + my ($points, $start_near) = @_; + return [ @$points[ chained_path($points, $start_near) ] ]; } sub douglas_peucker { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 6a925ddbb..daab1a6d1 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -2,7 +2,7 @@ package Slic3r::Layer::Region; use Moo; use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Geometry qw(scale chained_path); +use Slic3r::Geometry qw(scale chained_path_items); use Slic3r::Geometry::Clipper qw(safety_offset union_ex diff_ex intersection_ex); use Slic3r::Surface ':types'; @@ -167,7 +167,7 @@ sub make_perimeters { my @perimeters = (); # one item per depth; each item # organize islands using a shortest path search - my @surfaces = @{chained_path([ + my @surfaces = @{chained_path_items([ map [ $_->contour->[0], $_ ], @{$self->slices}, ])}; @@ -319,18 +319,24 @@ sub make_perimeters { } # process one island (original surface) at time + # islands are already sorted with a nearest-neighbor search foreach my $island (@perimeters) { # do holes starting from innermost one my @holes = (); my %is_external = (); + + # each item of @$island contains the expolygons having the same depth; + # for each depth we build an arrayref containing all the holes my @hole_depths = map [ map $_->holes, @$_ ], @$island; - # organize the outermost hole loops using a shortest path search - @{$hole_depths[0]} = @{chained_path([ + # organize the outermost hole loops using a nearest-neighbor search + @{$hole_depths[0]} = @{chained_path_items([ map [ $_->[0], $_ ], @{$hole_depths[0]}, ])}; + # loop while we have spare holes CYCLE: while (map @$_, @hole_depths) { + # remove first depth container if it contains no holes anymore shift @hole_depths while !@{$hole_depths[0]}; # take first available hole