mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-12 06:39:06 +08:00
thin_wall: improvements & bugfixes
This commit is contained in:
parent
85e42194c4
commit
9b330f4631
15
t/thin.t
15
t/thin.t
@ -1,4 +1,4 @@
|
|||||||
use Test::More tests => 28;
|
use Test::More tests => 29;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -102,7 +102,9 @@ if (0) {
|
|||||||
is scalar(@$res), 1, 'medial axis of a semicircumference is a single line';
|
is scalar(@$res), 1, 'medial axis of a semicircumference is a single line';
|
||||||
|
|
||||||
# check whether turns are all CCW or all CW
|
# check whether turns are all CCW or all CW
|
||||||
my @lines = @{$res->[0]->lines};
|
my @alllines = @{$res->[0]->lines};
|
||||||
|
# remove lines taht are near the end.
|
||||||
|
my @lines = grep($_->a->y >= 1578184 || $_->b->y >= 1578184, @alllines);
|
||||||
my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines;
|
my @angles = map { $lines[$_-1]->ccw($lines[$_]->b) } 1..$#lines;
|
||||||
ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles),
|
ok !!(none { $_ < 0 } @angles) || (none { $_ > 0 } @angles),
|
||||||
'all medial axis segments of a semicircumference have the same orientation';
|
'all medial axis segments of a semicircumference have the same orientation';
|
||||||
@ -113,16 +115,25 @@ if (0) {
|
|||||||
[4.3, 4], [4.3, 0], [4,0], [4,4], [0,4], [0,4.5], [4,4.5], [4,10], [4.3,10], [4.3, 4.5],
|
[4.3, 4], [4.3, 0], [4,0], [4,4], [0,4], [0,4.5], [4,4.5], [4,10], [4.3,10], [4.3, 4.5],
|
||||||
[6, 4.5], [6,10], [6.2,10], [6.2,4.5], [10,4.5], [10,4], [6.2,4], [6.2,0], [6, 0], [6, 4],
|
[6, 4.5], [6,10], [6.2,10], [6.2,4.5], [10,4.5], [10,4], [6.2,4], [6.2,0], [6, 0], [6, 4],
|
||||||
));
|
));
|
||||||
|
$expolygon->contour->make_counter_clockwise();
|
||||||
my $res = $expolygon->medial_axis(scale 0.55, scale 0.25);
|
my $res = $expolygon->medial_axis(scale 0.55, scale 0.25);
|
||||||
is scalar(@$res), 2, 'medial axis of a (bit too narrow) french cross is two lines';
|
is scalar(@$res), 2, 'medial axis of a (bit too narrow) french cross is two lines';
|
||||||
ok unscale($res->[0]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
ok unscale($res->[0]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
||||||
ok unscale($res->[1]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
ok unscale($res->[1]->length) >= (9.9) - epsilon, 'medial axis has reasonable length';
|
||||||
|
my @lines1 = @{$res->[0]->lines};
|
||||||
|
my @angles1 = map { $lines1[$_-1]->ccw($lines1[$_]->b) } 1..$#lines1;
|
||||||
|
my @lines2 = @{$res->[1]->lines};
|
||||||
|
my @angles2 = map { $lines2[$_-1]->ccw($lines2[$_]->b) } 1..$#lines2;
|
||||||
|
my @angles = (@angles1, @angles2);
|
||||||
|
ok !!(none { $_ != 0 } @angles),
|
||||||
|
'medial axis of a (bit too narrow) french cross is two lines has only strait lines';
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale(
|
my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_scale(
|
||||||
[0.86526705,1.4509841], [0.57696039,1.8637021], [0.4502297,2.5569978], [0.45626199,3.2965596], [1.1218851,3.3049455], [0.96681072,2.8243202], [0.86328971,2.2056997], [0.85367905,1.7790778],
|
[0.86526705,1.4509841], [0.57696039,1.8637021], [0.4502297,2.5569978], [0.45626199,3.2965596], [1.1218851,3.3049455], [0.96681072,2.8243202], [0.86328971,2.2056997], [0.85367905,1.7790778],
|
||||||
));
|
));
|
||||||
|
$expolygon->contour->make_counter_clockwise();
|
||||||
my $res = $expolygon->medial_axis(scale 1, scale 0.25);
|
my $res = $expolygon->medial_axis(scale 1, scale 0.25);
|
||||||
is scalar(@$res), 1, 'medial axis of a (bit too narrow) french cross is two lines';
|
is scalar(@$res), 1, 'medial axis of a (bit too narrow) french cross is two lines';
|
||||||
ok unscale($res->[0]->length) >= (1.4) - epsilon, 'medial axis has reasonable length';
|
ok unscale($res->[0]->length) >= (1.4) - epsilon, 'medial axis has reasonable length';
|
||||||
|
@ -237,11 +237,7 @@ ExPolygon::remove_point_too_near(const coord_t tolerance) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const {
|
ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const {
|
||||||
ExPolygon simplifiedBounds = bounds;
|
Slic3r::MedialAxis ma(*this, bounds, max_width, min_width, height);
|
||||||
simplifiedBounds.remove_point_too_near(SCALED_RESOLUTION);
|
|
||||||
ExPolygon simplifiedPolygon = *this;
|
|
||||||
simplifiedPolygon.remove_point_too_near(SCALED_RESOLUTION);
|
|
||||||
Slic3r::MedialAxis ma(simplifiedPolygon, simplifiedBounds, max_width, min_width, height);
|
|
||||||
ma.build(polylines);
|
ma.build(polylines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,21 +359,21 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify)
|
|||||||
/// return 1 for an angle of 90° and 0 for an angle of 0° or 180°
|
/// return 1 for an angle of 90° and 0 for an angle of 0° or 180°
|
||||||
double
|
double
|
||||||
get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) {
|
get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t min_dist_between_point) {
|
||||||
double nearestDist = point.distance_to(contour.contour.points.front());
|
double nearest_dist = point.distance_to(contour.contour.points.front());
|
||||||
Point nearest = contour.contour.points.front();
|
Point point_nearest = contour.contour.points.front();
|
||||||
size_t id_nearest = 0;
|
size_t id_nearest = 0;
|
||||||
double nearDist = nearestDist;
|
double near_dist = nearest_dist;
|
||||||
Point near = nearest;
|
Point point_near = point_nearest;
|
||||||
size_t id_near = 0;
|
size_t id_near = 0;
|
||||||
for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) {
|
for (size_t id_point = 1; id_point < contour.contour.points.size(); ++id_point) {
|
||||||
if (nearestDist > point.distance_to(contour.contour.points[id_point])) {
|
if (nearest_dist > point.distance_to(contour.contour.points[id_point])) {
|
||||||
//update near
|
//update point_near
|
||||||
id_near = id_nearest;
|
id_near = id_nearest;
|
||||||
near = nearest;
|
point_near = point_nearest;
|
||||||
nearDist = nearestDist;
|
near_dist = nearest_dist;
|
||||||
//update nearest
|
//update nearest
|
||||||
nearestDist = point.distance_to(contour.contour.points[id_point]);
|
nearest_dist = point.distance_to(contour.contour.points[id_point]);
|
||||||
nearest = contour.contour.points[id_point];
|
point_nearest = contour.contour.points[id_point];
|
||||||
id_nearest = id_point;
|
id_nearest = id_point;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
|||||||
size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1;
|
size_t id_before = id_nearest == 0 ? contour.contour.points.size() - 1 : id_nearest - 1;
|
||||||
Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1];
|
Point point_before = id_nearest == 0 ? contour.contour.points.back() : contour.contour.points[id_nearest - 1];
|
||||||
//Search one point far enough to be relevant
|
//Search one point far enough to be relevant
|
||||||
while (nearest.distance_to(point_before) < min_dist_between_point) {
|
while (point_nearest.distance_to(point_before) < min_dist_between_point) {
|
||||||
point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1];
|
point_before = id_before == 0 ? contour.contour.points.back() : contour.contour.points[id_before - 1];
|
||||||
id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1;
|
id_before = id_before == 0 ? contour.contour.points.size() - 1 : id_before - 1;
|
||||||
//don't loop
|
//don't loop
|
||||||
@ -394,7 +394,7 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
|||||||
size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1;
|
size_t id_after = id_nearest == contour.contour.points.size() - 1 ? 0 : id_nearest + 1;
|
||||||
Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1];
|
Point point_after = id_nearest == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_nearest + 1];
|
||||||
//Search one point far enough to be relevant
|
//Search one point far enough to be relevant
|
||||||
while (nearest.distance_to(point_after) < min_dist_between_point) {
|
while (point_nearest.distance_to(point_after) < min_dist_between_point) {
|
||||||
point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1];
|
point_after = id_after == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_after + 1];
|
||||||
id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1;
|
id_after = id_after == contour.contour.points.size() - 1 ? 0 : id_after + 1;
|
||||||
//don't loop
|
//don't loop
|
||||||
@ -405,15 +405,15 @@ get_coeff_from_angle_countour(Point &point, const ExPolygon &contour, coord_t mi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//compute angle
|
//compute angle
|
||||||
angle = nearest.ccw_angle(point_before, point_after);
|
angle = point_nearest.ccw_angle(point_before, point_after);
|
||||||
if (angle >= PI) angle = 2 * PI - angle; // smaller angle
|
if (angle >= PI) angle = 2 * PI - angle; // smaller angle
|
||||||
//compute the diff from 90°
|
//compute the diff from 90°
|
||||||
angle = abs(angle - PI / 2);
|
angle = abs(angle - PI / 2);
|
||||||
if (near.coincides_with(nearest) && max(nearestDist, nearDist) + SCALED_EPSILON < nearest.distance_to(near)) {
|
if (point_near.coincides_with(point_nearest) && max(nearest_dist, near_dist) + SCALED_EPSILON < point_nearest.distance_to(point_near)) {
|
||||||
//not only nearest
|
//not only nearest
|
||||||
Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1];
|
Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1];
|
||||||
Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1];
|
Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1];
|
||||||
double angle2 = min(nearest.ccw_angle(point_before, point_after), nearest.ccw_angle(point_after, point_before));
|
double angle2 = min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before));
|
||||||
angle2 = abs(angle - PI / 2);
|
angle2 = abs(angle - PI / 2);
|
||||||
angle = (angle + angle2) / 2;
|
angle = (angle + angle2) / 2;
|
||||||
}
|
}
|
||||||
@ -613,8 +613,12 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
|
|||||||
// polyline, after we extend the start point it will be caught by the intersection()
|
// polyline, after we extend the start point it will be caught by the intersection()
|
||||||
// call, so we keep the inner point until we perform the second intersection() as well
|
// call, so we keep the inner point until we perform the second intersection() as well
|
||||||
if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) {
|
if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) {
|
||||||
Line line(*(polyline.points.end() - 2), polyline.points.back());
|
size_t first_idx = polyline.points.size() - 2;
|
||||||
|
Line line(*(polyline.points.begin() + first_idx), polyline.points.back());
|
||||||
|
while (line.length() < SCALED_RESOLUTION && first_idx>0) {
|
||||||
|
first_idx--;
|
||||||
|
line.a = *(polyline.points.begin() + first_idx);
|
||||||
|
}
|
||||||
// prevent the line from touching on the other side, otherwise intersection() might return that solution
|
// prevent the line from touching on the other side, otherwise intersection() might return that solution
|
||||||
if (polyline.points.size() == 2) line.a = line.midpoint();
|
if (polyline.points.size() == 2) line.a = line.midpoint();
|
||||||
|
|
||||||
@ -623,16 +627,39 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
|
|||||||
if (this->expolygon.contour.has_boundary_point(polyline.points.back())) {
|
if (this->expolygon.contour.has_boundary_point(polyline.points.back())) {
|
||||||
new_back = polyline.points.back();
|
new_back = polyline.points.back();
|
||||||
} else {
|
} else {
|
||||||
|
//TODO: verify also for holes.
|
||||||
(void)this->expolygon.contour.first_intersection(line, &new_back);
|
(void)this->expolygon.contour.first_intersection(line, &new_back);
|
||||||
// safety check if no intersection
|
// safety check if no intersection
|
||||||
if (new_back.x == 0 && new_back.y == 0) return;
|
if (new_back.x == 0 && new_back.y == 0) {
|
||||||
|
if (!this->expolygon.contains(line.b)) {
|
||||||
|
//it's outside!!!
|
||||||
|
std::cout << "Error, a line is formed that start in a polygon, end outside of it can don't cross it!\n";
|
||||||
|
}
|
||||||
|
new_back = line.b;
|
||||||
|
}
|
||||||
polyline.points.push_back(new_back);
|
polyline.points.push_back(new_back);
|
||||||
polyline.width.push_back(polyline.width.back());
|
polyline.width.push_back(polyline.width.back());
|
||||||
}
|
}
|
||||||
Point new_bound;
|
Point new_bound;
|
||||||
|
//TODO: verify also for holes.
|
||||||
(void)bounds.contour.first_intersection(line, &new_bound);
|
(void)bounds.contour.first_intersection(line, &new_bound);
|
||||||
// safety check if no intersection
|
// safety check if no intersection
|
||||||
if (new_bound.x == 0 && new_bound.y == 0) return;
|
if (new_bound.x == 0 && new_bound.y == 0) {
|
||||||
|
if (line.b.coincides_with_epsilon(polyline.points.back())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//check if we don't over-shoot inside us
|
||||||
|
bool is_in_anchor = false;
|
||||||
|
for (const ExPolygon& a : anchors) {
|
||||||
|
if (a.contains(line.b)) {
|
||||||
|
is_in_anchor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_in_anchor) std::cout << "not in anchor:\n";
|
||||||
|
if (!is_in_anchor) return;
|
||||||
|
new_bound = line.b;
|
||||||
|
}
|
||||||
/* if (new_bound.coincides_with_epsilon(new_back)) {
|
/* if (new_bound.coincides_with_epsilon(new_back)) {
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
@ -765,7 +792,7 @@ MedialAxis::main_fusion(ThickPolylines& pp)
|
|||||||
coord_t biggest_main_branch_length = 0;
|
coord_t biggest_main_branch_length = 0;
|
||||||
for (size_t k = 0; k < pp.size(); ++k) {
|
for (size_t k = 0; k < pp.size(); ++k) {
|
||||||
//std::cout << "try to find main : " << k << " ? " << i << " " << j << " ";
|
//std::cout << "try to find main : " << k << " ? " << i << " " << j << " ";
|
||||||
if (k == i | k == j) continue;
|
if (k == i || k == j) continue;
|
||||||
ThickPolyline& main = pp[k];
|
ThickPolyline& main = pp[k];
|
||||||
if (polyline.first_point().coincides_with(main.last_point())) {
|
if (polyline.first_point().coincides_with(main.last_point())) {
|
||||||
main.reverse();
|
main.reverse();
|
||||||
@ -993,19 +1020,22 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
|
|||||||
while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
|
while (polyline.points.size() > 1 && polyline.width.front() < this->min_width && polyline.endpoints.first) {
|
||||||
//try to split if possible
|
//try to split if possible
|
||||||
if (polyline.width[1] > min_width) {
|
if (polyline.width[1] > min_width) {
|
||||||
double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
|
double percent_can_keep = 1 - (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
|
||||||
if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > this->max_width / 2
|
if (polyline.points.front().distance_to(polyline.points[1]) * percent_can_keep > SCALED_RESOLUTION) {
|
||||||
&& polyline.points.front().distance_to(polyline.points[1])* (1 - percent_can_keep) > this->max_width / 2) {
|
|
||||||
//Can split => move the first point and assign a new weight.
|
//Can split => move the first point and assign a new weight.
|
||||||
//the update of endpoints wil be performed in concatThickPolylines
|
//the update of endpoints wil be performed in concatThickPolylines
|
||||||
polyline.points.front().x = polyline.points.front().x +
|
polyline.points.front().x = polyline.points.front().x +
|
||||||
(coord_t)((polyline.points[1].x - polyline.points.front().x) * percent_can_keep);
|
(coord_t)((polyline.points[1].x - polyline.points.front().x) * (1 - percent_can_keep));
|
||||||
polyline.points.front().y = polyline.points.front().y +
|
polyline.points.front().y = polyline.points.front().y +
|
||||||
(coord_t)((polyline.points[1].y - polyline.points.front().y) * percent_can_keep);
|
(coord_t)((polyline.points[1].y - polyline.points.front().y) * (1 - percent_can_keep));
|
||||||
polyline.width.front() = min_width;
|
polyline.width.front() = min_width;
|
||||||
changes = true;
|
} else {
|
||||||
break;
|
/// almost 0-length, Remove
|
||||||
|
polyline.points.erase(polyline.points.begin());
|
||||||
|
polyline.width.erase(polyline.width.begin());
|
||||||
}
|
}
|
||||||
|
changes = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
polyline.points.erase(polyline.points.begin());
|
polyline.points.erase(polyline.points.begin());
|
||||||
polyline.width.erase(polyline.width.begin());
|
polyline.width.erase(polyline.width.begin());
|
||||||
@ -1014,25 +1044,29 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
|
|||||||
while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
|
while (polyline.points.size() > 1 && polyline.width.back() < this->min_width && polyline.endpoints.second) {
|
||||||
//try to split if possible
|
//try to split if possible
|
||||||
if (polyline.width[polyline.points.size() - 2] > min_width) {
|
if (polyline.width[polyline.points.size() - 2] > min_width) {
|
||||||
double percent_can_keep = (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
|
double percent_can_keep = 1 - (min_width - polyline.width.back()) / (polyline.width[polyline.points.size() - 2] - polyline.width.back());
|
||||||
if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > this->max_width / 2
|
if (polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * percent_can_keep > SCALED_RESOLUTION) {
|
||||||
&& polyline.points.back().distance_to(polyline.points[polyline.points.size() - 2]) * (1 - percent_can_keep) > this->max_width / 2) {
|
|
||||||
//Can split => move the first point and assign a new weight.
|
//Can split => move the first point and assign a new weight.
|
||||||
//the update of endpoints wil be performed in concatThickPolylines
|
//the update of endpoints wil be performed in concatThickPolylines
|
||||||
polyline.points.back().x = polyline.points.back().x +
|
polyline.points.back().x = polyline.points.back().x +
|
||||||
(coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * percent_can_keep);
|
(coord_t)((polyline.points[polyline.points.size() - 2].x - polyline.points.back().x) * (1 - percent_can_keep));
|
||||||
polyline.points.back().y = polyline.points.back().y +
|
polyline.points.back().y = polyline.points.back().y +
|
||||||
(coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * percent_can_keep);
|
(coord_t)((polyline.points[polyline.points.size() - 2].y - polyline.points.back().y) * (1 - percent_can_keep));
|
||||||
polyline.width.back() = min_width;
|
polyline.width.back() = min_width;
|
||||||
changes = true;
|
} else {
|
||||||
break;
|
/// almost 0-length, Remove
|
||||||
|
polyline.points.erase(polyline.points.end() - 1);
|
||||||
|
polyline.width.erase(polyline.width.end() - 1);
|
||||||
}
|
}
|
||||||
|
changes = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
polyline.points.erase(polyline.points.end() - 1);
|
polyline.points.erase(polyline.points.end() - 1);
|
||||||
polyline.width.erase(polyline.width.end() - 1);
|
polyline.width.erase(polyline.width.end() - 1);
|
||||||
changes = true;
|
changes = true;
|
||||||
}
|
}
|
||||||
if (polyline.points.size() < 2) {
|
//remove points and bits that comes from a "main line"
|
||||||
|
if (polyline.points.size() < 2 || (changes && polyline.length() < max_width && polyline.points.size() ==2)) {
|
||||||
//remove self if too small
|
//remove self if too small
|
||||||
pp.erase(pp.begin() + i);
|
pp.erase(pp.begin() + i);
|
||||||
--i;
|
--i;
|
||||||
@ -1056,7 +1090,6 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
|||||||
Optimisation of the old algorithm : now we select the most "strait line" choice
|
Optimisation of the old algorithm : now we select the most "strait line" choice
|
||||||
when we merge with an other line at a point with more than two meet.
|
when we merge with an other line at a point with more than two meet.
|
||||||
*/
|
*/
|
||||||
bool changes = false;
|
|
||||||
for (size_t i = 0; i < pp.size(); ++i) {
|
for (size_t i = 0; i < pp.size(); ++i) {
|
||||||
ThickPolyline& polyline = pp[i];
|
ThickPolyline& polyline = pp[i];
|
||||||
if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
|
if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
|
||||||
@ -1066,8 +1099,11 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
|||||||
size_t best_idx = 0;
|
size_t best_idx = 0;
|
||||||
|
|
||||||
// find another polyline starting here
|
// find another polyline starting here
|
||||||
for (size_t j = i + 1; j < pp.size(); ++j) {
|
for (size_t j = 0; j < pp.size(); ++j) {
|
||||||
|
if (j == i) continue;
|
||||||
ThickPolyline& other = pp[j];
|
ThickPolyline& other = pp[j];
|
||||||
|
if (other.endpoints.first && other.endpoints.second) continue;
|
||||||
|
|
||||||
if (polyline.last_point().coincides_with(other.last_point())) {
|
if (polyline.last_point().coincides_with(other.last_point())) {
|
||||||
other.reverse();
|
other.reverse();
|
||||||
} else if (polyline.first_point().coincides_with(other.last_point())) {
|
} else if (polyline.first_point().coincides_with(other.last_point())) {
|
||||||
@ -1091,16 +1127,14 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (best_candidate != nullptr) {
|
if (best_candidate != nullptr) {
|
||||||
|
|
||||||
polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end());
|
polyline.points.insert(polyline.points.end(), best_candidate->points.begin() + 1, best_candidate->points.end());
|
||||||
polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end());
|
polyline.width.insert(polyline.width.end(), best_candidate->width.begin() + 1, best_candidate->width.end());
|
||||||
polyline.endpoints.second = best_candidate->endpoints.second;
|
polyline.endpoints.second = best_candidate->endpoints.second;
|
||||||
assert(polyline.width.size() == polyline.points.size());
|
assert(polyline.width.size() == polyline.points.size());
|
||||||
changes = true;
|
if (best_idx < i) i--;
|
||||||
pp.erase(pp.begin() + best_idx);
|
pp.erase(pp.begin() + best_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changes) concatThickPolylines(pp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -1276,15 +1310,35 @@ void
|
|||||||
MedialAxis::build(ThickPolylines* polylines_out)
|
MedialAxis::build(ThickPolylines* polylines_out)
|
||||||
{
|
{
|
||||||
this->id++;
|
this->id++;
|
||||||
|
//std::cout << layerid << "\n";
|
||||||
|
//{
|
||||||
|
// stringstream stri;
|
||||||
|
// stri << "medial_axis_0_enter_" << id << ".svg";
|
||||||
|
// SVG svg(stri.str());
|
||||||
|
// svg.draw(this->surface);
|
||||||
|
// svg.Close();
|
||||||
|
//}
|
||||||
this->expolygon = simplify_polygon_frontier();
|
this->expolygon = simplify_polygon_frontier();
|
||||||
|
//{
|
||||||
|
// stringstream stri;
|
||||||
|
// stri << "medial_axis_0.5_simplified_" << id << ".svg";
|
||||||
|
// SVG svg(stri.str());
|
||||||
|
// svg.draw(bounds);
|
||||||
|
// svg.draw(this->expolygon);
|
||||||
|
// svg.Close();
|
||||||
|
//}
|
||||||
|
//safety check
|
||||||
|
if (this->expolygon.area() < this->min_width * this->min_width) this->expolygon = this->surface;
|
||||||
|
if (this->expolygon.area() < this->min_width * this->min_width) return;
|
||||||
|
|
||||||
|
//std::cout << "simplify_polygon_frontier\n";
|
||||||
// compute the Voronoi diagram and extract medial axis polylines
|
// compute the Voronoi diagram and extract medial axis polylines
|
||||||
ThickPolylines pp;
|
ThickPolylines pp;
|
||||||
this->polyline_from_voronoi(this->expolygon.lines(), &pp);
|
this->polyline_from_voronoi(this->expolygon.lines(), &pp);
|
||||||
|
|
||||||
|
concatThickPolylines(pp);
|
||||||
|
|
||||||
|
//std::cout << "concatThickPolylines\n";
|
||||||
//{
|
//{
|
||||||
// stringstream stri;
|
// stringstream stri;
|
||||||
// stri << "medial_axis_1_voronoi_" << id << ".svg";
|
// stri << "medial_axis_1_voronoi_" << id << ".svg";
|
||||||
@ -1313,7 +1367,6 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
|||||||
// svg.Close();
|
// svg.Close();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
concatThickPolylines(pp);
|
|
||||||
|
|
||||||
// Aligned fusion: Fusion the bits at the end of lines by "increasing thickness"
|
// Aligned fusion: Fusion the bits at the end of lines by "increasing thickness"
|
||||||
// For that, we have to find other lines,
|
// For that, we have to find other lines,
|
||||||
@ -1334,6 +1387,16 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
|||||||
//fusion right-angle corners.
|
//fusion right-angle corners.
|
||||||
fusion_corners(pp);
|
fusion_corners(pp);
|
||||||
|
|
||||||
|
if (do_not_overextrude) {
|
||||||
|
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION);
|
||||||
|
for (size_t i = 0; i < pp.size(); ++i) {
|
||||||
|
ThickPolyline& polyline = pp[i];
|
||||||
|
extends_line(polyline, anchors, min_width);
|
||||||
|
polyline.reverse();
|
||||||
|
extends_line(polyline, anchors, min_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//reduce extrusion when it's too thin to be printable
|
//reduce extrusion when it's too thin to be printable
|
||||||
remove_too_thin_extrusion(pp);
|
remove_too_thin_extrusion(pp);
|
||||||
//{
|
//{
|
||||||
@ -1359,12 +1422,14 @@ MedialAxis::build(ThickPolylines* polylines_out)
|
|||||||
|
|
||||||
// Loop through all returned polylines in order to extend their endpoints to the
|
// Loop through all returned polylines in order to extend their endpoints to the
|
||||||
// expolygon boundaries
|
// expolygon boundaries
|
||||||
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION);
|
if (!do_not_overextrude) {
|
||||||
for (size_t i = 0; i < pp.size(); ++i) {
|
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), -SCALED_RESOLUTION, SCALED_RESOLUTION);
|
||||||
ThickPolyline& polyline = pp[i];
|
for (size_t i = 0; i < pp.size(); ++i) {
|
||||||
extends_line(polyline, anchors, min_width);
|
ThickPolyline& polyline = pp[i];
|
||||||
polyline.reverse();
|
extends_line(polyline, anchors, min_width);
|
||||||
extends_line(polyline, anchors, min_width);
|
polyline.reverse();
|
||||||
|
extends_line(polyline, anchors, min_width);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//{
|
//{
|
||||||
// stringstream stri;
|
// stringstream stri;
|
||||||
|
@ -22,6 +22,7 @@ namespace Slic3r {
|
|||||||
const double max_width;
|
const double max_width;
|
||||||
const double min_width;
|
const double min_width;
|
||||||
const double height;
|
const double height;
|
||||||
|
bool do_not_overextrude = true;
|
||||||
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const double _max_width, const double _min_width, const double _height)
|
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const double _max_width, const double _min_width, const double _height)
|
||||||
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) {
|
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height) {
|
||||||
};
|
};
|
||||||
|
@ -249,16 +249,18 @@ void PerimeterGenerator::process()
|
|||||||
|
|
||||||
// detect edge case where a curve can be split in multiple small chunks.
|
// detect edge case where a curve can be split in multiple small chunks.
|
||||||
ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2));
|
ExPolygons no_thin_onion = offset_ex(last, -(float)(ext_perimeter_width / 2));
|
||||||
if (no_thin_onion.size()>0 && next_onion.size() > 3 * no_thin_onion.size()) {
|
float div = 2;
|
||||||
|
while (no_thin_onion.size() > 0 && next_onion.size() > no_thin_onion.size() && no_thin_onion.size() + next_onion.size() > 3) {
|
||||||
|
div += 0.5;
|
||||||
//use a sightly smaller spacing to try to drastically improve the split
|
//use a sightly smaller spacing to try to drastically improve the split
|
||||||
ExPolygons next_onion_secondTry = offset2_ex(
|
ExPolygons next_onion_secondTry = offset2_ex(
|
||||||
last,
|
last,
|
||||||
-(float)(ext_perimeter_width / 2 + ext_min_spacing / 2.5 - 1),
|
-(float)(ext_perimeter_width / 2 + ext_min_spacing / div - 1),
|
||||||
+(float)(ext_min_spacing / 2.5 - 1));
|
+(float)(ext_min_spacing / div - 1));
|
||||||
if (abs(((int32_t)next_onion.size()) - ((int32_t)no_thin_onion.size())) >
|
if (next_onion.size() > next_onion_secondTry.size()) {
|
||||||
2*abs(((int32_t)next_onion_secondTry.size()) - ((int32_t)no_thin_onion.size()))) {
|
|
||||||
next_onion = next_onion_secondTry;
|
next_onion = next_onion_secondTry;
|
||||||
}
|
}
|
||||||
|
if (div > 3) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||||
@ -269,27 +271,32 @@ void PerimeterGenerator::process()
|
|||||||
// medial axis requires non-overlapping geometry
|
// medial axis requires non-overlapping geometry
|
||||||
ExPolygons thin_zones = diff_ex(last, no_thin_zone, true);
|
ExPolygons thin_zones = diff_ex(last, no_thin_zone, true);
|
||||||
//don't use offset2_ex, because we don't want to merge the zones that have been separated.
|
//don't use offset2_ex, because we don't want to merge the zones that have been separated.
|
||||||
ExPolygons expp = offset_ex(thin_zones, (float)(-min_width / 2));
|
//a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome.
|
||||||
|
ExPolygons half_thins = offset_ex(thin_zones, (float)(-min_width / 2));
|
||||||
|
//simplify them
|
||||||
|
for (ExPolygon &half_thin : half_thins) {
|
||||||
|
half_thin.remove_point_too_near(SCALED_RESOLUTION);
|
||||||
|
}
|
||||||
//we push the bits removed and put them into what we will use as our anchor
|
//we push the bits removed and put them into what we will use as our anchor
|
||||||
if (expp.size() > 0) {
|
if (half_thins.size() > 0) {
|
||||||
no_thin_zone = diff_ex(last, offset_ex(expp, (float)(min_width / 2)), true);
|
no_thin_zone = diff_ex(last, offset_ex(half_thins, (float)(min_width / 2) - SCALED_EPSILON), true);
|
||||||
}
|
}
|
||||||
// compute a bit of overlap to anchor thin walls inside the print.
|
// compute a bit of overlap to anchor thin walls inside the print.
|
||||||
for (ExPolygon &ex : expp) {
|
for (ExPolygon &half_thin : half_thins) {
|
||||||
//growing back the polygon
|
//growing back the polygon
|
||||||
//a very little bit of overlap can be created here with other thin polygons, but it's more useful than worisome.
|
ExPolygons thin = offset_ex(half_thin, (float)(min_width / 2));
|
||||||
ex.remove_point_too_near(SCALED_RESOLUTION);
|
if (thin.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0.
|
||||||
ExPolygons ex_bigger = offset_ex(ex, (float)(min_width / 2));
|
ExPolygons anchor = intersection_ex(offset_ex(half_thin, (float)(min_width / 2) +
|
||||||
if (ex_bigger.size() != 1) continue; // impossible error, growing a single polygon can't create multiple or 0.
|
|
||||||
ExPolygons anchor = intersection_ex(offset_ex(ex, (float)(min_width / 2) +
|
|
||||||
(float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true);
|
(float)(ext_perimeter_width / 2), jtSquare), no_thin_zone, true);
|
||||||
ExPolygons bounds = union_ex(ex_bigger, anchor, true);
|
ExPolygons bounds = union_ex(thin, anchor, true);
|
||||||
for (ExPolygon &bound : bounds) {
|
for (ExPolygon &bound : bounds) {
|
||||||
if (!intersection_ex(ex_bigger[0], bound).empty()) {
|
if (!intersection_ex(thin[0], bound).empty()) {
|
||||||
//be sure it's not too small to extrude reliably
|
//be sure it's not too small to extrude reliably
|
||||||
if (ex_bigger[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
|
thin[0].remove_point_too_near(SCALED_RESOLUTION);
|
||||||
|
if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
|
||||||
|
bound.remove_point_too_near(SCALED_RESOLUTION);
|
||||||
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||||
ex_bigger[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width,
|
thin[0].medial_axis(bound, ext_perimeter_width + ext_perimeter_spacing2, min_width,
|
||||||
&thin_walls, this->layer_height);
|
&thin_walls, this->layer_height);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -296,6 +296,11 @@ void concatThickPolylines(ThickPolylines& pp) {
|
|||||||
//concat polyline if only 2 polyline at a point
|
//concat polyline if only 2 polyline at a point
|
||||||
for (size_t i = 0; i < pp.size(); ++i) {
|
for (size_t i = 0; i < pp.size(); ++i) {
|
||||||
ThickPolyline *polyline = &pp[i];
|
ThickPolyline *polyline = &pp[i];
|
||||||
|
if (polyline->first_point().coincides_with(polyline->last_point())) {
|
||||||
|
polyline->endpoints.first = false;
|
||||||
|
polyline->endpoints.second = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
size_t id_candidate_first_point = -1;
|
size_t id_candidate_first_point = -1;
|
||||||
size_t id_candidate_last_point = -1;
|
size_t id_candidate_last_point = -1;
|
||||||
@ -305,77 +310,78 @@ void concatThickPolylines(ThickPolylines& pp) {
|
|||||||
for (size_t j = 0; j < pp.size(); ++j) {
|
for (size_t j = 0; j < pp.size(); ++j) {
|
||||||
if (j == i) continue;
|
if (j == i) continue;
|
||||||
ThickPolyline *other = &pp[j];
|
ThickPolyline *other = &pp[j];
|
||||||
|
if (other->first_point().coincides_with(other->last_point())) continue;
|
||||||
if (polyline->last_point().coincides_with(other->last_point())) {
|
if (polyline->last_point().coincides_with(other->last_point())) {
|
||||||
other->reverse();
|
|
||||||
id_candidate_last_point = j;
|
id_candidate_last_point = j;
|
||||||
nbCandidate_last_point++;
|
nbCandidate_last_point++;
|
||||||
} else if (polyline->first_point().coincides_with(other->last_point())) {
|
}
|
||||||
id_candidate_first_point = j;
|
if (polyline->last_point().coincides_with(other->first_point())) {
|
||||||
nbCandidate_first_point++;
|
|
||||||
} else if (polyline->first_point().coincides_with(other->first_point())) {
|
|
||||||
id_candidate_first_point = j;
|
|
||||||
nbCandidate_first_point++;
|
|
||||||
other->reverse();
|
|
||||||
} else if (polyline->last_point().coincides_with(other->first_point())) {
|
|
||||||
id_candidate_last_point = j;
|
id_candidate_last_point = j;
|
||||||
nbCandidate_last_point++;
|
nbCandidate_last_point++;
|
||||||
} else {
|
}
|
||||||
continue;
|
if (polyline->first_point().coincides_with(other->last_point())) {
|
||||||
|
id_candidate_first_point = j;
|
||||||
|
nbCandidate_first_point++;
|
||||||
|
}
|
||||||
|
if (polyline->first_point().coincides_with(other->first_point())) {
|
||||||
|
id_candidate_first_point = j;
|
||||||
|
nbCandidate_first_point++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) {
|
if (id_candidate_last_point == id_candidate_first_point && nbCandidate_first_point == 1 && nbCandidate_last_point == 1) {
|
||||||
|
if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse();
|
||||||
// it's a trap! it's a loop!
|
// it's a trap! it's a loop!
|
||||||
if (pp[id_candidate_first_point].points.size() > 2) {
|
polyline->points.insert(polyline->points.end(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end());
|
||||||
polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin() + 1, pp[id_candidate_first_point].points.end() - 1);
|
polyline->width.insert(polyline->width.end(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end());
|
||||||
polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin() + 1, pp[id_candidate_first_point].width.end() - 1);
|
|
||||||
}
|
|
||||||
pp.erase(pp.begin() + id_candidate_first_point);
|
pp.erase(pp.begin() + id_candidate_first_point);
|
||||||
changes = true;
|
changes = true;
|
||||||
polyline->endpoints.first = false;
|
polyline->endpoints.first = false;
|
||||||
polyline->endpoints.second = false;
|
polyline->endpoints.second = false;
|
||||||
continue;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
if (nbCandidate_first_point == 1) {
|
if (nbCandidate_first_point == 1) {
|
||||||
//concat at front
|
if (polyline->first_point().coincides_with(pp[id_candidate_first_point].first_point())) pp[id_candidate_first_point].reverse();
|
||||||
polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back());
|
//concat at front
|
||||||
polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1);
|
polyline->width[0] = std::max(polyline->width.front(), pp[id_candidate_first_point].width.back());
|
||||||
polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1);
|
polyline->points.insert(polyline->points.begin(), pp[id_candidate_first_point].points.begin(), pp[id_candidate_first_point].points.end() - 1);
|
||||||
polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first;
|
polyline->width.insert(polyline->width.begin(), pp[id_candidate_first_point].width.begin(), pp[id_candidate_first_point].width.end() - 1);
|
||||||
pp.erase(pp.begin() + id_candidate_first_point);
|
polyline->endpoints.first = pp[id_candidate_first_point].endpoints.first;
|
||||||
changes = true;
|
pp.erase(pp.begin() + id_candidate_first_point);
|
||||||
if (id_candidate_first_point < i) {
|
changes = true;
|
||||||
i--;
|
if (id_candidate_first_point < i) {
|
||||||
polyline = &pp[i];
|
i--;
|
||||||
|
polyline = &pp[i];
|
||||||
|
}
|
||||||
|
if (id_candidate_last_point > id_candidate_first_point) {
|
||||||
|
id_candidate_last_point--;
|
||||||
|
}
|
||||||
|
} else if (nbCandidate_first_point == 0) {
|
||||||
|
//update endpoint
|
||||||
|
polyline->endpoints.first = true;
|
||||||
}
|
}
|
||||||
if (id_candidate_last_point > id_candidate_first_point) {
|
if (nbCandidate_last_point == 1) {
|
||||||
id_candidate_last_point--;
|
if (polyline->last_point().coincides_with(pp[id_candidate_last_point].last_point())) pp[id_candidate_last_point].reverse();
|
||||||
|
//concat at back
|
||||||
|
polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front());
|
||||||
|
polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end());
|
||||||
|
polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end());
|
||||||
|
polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second;
|
||||||
|
pp.erase(pp.begin() + id_candidate_last_point);
|
||||||
|
changes = true;
|
||||||
|
if (id_candidate_last_point < i) {
|
||||||
|
i--;
|
||||||
|
polyline = &pp[i];
|
||||||
|
}
|
||||||
|
} else if (nbCandidate_last_point == 0) {
|
||||||
|
//update endpoint
|
||||||
|
polyline->endpoints.second = true;
|
||||||
}
|
}
|
||||||
} else if (nbCandidate_first_point == 0 && !polyline->endpoints.first && !polyline->first_point().coincides_with(polyline->last_point())) {
|
|
||||||
//update endpoint
|
|
||||||
polyline->endpoints.first = true;
|
|
||||||
}
|
|
||||||
if (nbCandidate_last_point == 1) {
|
|
||||||
//concat at back
|
|
||||||
polyline->width[polyline->width.size() - 1] = std::max(polyline->width.back(), pp[id_candidate_last_point].width.front());
|
|
||||||
polyline->points.insert(polyline->points.end(), pp[id_candidate_last_point].points.begin() + 1, pp[id_candidate_last_point].points.end());
|
|
||||||
polyline->width.insert(polyline->width.end(), pp[id_candidate_last_point].width.begin() + 1, pp[id_candidate_last_point].width.end());
|
|
||||||
polyline->endpoints.second = pp[id_candidate_last_point].endpoints.second;
|
|
||||||
pp.erase(pp.begin() + id_candidate_last_point);
|
|
||||||
changes = true;
|
|
||||||
if (id_candidate_last_point < i) {
|
|
||||||
i--;
|
|
||||||
polyline = &pp[i];
|
|
||||||
}
|
|
||||||
} else if (nbCandidate_last_point == 0 && !polyline->endpoints.second && !polyline->first_point().coincides_with(polyline->last_point())) {
|
|
||||||
//update endpoint
|
|
||||||
polyline->endpoints.second = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (polyline->last_point().coincides_with(polyline->first_point())) {
|
if (polyline->last_point().coincides_with(polyline->first_point())) {
|
||||||
//the concat has created a loop : update endpoints
|
//the concat has created a loop : update endpoints
|
||||||
polyline->endpoints.first = false;
|
polyline->endpoints.first = false;
|
||||||
polyline->endpoints.second = false;
|
polyline->endpoints.second = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user