Always fix self-intersecting polygons that Douglas-Peucker might return
This commit is contained in:
parent
dc0f706789
commit
33b40eda18
@ -172,8 +172,13 @@ sub clip_line {
|
|||||||
|
|
||||||
sub simplify {
|
sub simplify {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
$_->simplify(@_) for @$self;
|
my ($tolerance) = @_;
|
||||||
$self;
|
|
||||||
|
# it would be nice to have a multilinestring_simplify method in B::G::U
|
||||||
|
my @simplified = Slic3r::Geometry::Clipper::simplify_polygons(
|
||||||
|
[ map Boost::Geometry::Utils::linestring_simplify($_, $tolerance), @$self ],
|
||||||
|
);
|
||||||
|
return @{ Slic3r::Geometry::Clipper::union_ex([ @simplified ]) };
|
||||||
}
|
}
|
||||||
|
|
||||||
sub scale {
|
sub scale {
|
||||||
|
@ -81,6 +81,11 @@ sub clip_with_expolygon {
|
|||||||
return @paths;
|
return @paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub simplify {
|
||||||
|
my $self = shift;
|
||||||
|
$self->polyline($self->polyline->simplify(@_));
|
||||||
|
}
|
||||||
|
|
||||||
sub points {
|
sub points {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return $self->polyline;
|
return $self->polyline;
|
||||||
|
@ -56,22 +56,20 @@ sub BUILD {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# simplify islands
|
||||||
|
@{$self->islands} = map $_->simplify($self->_inner_margin), @{$self->islands};
|
||||||
|
|
||||||
# process individual islands
|
# process individual islands
|
||||||
for my $i (0 .. $#{$self->islands}) {
|
for my $i (0 .. $#{$self->islands}) {
|
||||||
# simplify the island's contours
|
|
||||||
$self->islands->[$i]->simplify($self->_inner_margin);
|
|
||||||
|
|
||||||
# offset the island inwards to make the boundaries for internal movements
|
# offset the island inwards to make the boundaries for internal movements
|
||||||
# so that no motion along external perimeters happens
|
# so that no motion along external perimeters happens
|
||||||
$self->_inner->[$i] = [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ]
|
$self->_inner->[$i] = $self->no_internal
|
||||||
if !$self->no_internal;
|
? []
|
||||||
|
: [ $self->islands->[$i]->offset_ex(-$self->_inner_margin) ];
|
||||||
|
|
||||||
# offset the island outwards to make the boundaries for external movements
|
# offset the island outwards to make the boundaries for external movements
|
||||||
$self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ];
|
$self->_outer->[$i] = [ $self->islands->[$i]->contour->offset($self->_outer_margin) ];
|
||||||
|
|
||||||
# further simplification (isn't this a duplication of the one above?)
|
|
||||||
$_->simplify($self->_inner_margin) for @{$self->_inner->[$i]}, @{$self->_outer->[$i]};
|
|
||||||
|
|
||||||
# if internal motion is enabled, build a set of utility expolygons representing
|
# if internal motion is enabled, build a set of utility expolygons representing
|
||||||
# the outer boundaries (as contours) and the inner boundaries (as holes). whenever
|
# the outer boundaries (as contours) and the inner boundaries (as holes). whenever
|
||||||
# we jump from a hole to a contour or viceversa, we know we're crossing a perimeter
|
# we jump from a hole to a contour or viceversa, we know we're crossing a perimeter
|
||||||
|
@ -1137,9 +1137,8 @@ sub make_thumbnail {
|
|||||||
for (map @$_, map @$_, @{$thumbnail->expolygons}) {
|
for (map @$_, map @$_, @{$thumbnail->expolygons}) {
|
||||||
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
|
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
|
||||||
}
|
}
|
||||||
|
@{$thumbnail->expolygons} = map $_->simplify(0.5), grep $_->area >= 1, @{$thumbnail->expolygons};
|
||||||
foreach my $expolygon (@{$thumbnail->expolygons}) {
|
foreach my $expolygon (@{$thumbnail->expolygons}) {
|
||||||
@$expolygon = grep $_->area >= 1, @$expolygon;
|
|
||||||
$expolygon->simplify(0.5);
|
|
||||||
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate));
|
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate));
|
||||||
$expolygon->scale($self->scale);
|
$expolygon->scale($self->scale);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ our @EXPORT_OK = qw(
|
|||||||
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
|
point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
|
||||||
point_along_segment polygon_segment_having_point polygon_has_subsegment
|
point_along_segment polygon_segment_having_point polygon_has_subsegment
|
||||||
polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg
|
polygon_has_vertex polyline_length can_connect_points deg2rad rad2deg
|
||||||
rotate_points move_points remove_coinciding_points clip_segment_polygon
|
rotate_points move_points clip_segment_polygon
|
||||||
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
|
sum_vectors multiply_vector subtract_vectors dot perp polygon_points_visibility
|
||||||
line_intersection bounding_box bounding_box_intersect same_point same_line
|
line_intersection bounding_box bounding_box_intersect same_point same_line
|
||||||
longest_segment angle3points three_points_aligned line_direction
|
longest_segment angle3points three_points_aligned line_direction
|
||||||
@ -375,15 +375,6 @@ sub move_points {
|
|||||||
return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points;
|
return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points;
|
||||||
}
|
}
|
||||||
|
|
||||||
# preserves order
|
|
||||||
sub remove_coinciding_points {
|
|
||||||
my ($points) = @_;
|
|
||||||
|
|
||||||
my %p = map { sprintf('%f,%f', @$_) => "$_" } @$points;
|
|
||||||
%p = reverse %p;
|
|
||||||
@$points = grep $p{"$_"}, @$points;
|
|
||||||
}
|
|
||||||
|
|
||||||
# implementation of Liang-Barsky algorithm
|
# implementation of Liang-Barsky algorithm
|
||||||
# polygon must be convex and ccw
|
# polygon must be convex and ccw
|
||||||
sub clip_segment_polygon {
|
sub clip_segment_polygon {
|
||||||
|
@ -92,4 +92,14 @@ sub collapse_ex {
|
|||||||
return union_ex([@result]);
|
return union_ex([@result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub simplify_polygon {
|
||||||
|
my ($polygon, $pft) = @_;
|
||||||
|
return @{ Math::Clipper::simplify_polygon($polygon, $pft // PFT_NONZERO) };
|
||||||
|
}
|
||||||
|
|
||||||
|
sub simplify_polygons {
|
||||||
|
my ($polygons, $pft) = @_;
|
||||||
|
return @{ Math::Clipper::simplify_polygons($polygons, $pft // PFT_NONZERO) };
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -263,14 +263,14 @@ sub make_perimeters {
|
|||||||
# we offset by half the perimeter spacing (to get to the actual infill boundary)
|
# we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||||
# and then we offset back and forth by the infill spacing to only consider the
|
# and then we offset back and forth by the infill spacing to only consider the
|
||||||
# non-collapsing regions
|
# non-collapsing regions
|
||||||
my @fill_boundaries = @{union_ex([
|
push @{ $self->fill_surfaces },
|
||||||
|
map $_->simplify(&Slic3r::SCALED_RESOLUTION),
|
||||||
|
@{union_ex([
|
||||||
Slic3r::Geometry::Clipper::offset(
|
Slic3r::Geometry::Clipper::offset(
|
||||||
[Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))],
|
[Slic3r::Geometry::Clipper::offset([ map @$_, @last_offsets ], -($perimeter_spacing/2 + $infill_spacing))],
|
||||||
+$infill_spacing,
|
+$infill_spacing,
|
||||||
),
|
),
|
||||||
])};
|
])};
|
||||||
$_->simplify(&Slic3r::SCALED_RESOLUTION) for @fill_boundaries;
|
|
||||||
push @{ $self->fill_surfaces }, @fill_boundaries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# fill gaps
|
# fill gaps
|
||||||
|
@ -97,6 +97,11 @@ sub grow {
|
|||||||
return $self->split_at_first_point->grow(@_);
|
return $self->split_at_first_point->grow(@_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub simplify {
|
||||||
|
my $self = shift;
|
||||||
|
return Slic3r::Geometry::Clipper::simplify_polygon( $self->SUPER::simplify(@_) );
|
||||||
|
}
|
||||||
|
|
||||||
# this method subdivides the polygon segments to that no one of them
|
# this method subdivides the polygon segments to that no one of them
|
||||||
# is longer than the length provided
|
# is longer than the length provided
|
||||||
sub subdivide {
|
sub subdivide {
|
||||||
|
@ -68,8 +68,8 @@ sub simplify {
|
|||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $tolerance = shift || 10;
|
my $tolerance = shift || 10;
|
||||||
|
|
||||||
@$self = @{ Boost::Geometry::Utils::linestring_simplify($self, $tolerance) };
|
my $simplified = Boost::Geometry::Utils::linestring_simplify($self, $tolerance);
|
||||||
bless $_, 'Slic3r::Point' for @$self;
|
return (ref $self)->new($simplified);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reverse {
|
sub reverse {
|
||||||
|
@ -335,8 +335,8 @@ sub export_gcode {
|
|||||||
# simplify slices (both layer and region slices),
|
# simplify slices (both layer and region slices),
|
||||||
# we only need the max resolution for perimeters
|
# we only need the max resolution for perimeters
|
||||||
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
|
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
|
||||||
$_->simplify(&Slic3r::SCALED_RESOLUTION)
|
@$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_
|
||||||
for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions});
|
for $layer->slices, (map $_->slices, @{$layer->regions});
|
||||||
}
|
}
|
||||||
|
|
||||||
# this will assign a type (top/bottom/internal) to $layerm->slices
|
# this will assign a type (top/bottom/internal) to $layerm->slices
|
||||||
|
@ -774,8 +774,10 @@ sub generate_support_material {
|
|||||||
[ map @$_, @{ $upper_layers_overhangs[-1] || [] } ],
|
[ map @$_, @{ $upper_layers_overhangs[-1] || [] } ],
|
||||||
[ map @$_, @current_layer_offsetted_slices ],
|
[ map @$_, @current_layer_offsetted_slices ],
|
||||||
);
|
);
|
||||||
$layers_contact_areas{$i} = collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width);
|
$layers_contact_areas{$i} = [
|
||||||
$_->simplify($flow->scaled_spacing) for @{$layers_contact_areas{$i}};
|
map $_->simplify($flow->scaled_spacing),
|
||||||
|
collapse_ex([ map @$_, @{$layers_contact_areas{$i}} ], $flow->scaled_width),
|
||||||
|
];
|
||||||
|
|
||||||
# to define interface regions of this layer we consider the overhangs of all the upper layers
|
# to define interface regions of this layer we consider the overhangs of all the upper layers
|
||||||
# minus the first one
|
# minus the first one
|
||||||
@ -786,8 +788,10 @@ sub generate_support_material {
|
|||||||
(map @$_, @{ $layers_contact_areas{$i} }),
|
(map @$_, @{ $layers_contact_areas{$i} }),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$layers_interfaces{$i} = collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width);
|
$layers_interfaces{$i} = [
|
||||||
$_->simplify($flow->scaled_spacing) for @{$layers_interfaces{$i}};
|
map $_->simplify($flow->scaled_spacing),
|
||||||
|
collapse_ex([ map @$_, @{$layers_interfaces{$i}} ], $flow->scaled_width),
|
||||||
|
];
|
||||||
|
|
||||||
# generate support material in current layer (for upper layers)
|
# generate support material in current layer (for upper layers)
|
||||||
@current_support_regions = @{diff_ex(
|
@current_support_regions = @{diff_ex(
|
||||||
@ -806,8 +810,10 @@ sub generate_support_material {
|
|||||||
(map @$_, @{ $layers_interfaces{$i} }),
|
(map @$_, @{ $layers_interfaces{$i} }),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
$layers{$i} = collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width);
|
$layers{$i} = [
|
||||||
$_->simplify($flow->scaled_spacing) for @{$layers{$i}};
|
map $_->simplify($flow->scaled_spacing),
|
||||||
|
collapse_ex([ map @$_, @{$layers{$i}} ], $flow->scaled_width),
|
||||||
|
];
|
||||||
|
|
||||||
# get layer overhangs and put them into queue for adding support inside lower layers;
|
# get layer overhangs and put them into queue for adding support inside lower layers;
|
||||||
# we need an angle threshold for this
|
# we need an angle threshold for this
|
||||||
|
@ -81,12 +81,22 @@ sub group {
|
|||||||
|
|
||||||
sub offset {
|
sub offset {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return map {
|
return map $self->_inflate_expolygon($_), $self->expolygon->offset_ex(@_);
|
||||||
(ref $self)->new(
|
}
|
||||||
expolygon => $_,
|
|
||||||
|
sub simplify {
|
||||||
|
my $self = shift;
|
||||||
|
return map $self->_inflate_expolygon($_), $self->expolygon->simplify(@_);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _inflate_expolygon {
|
||||||
|
my $self = shift;
|
||||||
|
my ($expolygon) = @_;
|
||||||
|
|
||||||
|
return (ref $self)->new(
|
||||||
|
expolygon => $expolygon,
|
||||||
map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle),
|
map { $_ => $self->$_ } qw(surface_type depth_layers bridge_angle),
|
||||||
)
|
);
|
||||||
} $self->expolygon->offset_ex(@_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub clipper_polygon {
|
sub clipper_polygon {
|
||||||
|
@ -2,7 +2,7 @@ use Test::More;
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
plan tests => 6;
|
plan tests => 7;
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
use FindBin;
|
use FindBin;
|
||||||
@ -24,7 +24,7 @@ use Slic3r;
|
|||||||
my $polyline = Slic3r::Polyline->new([
|
my $polyline = Slic3r::Polyline->new([
|
||||||
[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0],
|
[0,0],[1,0],[2,0],[2,1],[2,2],[1,2],[0,2],[0,1],[0,0],
|
||||||
]);
|
]);
|
||||||
$polyline->simplify(1);
|
$polyline = $polyline->simplify(1);
|
||||||
is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker';
|
is_deeply $polyline, [ [0, 0], [2, 0], [2, 2], [0, 2], [0, 0] ], 'Douglas-Peucker';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ use Slic3r;
|
|||||||
[0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5],
|
[0,0],[0.5,0.5],[1,0],[1.25,-0.25],[1.5,.5],
|
||||||
]);
|
]);
|
||||||
$polyline->scale(100);
|
$polyline->scale(100);
|
||||||
$polyline->simplify(25);
|
$polyline = $polyline->simplify(25);
|
||||||
is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker';
|
is_deeply $polyline, [ [0, 0], [50, 50], [125, -25], [150, 50] ], 'Douglas-Peucker';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +75,10 @@ use Slic3r;
|
|||||||
ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines';
|
ok @$polygon < @$gear, 'gear was simplified using merge_continuous_lines';
|
||||||
|
|
||||||
my $num_points = scalar @$polygon;
|
my $num_points = scalar @$polygon;
|
||||||
$polygon->simplify;
|
my @simplified = $polygon->simplify;
|
||||||
note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@$polygon);
|
ok @simplified == 1, 'gear simplified to a single polygon';
|
||||||
ok @$polygon < $num_points, 'gear was further simplified using Douglas-Peucker';
|
note sprintf "original points: %d\nnew points: %d", $num_points, scalar(@{$simplified[0]});
|
||||||
|
ok @{$simplified[0]} < $num_points, 'gear was further simplified using Douglas-Peucker';
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -43,8 +43,8 @@ use Slic3r::Test;
|
|||||||
$_->slice(keep_meshes => 1) for @{$self->objects};
|
$_->slice(keep_meshes => 1) for @{$self->objects};
|
||||||
$_->make_perimeters for @{$self->objects};
|
$_->make_perimeters for @{$self->objects};
|
||||||
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
|
foreach my $layer (map @{$_->layers}, @{$self->objects}) {
|
||||||
$_->simplify(&Slic3r::SCALED_RESOLUTION)
|
@$_ = map $_->simplify(&Slic3r::SCALED_RESOLUTION), @$_
|
||||||
for @{$layer->slices}, (map $_->expolygon, map @{$_->slices}, @{$layer->regions});
|
for $layer->slices, (map $_->slices, @{$layer->regions});
|
||||||
}
|
}
|
||||||
$_->detect_surfaces_type for @{$self->objects};
|
$_->detect_surfaces_type for @{$self->objects};
|
||||||
$_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects};
|
$_->prepare_fill_surfaces for map @{$_->regions}, map @{$_->layers}, @{$self->objects};
|
||||||
|
Loading…
Reference in New Issue
Block a user