Bugfix: some holes being closed incorrectly. #858
This commit is contained in:
parent
6e6bc74636
commit
141a8d3989
@ -14,7 +14,7 @@ our $clipper = Math::Clipper->new;
|
|||||||
|
|
||||||
sub safety_offset {
|
sub safety_offset {
|
||||||
my ($polygons, $factor) = @_;
|
my ($polygons, $factor) = @_;
|
||||||
return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100000, JT_MITER, 2);
|
return Math::Clipper::offset($polygons, $factor // (scale 1e-05), 100000, JT_MITER, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub offset {
|
sub offset {
|
||||||
|
@ -118,11 +118,28 @@ sub make_surfaces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub _merge_loops {
|
sub _merge_loops {
|
||||||
my ($loops) = @_;
|
my ($loops, $safety_offset) = @_;
|
||||||
|
|
||||||
my $safety_offset = scale 0.1;
|
# Input loops are not suitable for evenodd nor nonzero fill types, as we might get
|
||||||
# merge everything
|
# two consecutive concentric loops having the same winding order - and we have to
|
||||||
my $expolygons = [ map $_->offset_ex(-$safety_offset), @{union_ex(safety_offset($loops, $safety_offset))} ];
|
# respect such order. In that case, evenodd would create wrong inversions, and nonzero
|
||||||
|
# would ignore holes inside two concentric contours.
|
||||||
|
# So we're ordering loops and collapse consecutive concentric loops having the same
|
||||||
|
# winding order.
|
||||||
|
# TODO: find a faster algorithm for this.
|
||||||
|
my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first
|
||||||
|
$safety_offset //= scale 0.1;
|
||||||
|
@loops = @{ safety_offset(\@loops, $safety_offset) };
|
||||||
|
my $expolygons = [];
|
||||||
|
while (my $loop = shift @loops) {
|
||||||
|
bless $loop, 'Slic3r::Polygon';
|
||||||
|
if ($loop->is_counter_clockwise) {
|
||||||
|
$expolygons = union_ex([ $loop, map @$_, @$expolygons ]);
|
||||||
|
} else {
|
||||||
|
$expolygons = diff_ex([ map @$_, @$expolygons ], [$loop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$expolygons = [ map $_->offset_ex(-$safety_offset), @$expolygons ];
|
||||||
|
|
||||||
Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
|
Slic3r::debugf " %d surface(s) having %d holes detected from %d polylines\n",
|
||||||
scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
|
scalar(@$expolygons), scalar(map $_->holes, @$expolygons), scalar(@$loops);
|
||||||
|
@ -13,6 +13,12 @@ use Slic3r;
|
|||||||
use Slic3r::Test;
|
use Slic3r::Test;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
# We only need to slice at one height, so we'll build a non-manifold mesh
|
||||||
|
# that still produces complete loops at that height. Triangular walls are
|
||||||
|
# enough for this purpose.
|
||||||
|
# Basically we want to check what happens when three concentric loops happen
|
||||||
|
# to be at the same height, the two external ones being ccw and the other being
|
||||||
|
# a hole, thus cw.
|
||||||
my (@vertices, @facets) = ();
|
my (@vertices, @facets) = ();
|
||||||
Slic3r::Test::add_facet($_, \@vertices, \@facets) for
|
Slic3r::Test::add_facet($_, \@vertices, \@facets) for
|
||||||
# external surface below the slicing Z
|
# external surface below the slicing Z
|
||||||
@ -39,7 +45,7 @@ use Slic3r::Test;
|
|||||||
is scalar(@$loops), 3, 'correct number of loops detected';
|
is scalar(@$loops), 3, 'correct number of loops detected';
|
||||||
is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected';
|
is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected';
|
||||||
|
|
||||||
my @surfaces = Slic3r::Layer::Region::_merge_loops($loops);
|
my @surfaces = Slic3r::Layer::Region::_merge_loops($loops, 0);
|
||||||
is scalar(@surfaces), 1, 'one surface detected';
|
is scalar(@surfaces), 1, 'one surface detected';
|
||||||
is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole';
|
is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user