From 6e6bc746367b4f50dfdac108ce067ac94ff31a30 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sun, 30 Dec 2012 16:27:20 +0100
Subject: [PATCH] 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__