mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-08-06 21:44:08 -06:00
Finished implementing Boost.Polygon medial axis. Some cleanup needed
This commit is contained in:
parent
07a4c37c4c
commit
7b0decbeb1
7 changed files with 117 additions and 194 deletions
|
@ -5,7 +5,6 @@ use warnings;
|
|||
# an ExPolygon is a polygon with holes
|
||||
|
||||
use List::Util qw(first);
|
||||
use Math::Geometry::Voronoi;
|
||||
use Slic3r::Geometry qw(X Y A B point_in_polygon epsilon scaled_epsilon);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex diff_pl);
|
||||
|
||||
|
@ -43,169 +42,6 @@ sub bounding_box {
|
|||
return $self->contour->bounding_box;
|
||||
}
|
||||
|
||||
# this method only works for expolygons having only a contour or
|
||||
# a contour and a hole, and not being thicker than the supplied
|
||||
# width. it returns a polyline or a polygon
|
||||
sub ___medial_axis {
|
||||
my ($self, $width) = @_;
|
||||
return $self->_medial_axis_voronoi($width);
|
||||
}
|
||||
|
||||
sub _medial_axis_clip {
|
||||
my ($self, $width) = @_;
|
||||
|
||||
my $grow = sub {
|
||||
my ($line, $distance) = @_;
|
||||
|
||||
my $line_clone = $line->clone;
|
||||
$line_clone->clip_start(scaled_epsilon);
|
||||
return () if !$line_clone->is_valid;
|
||||
$line_clone->clip_end(scaled_epsilon);
|
||||
return () if !$line_clone->is_valid;
|
||||
|
||||
my ($a, $b) = @$line_clone;
|
||||
my $dx = $a->x - $b->x;
|
||||
my $dy = $a->y - $b->y; #-
|
||||
my $dist = sqrt($dx*$dx + $dy*$dy);
|
||||
$dx /= $dist;
|
||||
$dy /= $dist;
|
||||
return Slic3r::Polygon->new(
|
||||
Slic3r::Point->new($a->x + $distance*$dy, $a->y - $distance*$dx), #--
|
||||
Slic3r::Point->new($b->x + $distance*$dy, $b->y - $distance*$dx), #--
|
||||
Slic3r::Point->new($b->x - $distance*$dy, $b->y + $distance*$dx), #++
|
||||
Slic3r::Point->new($a->x - $distance*$dy, $a->y + $distance*$dx), #++
|
||||
);
|
||||
};
|
||||
|
||||
my @result = ();
|
||||
my $covered = [];
|
||||
foreach my $polygon (@$self) {
|
||||
my @polylines = ();
|
||||
foreach my $line (@{$polygon->lines}) {
|
||||
# remove the areas that are already covered from this line
|
||||
my $clipped = diff_pl([$line->as_polyline], $covered);
|
||||
|
||||
# skip very short segments/dots
|
||||
@$clipped = grep $_->length > $width/10, @$clipped;
|
||||
|
||||
# grow the remaining lines and add them to the covered areas
|
||||
push @$covered, map $grow->($_, $width*1.1), @$clipped;
|
||||
|
||||
# if the first remaining segment is connected to the last polyline, append it
|
||||
# to that -- FIXME: this assumes that diff_pl()
|
||||
# preserved the orientation of the input linestring but this is not generally true
|
||||
if (@polylines && @$clipped && $clipped->[0]->first_point->distance_to($polylines[-1]->last_point) <= $width/10) {
|
||||
$polylines[-1]->append_polyline(shift @$clipped);
|
||||
}
|
||||
push @polylines, @$clipped;
|
||||
}
|
||||
|
||||
foreach my $polyline (@polylines) {
|
||||
# if this polyline looks like a closed loop, return it as a polygon
|
||||
if ($polyline->first_point->coincides_with($polyline->last_point)) {
|
||||
next if @$polyline == 2;
|
||||
$polyline->pop_back;
|
||||
push @result, Slic3r::Polygon->new(@$polyline);
|
||||
} else {
|
||||
push @result, $polyline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return @result;
|
||||
}
|
||||
|
||||
my $voronoi_lock :shared;
|
||||
|
||||
sub _medial_axis_voronoi {
|
||||
my ($self, $width) = @_;
|
||||
|
||||
lock($voronoi_lock);
|
||||
|
||||
my $voronoi;
|
||||
{
|
||||
my @points = ();
|
||||
foreach my $polygon (@$self) {
|
||||
{
|
||||
my $p = $polygon->pp;
|
||||
Slic3r::Geometry::polyline_remove_short_segments($p, $width / 2);
|
||||
$polygon = Slic3r::Polygon->new(@$p);
|
||||
}
|
||||
|
||||
# subdivide polygon segments so that we don't have anyone of them
|
||||
# being longer than $width / 2
|
||||
$polygon = $polygon->subdivide($width/2);
|
||||
|
||||
push @points, @{$polygon->pp};
|
||||
}
|
||||
$voronoi = Math::Geometry::Voronoi->new(points => \@points);
|
||||
}
|
||||
|
||||
$voronoi->compute;
|
||||
my $vertices = $voronoi->vertices;
|
||||
|
||||
my @skeleton_lines = ();
|
||||
foreach my $edge (@{ $voronoi->edges }) {
|
||||
# ignore lines going to infinite
|
||||
next if $edge->[1] == -1 || $edge->[2] == -1;
|
||||
|
||||
my $line = Slic3r::Line->new($vertices->[$edge->[1]], $vertices->[$edge->[2]]);
|
||||
next if !$self->contains_line($line);
|
||||
|
||||
# contains_point() could be faster, but we need an implementation that
|
||||
# reliably considers points on boundary
|
||||
#next if !$self->contains_point(Slic3r::Point->new(@{$vertices->[$edge->[1]]}))
|
||||
# || !$self->contains_point(Slic3r::Point->new(@{$vertices->[$edge->[2]]}));
|
||||
|
||||
push @skeleton_lines, [$edge->[1], $edge->[2]];
|
||||
}
|
||||
return () if !@skeleton_lines;
|
||||
|
||||
# now walk along the medial axis and build continuos polylines or polygons
|
||||
my @polylines = ();
|
||||
{
|
||||
my @lines = @skeleton_lines;
|
||||
push @polylines, [ map @$_, shift @lines ];
|
||||
CYCLE: while (@lines) {
|
||||
for my $i (0..$#lines) {
|
||||
if ($lines[$i][0] == $polylines[-1][-1]) {
|
||||
push @{$polylines[-1]}, $lines[$i][1];
|
||||
} elsif ($lines[$i][1] == $polylines[-1][-1]) {
|
||||
push @{$polylines[-1]}, $lines[$i][0];
|
||||
} elsif ($lines[$i][1] == $polylines[-1][0]) {
|
||||
unshift @{$polylines[-1]}, $lines[$i][0];
|
||||
} elsif ($lines[$i][0] == $polylines[-1][0]) {
|
||||
unshift @{$polylines[-1]}, $lines[$i][1];
|
||||
} else {
|
||||
next;
|
||||
}
|
||||
splice @lines, $i, 1;
|
||||
next CYCLE;
|
||||
}
|
||||
push @polylines, [ map @$_, shift @lines ];
|
||||
}
|
||||
}
|
||||
|
||||
my @result = ();
|
||||
my $simplify_tolerance = $width / 7;
|
||||
foreach my $polyline (@polylines) {
|
||||
next unless @$polyline >= 2;
|
||||
|
||||
# now replace point indexes with coordinates
|
||||
my @points = map Slic3r::Point->new(@{$vertices->[$_]}), @$polyline;
|
||||
|
||||
if ($points[0]->coincides_with($points[-1])) {
|
||||
next if @points == 2;
|
||||
push @result, @{Slic3r::Polygon->new(@points[0..$#points-1])->simplify($simplify_tolerance)};
|
||||
} else {
|
||||
push @result, Slic3r::Polyline->new(@points);
|
||||
$result[-1]->simplify($simplify_tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
return @result;
|
||||
}
|
||||
|
||||
package Slic3r::ExPolygon::Collection;
|
||||
use Slic3r::Geometry qw(X1 Y1);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue