From a86bc260e7eb2cac6ee160fd8cbae3b411c34a69 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 24 Dec 2011 11:01:28 +0100 Subject: [PATCH] Bugfix: some islands having ho holes and contained in holes themselves were ignored --- MANIFEST | 1 + lib/Slic3r/ExPolygon.pm | 7 +++++++ lib/Slic3r/Geometry/Clipper.pm | 14 ++++++++++++- lib/Slic3r/Layer.pm | 37 ++++++++++++++++++++++++---------- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/MANIFEST b/MANIFEST index b80c4a829..40cfc3f6c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -49,3 +49,4 @@ t/collinear.t t/geometry.t t/polyclip.t t/stl.t +utils/post-processing/z-every-line.pl diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 6fc391a41..eddeac8e3 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -140,4 +140,11 @@ sub rotate { $_->rotate(@_) for @$self; } +sub area { + my $self = shift; + my $area = $self->contour->area; + $area -= $_->area for $self->holes; + return $area; +} + 1; diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 05e7b6c10..f22466b65 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -5,7 +5,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset - diff_ex diff union_ex intersection_ex PFT_EVENODD JT_MITER JT_ROUND + diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND is_counter_clockwise); use Math::Clipper 1.02 ':all'; @@ -66,4 +66,16 @@ sub intersection_ex { ]; } +sub xor_ex { + my ($subject, $clip, $jointype) = @_; + $jointype = PFT_NONZERO unless defined $jointype; + $clipper->clear; + $clipper->add_subject_polygons($subject); + $clipper->add_clip_polygons($clip); + return [ + map Slic3r::ExPolygon->new($_), + @{ $clipper->ex_execute(CT_XOR, $jointype, $jointype) }, + ]; +} + 1; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 8941261af..eab34e029 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -3,7 +3,7 @@ use Moo; use Math::Clipper ':all'; use Slic3r::Geometry qw(scale collinear X Y A B PI rad2deg_dir bounding_box_center); -use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex is_counter_clockwise); +use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex xor_ex is_counter_clockwise); use XXX; # a sequential number of layer, starting at 0 @@ -112,20 +112,35 @@ sub make_surfaces { # we need to ignore such holes, but Clipper will convert them to contours. # so we identify them and remove them manually. + my $area_sum = sub { + my $area = 0; + $area += $_->area for @_; + return $area; + }; + # get expolygons without holes (candidate for reverse holes detection) my @expolygons_without_holes = grep { @$_ == 1 } @$expolygons; - # remove all holes from such expolygons - my $diff = diff_ex( - [ map @$_, @expolygons_without_holes ], - [ map [ reverse @$_ ], grep !is_counter_clockwise($_), @$loops ], - ); + # prepare holes as contours to allow for safe xor'ing + my @reversed_holes = map [ reverse @$_ ], grep !is_counter_clockwise($_), @$loops; - # merge resulting holes (true holes) and other expolygons - $expolygons = [ - (grep { @$_ > 1 } @$expolygons), - @$diff, - ]; + # compare each expolygon without holes with each original hole; if their XOR + # is empty then they're the same and we can remove the hole from our layer + my %bogus_holes = (); + foreach my $contour (map $_->contour, @expolygons_without_holes) { + foreach my $hole (grep !exists $bogus_holes{$_}, @reversed_holes) { + my $xor = xor_ex([$contour], [$hole]); + if (!@$xor || $area_sum->(@$xor) < scale 1) { # TODO: define this threshold better + $bogus_holes{$hole} = $hole; + } + } + } + + # remove identified holes + $expolygons = diff_ex( + [ map @$_, @$expolygons ], + [ values %bogus_holes ], + ) if %bogus_holes; Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n", scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);