diff --git a/MANIFEST b/MANIFEST index 67c40fd600..4170fa50e7 100644 --- a/MANIFEST +++ b/MANIFEST @@ -20,7 +20,6 @@ lib/Slic3r/Fill/Rectilinear.pm lib/Slic3r/Fill/Rectilinear2.pm lib/Slic3r/Geometry.pm lib/Slic3r/Geometry/Clipper.pm -lib/Slic3r/Geometry/DouglasPeucker.pm lib/Slic3r/GUI.pm lib/Slic3r/GUI/OptionsGroup.pm lib/Slic3r/GUI/SkeinPanel.pm diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 526791657d..18789a747d 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -108,6 +108,17 @@ sub bounding_box { return Slic3r::Geometry::bounding_box($self->contour); } +sub bounding_box_polygon { + my $self = shift; + my @bb = $self->bounding_box; + return Slic3r::Polygon->new([ + [ $bb[0], $bb[1] ], + [ $bb[2], $bb[1] ], + [ $bb[2], $bb[3] ], + [ $bb[0], $bb[3] ], + ]); +} + sub clip_line { my $self = shift; my ($line) = @_; @@ -141,6 +152,11 @@ sub clip_line { return [@lines]; } +sub simplify { + my $self = shift; + $_->simplify(@_) for @$self; +} + sub translate { my $self = shift; $_->translate(@_) for @$self; diff --git a/lib/Slic3r/ExtrusionPath.pm b/lib/Slic3r/ExtrusionPath.pm index fca813380d..77f54ce06c 100644 --- a/lib/Slic3r/ExtrusionPath.pm +++ b/lib/Slic3r/ExtrusionPath.pm @@ -45,6 +45,13 @@ sub clip_end { } } +sub clip_with_polygon { + my $self = shift; + my ($polygon) = @_; + + return $self->clip_with_expolygon(Slic3r::ExPolygon->new($polygon)); +} + sub clip_with_expolygon { my $self = shift; my ($expolygon) = @_; diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index f919c4a910..f28db1507c 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -18,11 +18,10 @@ our @EXPORT_OK = qw( polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges shortest_path collinear scale unscale merge_collinear_lines - rad2deg_dir bounding_box_center line_intersects_any + rad2deg_dir bounding_box_center line_intersects_any douglas_peucker polyline_remove_short_segments normal triangle_normal ); -use Slic3r::Geometry::DouglasPeucker qw(Douglas_Peucker); use XXX; use constant PI => 4 * atan2(1, 1); @@ -110,6 +109,19 @@ sub distance_between_points { return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2); } +sub point_line_distance { + my ($point, $line) = @_; + return distance_between_points($point, $line->[A]) + if same_point($line->[A], $line->[B]); + + my $n = ($line->[B][X] - $line->[A][X]) * ($line->[A][Y] - $point->[Y]) + - ($line->[A][X] - $point->[X]) * ($line->[B][Y] - $line->[A][Y]); + + my $d = sqrt((($line->[B][X] - $line->[A][X]) ** 2) + (($line->[B][Y] - $line->[A][Y]) ** 2)); + + return abs($n) / $d; +} + sub line_length { my ($line) = @_; return distance_between_points(@$line[A, B]); @@ -740,4 +752,97 @@ sub shortest_path { return $result; } +sub douglas_peucker { + my ($points, $tolerance) = @_; + + my $results = []; + my $dmax = 0; + my $index = 0; + for my $i (1..$#$points) { + my $d = point_line_distance($points->[$i], [ $points->[0], $points->[-1] ]); + if ($d > $dmax) { + $index = $i; + $dmax = $d; + } + } + if ($dmax >= $tolerance) { + my $dp1 = douglas_peucker([ @$points[0..$index] ], $tolerance); + $results = [ + @$dp1[0..($#$dp1-1)], + @{douglas_peucker([ @$points[$index..$#$points] ], $tolerance)}, + ]; + } else { + $results = [ $points->[0], $points->[-1] ]; + } + return $results; +} + +sub douglas_peucker2 { + my ($points, $tolerance) = @_; + + my $anchor = 0; + my $floater = $#$points; + my @stack = (); + my %keep = (); + + push @stack, [$anchor, $floater]; + while (@stack) { + ($anchor, $floater) = @{pop @stack}; + + # initialize line segment + my ($anchor_x, $anchor_y, $seg_len); + if (grep $points->[$floater][$_] != $points->[$anchor][$_], X, Y) { + $anchor_x = $points->[$floater][X] - $points->[$anchor][X]; + $anchor_y = $points->[$floater][Y] - $points->[$anchor][Y]; + $seg_len = sqrt(($anchor_x ** 2) + ($anchor_y ** 2)); + # get the unit vector + $anchor_x /= $seg_len; + $anchor_y /= $seg_len; + } else { + $anchor_x = $anchor_y = $seg_len = 0; + } + + # inner loop: + my $max_dist = 0; + my $farthest = $anchor + 1; + for my $i (($anchor + 1) .. $floater) { + my $dist_to_seg = 0; + # compare to anchor + my $vecX = $points->[$i][X] - $points->[$anchor][X]; + my $vecY = $points->[$i][Y] - $points->[$anchor][Y]; + $seg_len = sqrt(($vecX ** 2) + ($vecY ** 2)); + # dot product: + my $proj = $vecX * $anchor_x + $vecY * $anchor_y; + if ($proj < 0) { + $dist_to_seg = $seg_len; + } else { + # compare to floater + $vecX = $points->[$i][X] - $points->[$floater][X]; + $vecY = $points->[$i][Y] - $points->[$floater][Y]; + $seg_len = sqrt(($vecX ** 2) + ($vecY ** 2)); + # dot product: + $proj = $vecX * (-$anchor_x) + $vecY * (-$anchor_y); + if ($proj < 0) { + $dist_to_seg = $seg_len + } else { # calculate perpendicular distance to line (pythagorean theorem): + $dist_to_seg = sqrt(abs(($seg_len ** 2) - ($proj ** 2))); + } + if ($max_dist < $dist_to_seg) { + $max_dist = $dist_to_seg; + $farthest = $i; + } + } + } + + if ($max_dist <= $tolerance) { # use line segment + $keep{$_} = 1 for $anchor, $floater; + } else { + push @stack, [$anchor, $farthest]; + push @stack, [$farthest, $floater]; + } + } + + return [ map $points->[$_], sort keys %keep ]; +} + 1; diff --git a/lib/Slic3r/Geometry/DouglasPeucker.pm b/lib/Slic3r/Geometry/DouglasPeucker.pm deleted file mode 100644 index c09eb95f72..0000000000 --- a/lib/Slic3r/Geometry/DouglasPeucker.pm +++ /dev/null @@ -1,167 +0,0 @@ -package Slic3r::Geometry::DouglasPeucker; -use strict; -use warnings; - -BEGIN { - use Exporter ; - use vars qw ( $VERSION @ISA @EXPORT) ; - $VERSION = 1.0 ; - @ISA = qw ( Exporter ) ; - @EXPORT = qw ( - Douglas_Peucker - perp_distance - haversine_distance_meters - angle3points - ) ; -} - -# Call as: @Opoints = &Douglas_Peucker( , ) ; -# Returns: Array of points -# Points Array Format: -# ([lat1,lng1],[lat2,lng2],...[latn,lngn]) -# - -sub Douglas_Peucker -{ -my $href = shift ; -my $tolerance = shift ; -my @Ipoints = @$href ; -my @Opoints = ( ) ; -my @stack = ( ) ; -my $fIndex = 0 ; -my $fPoint = '' ; -my $aIndex = 0 ; -my $anchor = '' ; -my $max = 0 ; -my $maxIndex = 0 ; -my $point = '' ; -my $dist = 0 ; -my $polygon = 0 ; # Line Type - -$anchor = $Ipoints[0] ; # save first point - -push( @Opoints, $anchor ) ; - -$aIndex = 0 ; # Anchor Index - -# Check for a polygon: At least 4 points and the first point == last point... - -if ( $#Ipoints >= 4 and $Ipoints[0] == $Ipoints[$#Ipoints] ) -{ - $fIndex = $#Ipoints - 1 ; # Start from the next to last point - $polygon = 1 ; # It's a polygon - -} else -{ - $fIndex = $#Ipoints ; # It's a path (open polygon) -} - -push( @stack, $fIndex ) ; - -# Douglas - Peucker algorithm... - -while(@stack) -{ - $fIndex = $stack[$#stack] ; - $fPoint = $Ipoints[$fIndex] ; - $max = $tolerance ; # comparison values - $maxIndex = 0 ; - - # Process middle points... - - for (($aIndex+1) .. ($fIndex-1)) - { - $point = $Ipoints[$_] ; - $dist = &perp_distance($anchor, $fPoint, $point); - - if( $dist >= $max ) - { - $max = $dist ; - $maxIndex = $_; - } - } - - if( $maxIndex > 0 ) - { - push( @stack, $maxIndex ) ; - } else - { - push( @Opoints, $fPoint ) ; - $anchor = $Ipoints[(pop @stack)] ; - $aIndex = $fIndex ; - } -} - -if ( $polygon ) # Check for Polygon -{ - push( @Opoints, $Ipoints[$#Ipoints] ) ; # Add the last point - - # Check for collapsed polygons, use original data in that case... - - if( $#Opoints < 4 ) - { - @Opoints = @Ipoints ; - } -} - -return ( @Opoints ) ; - -} - -# Calculate Perpendicular Distance in meters between a line (two points) and a point... -# my $dist = &perp_distance( , , ) ; - -sub perp_distance # Perpendicular distance in meters -{ - my $lp1 = shift ; - my $lp2 = shift ; - my $p = shift ; - my $dist = &haversine_distance_meters( $lp1, $p ) ; - my $angle = &angle3points( $lp1, $lp2, $p ) ; - - return ( sprintf("%0.6f", abs($dist * sin($angle)) ) ) ; -} - -# Calculate Distance in meters between two points... - -sub haversine_distance_meters -{ - my $p1 = shift ; - my $p2 = shift ; - - my $O = 3.141592654/180 ; - my $b = $$p1[0] * $O ; - my $c = $$p2[0] * $O ; - my $d = $b - $c ; - my $e = ($$p1[1] * $O) - ($$p2[1] * $O) ; - my $f = 2 * &asin( sqrt( (sin($d/2) ** 2) + cos($b) * cos($c) * (sin($e/2) ** 2))); - - return sprintf("%0.4f",$f * 6378137) ; # Return meters - - sub asin - { - atan2($_[0], sqrt(1 - $_[0] * $_[0])) ; - } -} - -# Calculate Angle in Radians between three points... - -sub angle3points # Angle between three points in radians -{ - my $p1 = shift ; - my $p2 = shift ; - my $p3 = shift ; - my $m1 = &slope( $p2, $p1 ) ; - my $m2 = &slope( $p3, $p1 ) ; - - return ($m2 - $m1) ; - - sub slope # Slope in radians - { - my $p1 = shift ; - my $p2 = shift ; - return atan2( (@$p2[1] - @$p1[1]),( @$p2[0] - @$p1[0] )); - } -} - -1; \ No newline at end of file diff --git a/lib/Slic3r/Polygon.pm b/lib/Slic3r/Polygon.pm index 5ec5d9a3fa..d577a24170 100644 --- a/lib/Slic3r/Polygon.pm +++ b/lib/Slic3r/Polygon.pm @@ -109,7 +109,6 @@ sub subdivide { my $len = Slic3r::Geometry::line_length([ $self->[$i-1], $self->[$i] ]); my $num_points = int($len / $max_length) - 1; $num_points++ if $len % $max_length; - next unless $num_points; # $num_points is the number of points to add between $i-1 and $i my $spacing = $len / ($num_points + 1); diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm index a8155764d9..e0d63f5ae6 100644 --- a/lib/Slic3r/Polyline.pm +++ b/lib/Slic3r/Polyline.pm @@ -57,7 +57,7 @@ sub simplify { my $self = shift; my $tolerance = shift || 10; - @$self = Slic3r::Geometry::Douglas_Peucker($self, $tolerance); + @$self = @{ Slic3r::Geometry::douglas_peucker($self, $tolerance) }; bless $_, 'Slic3r::Point' for @$self; } @@ -110,6 +110,9 @@ sub clip_with_expolygon { my $self = shift; my ($expolygon) = @_; + #printf "Clipping polyline of %d points to expolygon of %d polygons and %d points\n", + # scalar(@$self), scalar(@$expolygon), scalar(map @$_, @$expolygon); + my @polylines = (); my $current_polyline = []; foreach my $line ($self->lines) { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 3a1101131f..ff0627133a 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -364,8 +364,6 @@ sub infill_every_layers { my $self = shift; return unless $Slic3r::infill_every_layers > 1 && $Slic3r::fill_density > 0; - printf "==> COMBINING INFILL\n"; - # start from bottom, skip first layer for (my $i = 1; $i < $self->layer_count; $i++) { my $layer = $self->layer($i); @@ -489,6 +487,14 @@ sub generate_support_material { ), @paths; } } + $_->polyline->simplify(scale $Slic3r::flow_spacing / 3) for @$support_pattern; + + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output(undef, "support.svg", + polylines => [ map $_->polyline, @$support_pattern ], + ); + } # now apply the pattern to layers below unsupported surfaces my (@a, @b) = (); @@ -502,13 +508,15 @@ sub generate_support_material { )}; $layer->support_fills(Slic3r::ExtrusionPath::Collection->new); foreach my $expolygon (@c) { - push @{$layer->support_fills->paths}, map $_->clip_with_expolygon($expolygon), @$support_pattern; + push @{$layer->support_fills->paths}, map $_->clip_with_expolygon($expolygon), + map $_->clip_with_polygon($expolygon->bounding_box_polygon), @$support_pattern; } } @b = @{union_ex([ map @$_, @c, @a ])}; @a = map $_->expolygon->offset_ex(scale 2), grep $_->surface_type eq 'bottom' && !defined $_->bridge_angle, @{$layer->slices}; + $_->simplify(scale $Slic3r::flow_spacing * 3) for @a; } } diff --git a/t/clean_polylines.t b/t/clean_polylines.t index 8f9cbafa11..bc7d864e6f 100644 --- a/t/clean_polylines.t +++ b/t/clean_polylines.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 3; +plan tests => 6; BEGIN { use FindBin; @@ -20,6 +20,22 @@ use Slic3r; is scalar(@$polygon), 3, 'merge_continuous_lines'; } +{ + 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); + is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker'; +} + +{ + my $polyline = Slic3r::Polyline->new([ + [0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5], + ]); + $polyline->simplify(0.25); + is_deeply $polyline, [ [0, 0], [0.5, 0.5], [1.25, -0.25], [1.5, 0.5] ], 'Douglas-Peucker'; +} + { my $gear = [ [144.9694,317.1543], [145.4181,301.5633], [146.3466,296.921], [131.8436,294.1643], [131.7467,294.1464], @@ -57,13 +73,10 @@ use Slic3r; note sprintf "original points: %d\nnew points: %d", scalar(@$gear), scalar(@$polygon); ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines'; - # simplify() is not being used, so we don't test it - if (0) { - 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 $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'; } {