More refactoring to medial axis and gap fill, more robust

This commit is contained in:
Alessandro Ranellucci 2016-05-20 06:24:05 +02:00
parent b068616366
commit 9e8022f6f6
13 changed files with 133 additions and 58 deletions

View file

@ -324,7 +324,7 @@ MedialAxis::build(ThickPolylines* polylines)
if (edge->is_secondary() || edge->is_infinite()) continue;
// don't re-validate twins
if (seen_edges.find(&*edge) != seen_edges.end()) continue;
if (seen_edges.find(&*edge) != seen_edges.end()) continue; // TODO: is this needed?
seen_edges.insert(&*edge);
seen_edges.insert(edge->twin());
@ -445,49 +445,66 @@ MedialAxis::validate_edge(const VD::edge_type* edge)
}
// retrieve the original line segments which generated the edge we're checking
const VD::cell_type* cell1 = edge->cell();
const VD::cell_type* cell2 = edge->twin()->cell();
const Line &segment1 = this->retrieve_segment(cell1);
const Line &segment2 = this->retrieve_segment(cell2);
const VD::cell_type* cell_l = edge->cell();
const VD::cell_type* cell_r = edge->twin()->cell();
const Line &segment_l = this->retrieve_segment(cell_l);
const Line &segment_r = this->retrieve_segment(cell_r);
/* Calculate thickness of the section at both the endpoints of this edge.
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
(segment1). This edge's twin goes around segment2. Thus, segment2 is
oriented in the same direction as our main edge, and segment1 is oriented
/*
SVG svg("edge.svg");
svg.draw(*this->expolygon);
svg.draw(line);
svg.draw(segment_l, "red");
svg.draw(segment_r, "blue");
svg.Close();
*/
/* Calculate thickness of the cross-section at both the endpoints of this edge.
Our Voronoi edge is part of a CCW sequence going around its Voronoi cell
located on the left side. (segment_l).
This edge's twin goes around segment_r. Thus, segment_r is
oriented in the same direction as our main edge, and segment_l is oriented
in the same direction as our twin edge.
We used to only consider the (half-)distances to segment2, and that works
whenever segment1 and segment2 are almost specular and facing. However,
We used to only consider the (half-)distances to segment_r, and that works
whenever segment_l and segment_r are almost specular and facing. However,
at curves they are staggered and they only face for a very little length
(such visibility actually coincides with our very short edge). This is why
we calculate w0 and w1 this way.
When cell1 or cell2 don't refer to the segment but only to an endpoint, we
(our very short edge represents such visibility).
Both w0 and w1 can be calculated either towards cell_l or cell_r with equal
results by Voronoi definition.
When cell_l or cell_r don't refer to the segment but only to an endpoint, we
calculate the distance to that endpoint instead. */
coordf_t w0 = cell2->contains_segment()
? line.a.perp_distance_to(segment2)*2
: line.a.distance_to(this->retrieve_endpoint(cell2))*2;
coordf_t w0 = cell_r->contains_segment()
? line.a.distance_to(segment_r)*2
: line.a.distance_to(this->retrieve_endpoint(cell_r))*2;
coordf_t w1 = cell1->contains_segment()
? line.b.perp_distance_to(segment1)*2
: line.b.distance_to(this->retrieve_endpoint(cell1))*2;
coordf_t w1 = cell_l->contains_segment()
? line.b.distance_to(segment_l)*2
: line.b.distance_to(this->retrieve_endpoint(cell_l))*2;
// if this edge is the centerline for a very thin area, we might want to skip it
// in case the area is too thin
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON) {
if (cell1->contains_segment() && cell2->contains_segment()) {
// calculate the relative angle between the two boundary segments
double angle = fabs(segment2.orientation() - segment1.orientation());
if (cell_l->contains_segment() && cell_r->contains_segment()) {
// calculate the relative angle between the two boundary segments
double angle = fabs(segment_r.orientation() - segment_l.orientation());
if (angle > PI) angle = 2*PI - angle;
assert(angle >= 0 && angle <= PI);
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
// we're interested only in segments close to the second case (facing segments)
// so we allow some tolerance.
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
// we don't run it on edges not generated by two segments (thus generated by one segment
// and the endpoint of another segment), since their orientation would not be meaningful
if (PI - angle > PI/8) {
// angle is not narrow enough
// fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction)
// we're interested only in segments close to the second case (facing segments)
// so we allow some tolerance.
// this filter ensures that we're dealing with a narrow/oriented area (longer than thick)
// we don't run it on edges not generated by two segments (thus generated by one segment
// and the endpoint of another segment), since their orientation would not be meaningful
if (fabs(angle - PI) > PI/5) return false;
} else {
return false;
// only apply this filter to segments that are not too short otherwise their
// angle could possibly be not meaningful
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON || line.length() >= this->min_width)
return false;
}
} else {
if (w0 < SCALED_EPSILON || w1 < SCALED_EPSILON)
return false;
}
if (w0 < this->min_width && w1 < this->min_width)