Skip to content

Commit

Permalink
Rewritten the medial axis algorithm, now more robust (don't just prun…
Browse files Browse the repository at this point in the history
…e MAT from endpoints, but validate all single edges)
  • Loading branch information
alranel committed Mar 26, 2016
1 parent e4147a6 commit 7041bb5
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 212 deletions.
9 changes: 4 additions & 5 deletions t/thin.t
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ if (0) {
[160, 140],
);
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
my $res = $expolygon->medial_axis(scale 1, scale 0.5);
my $res = $expolygon->medial_axis(scale 40, scale 0.5);
is scalar(@$res), 1, 'medial axis of a square shape is a single path';
isa_ok $res->[0], 'Slic3r::Polyline', 'medial axis result is a polyline';
ok $res->[0]->first_point->coincides_with($res->[0]->last_point), 'polyline forms a closed loop';
Expand All @@ -76,7 +76,7 @@ if (0) {
[120, 200],
[100, 200],
));
my $res = $expolygon->medial_axis(scale 1, scale 0.5);
my $res = $expolygon->medial_axis(scale 20, scale 0.5);
is scalar(@$res), 1, 'medial axis of a narrow rectangle is a single line';
ok unscale($res->[0]->length) >= (200-100 - (120-100)) - epsilon, 'medial axis has reasonable length';

Expand All @@ -101,7 +101,7 @@ if (0) {
[112, 200],
[108, 200],
));
my $res = $expolygon->medial_axis(scale 1, scale 0.5);
my $res = $expolygon->medial_axis(scale 20, scale 0.5);
is scalar(@$res), 1, 'medial axis of a narrow trapezoid is a single line';
ok unscale($res->[0]->length) >= (200-100 - (120-100)) - epsilon, 'medial axis has reasonable length';
}
Expand All @@ -115,7 +115,7 @@ if (0) {
[200, 200],
[100, 200],
));
my $res = $expolygon->medial_axis(scale 1, scale 0.5);
my $res = $expolygon->medial_axis(scale 20, scale 0.5);
is scalar(@$res), 1, 'medial axis of a L shape is a single polyline';
my $len = unscale($res->[0]->length) + 20; # 20 is the thickness of the expolygon, which is subtracted from the ends
ok $len > 80*2 && $len < 100*2, 'medial axis has reasonable length';
Expand Down Expand Up @@ -150,7 +150,6 @@ if (0) {
[91294454,31032190],[11294481,31032190],[11294481,29967810],[44969182,29967810],[89909960,29967808],[91294454,29967808]
));
my $polylines = $expolygon->medial_axis(1871238, 500000);

is scalar(@$polylines), 1, 'medial axis is a single polyline';
my $polyline = $polylines->[0];

Expand Down
50 changes: 46 additions & 4 deletions xs/src/libslic3r/ExPolygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "polypartition.h"
#include "poly2tri/poly2tri.h"
#include <algorithm>
#include <cassert>
#include <list>

namespace Slic3r {
Expand Down Expand Up @@ -194,6 +195,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
svg.Close();
*/

bool removed = false;
for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i];

Expand All @@ -203,7 +205,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
call, so we keep the inner point until we perform the second intersection() as well */
Point new_front = polyline.points.front();
Point new_back = polyline.points.back();
if (polyline.endpoints.front() && !this->has_boundary_point(new_front)) {
if (polyline.endpoints.first && !this->has_boundary_point(new_front)) {
Line line(polyline.points.front(), polyline.points[1]);

// prevent the line from touching on the other side, otherwise intersection() might return that solution
Expand All @@ -212,7 +214,7 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
line.extend_start(max_width);
(void)this->contour.intersection(line, &new_front);
}
if (polyline.endpoints.back() && !this->has_boundary_point(new_back)) {
if (polyline.endpoints.second && !this->has_boundary_point(new_back)) {
Line line(
*(polyline.points.end() - 2),
polyline.points.back()
Expand All @@ -230,14 +232,54 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
/* remove too short polylines
(we can't do this check before endpoints extension and clipping because we don't
know how long will the endpoints be extended since it depends on polygon thickness
which is variable - extension will be <= max_width/2 on each side) */
if (polyline.length() < max_width) {
which is variable - extension will be <= max_width/2 on each side)
Maybe instead of using max_width we should use max_element(polyline.width) */
if ((polyline.endpoints.first || polyline.endpoints.second)
&& polyline.length() < max_width*2) {
pp.erase(pp.begin() + i);
--i;
removed = true;
continue;
}
}

/* If we removed any short polylines we now try to connect consecutive polylines
in order to allow loop detection. Note that this algorithm is greedier than
MedialAxis::process_edge_neighbors() as it will connect random pairs of
polylines even when more than two start from the same point. This has no
drawbacks since we optimize later using nearest-neighbor which would do the
same, but should we use a more sophisticated optimization algorithm we should
not connect polylines when more than two meet. */
if (removed) {
for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i];
if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization

// find another polyline starting here
for (size_t j = i+1; j < pp.size(); ++j) {
ThickPolyline& other = pp[j];
if (polyline.last_point().coincides_with(other.last_point())) {
other.reverse();
} else if (polyline.first_point().coincides_with(other.last_point())) {
polyline.reverse();
other.reverse();
} else if (polyline.first_point().coincides_with(other.first_point())) {
polyline.reverse();
} else if (!polyline.last_point().coincides_with(other.first_point())) {
continue;
}

polyline.points.insert(polyline.points.end(), other.points.begin() + 1, other.points.end());
polyline.width.insert(polyline.width.end(), other.width.begin(), other.width.end());
polyline.endpoints.second = other.endpoints.second;
assert(polyline.width.size() == polyline.points.size()*2 - 2);

pp.erase(pp.begin() + j);
j = i; // restart search from i+1
}
}
}

polylines->insert(polylines->end(), pp.begin(), pp.end());
}

Expand Down
Loading

0 comments on commit 7041bb5

Please sign in to comment.