From 84abd41cf454bcc9bccaa0c3921263d519106d97 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 9 Oct 2011 22:18:06 +0200
Subject: [PATCH] Fixes for bridges

---
 lib/Slic3r.pm                 |  2 +-
 lib/Slic3r/Geometry.pm        | 37 ++++++++++++++++++++++++++++++
 lib/Slic3r/Layer.pm           | 23 +++++++++++++++++--
 lib/Slic3r/Polyline.pm        | 30 +++++++++++++++---------
 lib/Slic3r/Polyline/Closed.pm | 12 ----------
 lib/Slic3r/Print.pm           |  9 ++++++++
 lib/Slic3r/SVG.pm             |  2 +-
 t/geometry.t                  | 43 +++++++++++++++++++++++++++++++++--
 t/stl.t                       |  4 ++--
 9 files changed, 131 insertions(+), 31 deletions(-)

diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm
index 49909a7a5..d399cedaf 100644
--- a/lib/Slic3r.pm
+++ b/lib/Slic3r.pm
@@ -55,7 +55,7 @@ our $flow_width;
 # print options
 our $perimeter_offsets  = 3;
 our $solid_layers       = 3;
-our $bridge_overlap     = 2;    # mm
+our $bridge_overlap     = 3;    # mm
 our $fill_type          = 'rectilinear';
 our $fill_density       = 0.4;  # 1 = 100%
 our $fill_angle         = 0;
diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm
index 30104e109..d53193677 100644
--- a/lib/Slic3r/Geometry.pm
+++ b/lib/Slic3r/Geometry.pm
@@ -15,6 +15,8 @@ our @EXPORT_OK = qw(
     sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
     line_intersection bounding_box bounding_box_intersect 
     clip_segment_complex_polygon longest_segment angle3points
+    polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
+    polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges
 );
 
 use Slic3r::Geometry::DouglasPeucker qw(Douglas_Peucker);
@@ -575,4 +577,39 @@ sub angle3points {
     return $angle <= 0 ? $angle + 2*PI() : $angle;
 }
 
+sub polyline_remove_parallel_continuous_edges {
+    my ($points, $isPolygon) = @_;
+    
+    for (my $i = $isPolygon ? 0 : 2; $i <= $#$points; $i++) {
+        if (Slic3r::Geometry::lines_parallel([$points->[$i-2], $points->[$i-1]], [$points->[$i-1], $points->[$i]])) {
+            # we can remove $points->[$i-1]
+            splice @$points, $i-1, 1;
+            $i--;
+        }
+    }
+}
+
+sub polygon_remove_parallel_continuous_edges {
+    my ($points) = @_;
+    return polyline_remove_parallel_continuous_edges($points, 1);
+}
+
+sub polyline_remove_acute_vertices {
+    my ($points, $isPolygon) = @_;
+    
+    for (my $i = $isPolygon ? -1 : 1; $i < $#$points; $i++) {
+        my $angle = angle3points($points->[$i], $points->[$i-1], $points->[$i+1]);
+        if ($angle < 0.01 || $angle >= 2*PI - 0.01) {
+            # we can remove $points->[$i]
+            splice @$points, $i, 1;
+            $i--;
+        }
+    }
+}
+
+sub polygon_remove_acute_vertices {
+    my ($points) = @_;
+    return polyline_remove_acute_vertices($points, 1);
+}
+
 1;
diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm
index 3c250ca2c..267424f39 100644
--- a/lib/Slic3r/Layer.pm
+++ b/lib/Slic3r/Layer.pm
@@ -157,8 +157,21 @@ sub make_surfaces {
         my %seen_points = map { $get_point_id->($points[$_]) => $_ } 0..1;
         
         CYCLE: while (1) {
-            my $next_lines = $pointmap{ $get_point_id->($points[-1]) }
-                or die sprintf "No lines start at point %d,%d. This shouldn't happen", @{$points[-1]};
+            my $next_lines = $pointmap{ $get_point_id->($points[-1]) };
+            
+            # shouldn't we find the point, let's try with a slower algorithm
+            # as approximation may make the coordinates differ
+            if (!$next_lines) {
+                local $Slic3r::Geometry::epsilon = 1;
+                for (keys %pointmap) {
+                    $next_lines = $pointmap{$_} if points_coincide($points[-1], [ split /,/, $_ ]);
+                    last if $next_lines;
+                }
+            }
+            
+            $next_lines
+                or die sprintf "No lines start at point %s. This shouldn't happen", 
+                    $get_point_id->($points[-1]);
             last CYCLE if !@$next_lines;
             
             my @ordered_next_lines = sort 
@@ -256,6 +269,12 @@ sub process_bridges {
         # in a convex polygon; this will print thin membranes eventually
         my $surface_p = convex_hull($surface->contour->p);
         
+            #use Slic3r::SVG;
+            #Slic3r::SVG::output(undef, "bridge.svg",
+            #    green_polygons  => [ map $_->p, @supporting_surfaces ],
+            #    red_polygons    => [ $surface_p ],
+            #);
+        
         # find all supported edges (as polylines, thus keeping notion of 
         # consecutive supported edges)
         my @supported_polylines = ();
diff --git a/lib/Slic3r/Polyline.pm b/lib/Slic3r/Polyline.pm
index 3f321cc63..51846e8db 100644
--- a/lib/Slic3r/Polyline.pm
+++ b/lib/Slic3r/Polyline.pm
@@ -2,6 +2,8 @@ package Slic3r::Polyline;
 use Moo;
 
 use Math::Clipper qw();
+use Slic3r::Geometry qw(polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices
+    polygon_remove_acute_vertices polygon_remove_parallel_continuous_edges);
 use Sub::Quote;
 use XXX;
 
@@ -46,18 +48,24 @@ sub p {
 
 sub merge_continuous_lines {
     my $self = shift;
-    
-    my @points = map $_->p, @{$self->points};
-    for (my $i = 2; $i <= $#points; $i++) {
-        if (Slic3r::Geometry::lines_parallel([$points[$i-2], $points[$i-1]], [$points[$i-1], $points[$i]])) {
-            # we can remove $points[$i-1]
-            splice @points, $i-1, 1;
-            
-            $i--;
-        }
+    my $points = $self->p;
+    if ($self->isa('Slic3r::Polyline::Closed')) {
+        polygon_remove_parallel_continuous_edges($points);
+    } else {
+        polyline_remove_parallel_continuous_edges($points);
     }
-    
-    @{$self->points} = map Slic3r::Point->cast($_), @points;
+    @{$self->points} = map Slic3r::Point->cast($_), @$points;
+}
+
+sub remove_acute_vertices {
+    my $self = shift;
+    my $points = $self->p;
+    if ($self->isa('Slic3r::Polyline::Closed')) {
+        polygon_remove_acute_vertices($points);
+    } else {
+        polyline_remove_acute_vertices($points);
+    }
+    @{$self->points} = map Slic3r::Point->cast($_), @$points;
 }
 
 sub cleanup {
diff --git a/lib/Slic3r/Polyline/Closed.pm b/lib/Slic3r/Polyline/Closed.pm
index 5ad6d97ca..986d915fb 100644
--- a/lib/Slic3r/Polyline/Closed.pm
+++ b/lib/Slic3r/Polyline/Closed.pm
@@ -15,18 +15,6 @@ sub lines {
     return @lines;
 }
 
-# superclass doesn't check whether last line of our closed polyline
-# is parallel to first one, so let's do it here
-sub merge_continuous_lines {
-    my $self = shift;
-    $self->SUPER::merge_continuous_lines(@_);
-    
-    my @lines = $self->lines;
-    if ($lines[-1]->parallel_to($lines[0])) {
-        shift @{$self->points};
-    }
-}
-
 sub encloses_point {
     my $self = shift;
     my ($point) = @_;
diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm
index 783b126b8..8cff162da 100644
--- a/lib/Slic3r/Print.pm
+++ b/lib/Slic3r/Print.pm
@@ -120,6 +120,15 @@ sub detect_surfaces_type {
         # of current layer and lower one)
         if ($lower_layer) {
             @bottom = $surface_difference->($layer->surfaces, $lower_layer->surfaces, 'bottom');
+            for (@bottom) {
+                $_->contour->merge_continuous_lines;
+                $_->contour->remove_acute_vertices;
+        
+                # okay, this is an Ugly Hack(tm) to avoid floating point math problems
+                # with diagonal bridges. will find a nicer solution, promised.
+                my $offset = offset([$_->contour->p], 100, 100, JT_MITER, 2);
+                @{$_->contour->points} = map Slic3r::Point->cast($_), @{ $offset->[0] };
+            }
             
             #Slic3r::SVG::output(undef, "layer_" . $layer->id . "_diff.svg",
             #    green_polygons  => [ map $_->p, @{$layer->surfaces} ],
diff --git a/lib/Slic3r/SVG.pm b/lib/Slic3r/SVG.pm
index ff3faa66b..68b0321b2 100644
--- a/lib/Slic3r/SVG.pm
+++ b/lib/Slic3r/SVG.pm
@@ -65,7 +65,7 @@ sub output {
     
     foreach my $type (qw(points red_points)) {
         if ($things{$type}) {
-            my ($colour, $r) = $type eq 'points' ? ('black', 2) : ('red', 3);
+            my ($colour, $r) = $type eq 'points' ? ('black', 5) : ('red', 3);
             my $g = $svg->group(
                 style => {
                     'stroke-width' => 2,
diff --git a/t/geometry.t b/t/geometry.t
index 4c65d6ae5..24746e451 100644
--- a/t/geometry.t
+++ b/t/geometry.t
@@ -2,7 +2,7 @@ use Test::More;
 use strict;
 use warnings;
 
-plan tests => 15;
+plan tests => 17;
 
 BEGIN {
     use FindBin;
@@ -10,7 +10,9 @@ BEGIN {
 }
 
 use Slic3r;
-use Slic3r::Geometry qw(PI);
+use Slic3r::Geometry qw(PI polyline_remove_parallel_continuous_edges 
+    polyline_remove_acute_vertices polygon_remove_acute_vertices
+    polygon_remove_parallel_continuous_edges);
 
 #==========================================================
 
@@ -114,3 +116,40 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po
 }
 
 #==========================================================
+
+{
+    my $polygon = [
+        [2265447881, 7013509857], [2271869937, 7009802077], [2606221146, 6816764300], [1132221146, 4263721402], 
+        [1098721150, 4205697705], [1228411320, 4130821051], [1557501031, 3940821051], [1737340080, 3836990933], 
+        [1736886253, 3837252951], [1494771522, 3977037948], [2959638603, 6514262167], [3002271522, 6588104548], 
+        [3083252364, 6541350240], [2854283608, 6673545411], [2525193897, 6863545411],
+    ];
+    polygon_remove_parallel_continuous_edges($polygon);
+    polygon_remove_acute_vertices($polygon);
+    is scalar(@$polygon), 4, 'polygon_remove_acute_vertices';
+    
+    use Slic3r::SVG;
+    #pop @$polygon;
+    Slic3r::SVG::output(undef, "vert.svg",
+        polylines => [$polygon],
+    );
+}
+
+#==========================================================
+
+{
+    my $polygon = [
+        [226.5447881,701.3509857], [260.6221146,681.67643], [109.872115,420.5697705], [149.4771522,397.7037948], 
+        [300.2271522,658.8104548], [308.3252364,654.135024],
+    ];
+    polyline_remove_acute_vertices($polygon);
+    is scalar(@$polygon), 6, 'polyline_remove_acute_vertices';
+    use Slic3r::SVG;
+    local $Slic3r::resolution = 0.1;
+    pop @$polygon;
+    Slic3r::SVG::output(undef, "vert2.svg",
+        polylines => [$polygon],
+    );
+}
+
+#==========================================================
\ No newline at end of file
diff --git a/t/stl.t b/t/stl.t
index d891bc270..b06f2189c 100644
--- a/t/stl.t
+++ b/t/stl.t
@@ -25,8 +25,8 @@ is_deeply lines(22, 20, 20), [ [ $points[2], $points[1] ] ], 'lower edge on laye
 is_deeply lines(20, 20, 10), [ [ $points[0], $points[1] ] ], 'upper edge on layer';
 is_deeply lines(20, 15, 10), [                            ], 'upper vertex on layer';
 is_deeply lines(28, 20, 30), [                            ], 'lower vertex on layer';
-is_deeply lines(24, 10, 16), [ [ [2, 6],     [4, 4]     ] ], 'two edges intersect';
-is_deeply lines(24, 10, 20), [ [ [1, 9],     [4, 4]     ] ], 'one vertex on plane and one edge intersects';
+is_deeply lines(24, 10, 16), [ [ [4, 4],     [2, 6]     ] ], 'two edges intersect';
+is_deeply lines(24, 10, 20), [ [ [4, 4],     [1, 9]     ] ], 'one vertex on plane and one edge intersects';
 
 my @lower = $stl->intersect_facet(vertices(22, 20, 20), $z, $dz);
 my @upper = $stl->intersect_facet(vertices(20, 20, 10), $z, $dz);