Fixes of MutablePolygon

This commit is contained in:
Vojtech Bubnik 2021-03-23 09:59:08 +01:00
parent 1c2d264570
commit 00295919bf
2 changed files with 42 additions and 13 deletions

View File

@ -122,7 +122,7 @@ static bool clip_narrow_corner(
} else { } else {
assert(backward == Free); assert(backward == Free);
p02 = it0.prev()->cast<int64_t>(); p02 = it0.prev()->cast<int64_t>();
if (cross2(p0 - p2, p02 - p2) > 0) if (cross2(p02 - p2, p0 - p2) > 0)
backward = Blocked; backward = Blocked;
else { else {
// New clipping edge lenght. // New clipping edge lenght.
@ -141,14 +141,37 @@ static bool clip_narrow_corner(
} }
} }
assert(dist2_current <= shortcut_length2);
assert(polygon.size() >= 2);
assert(polygon.size() == 2 || forward == Blocked || forward == Far);
assert(polygon.size() == 2 || backward == Blocked || backward == Far);
if (polygon.size() <= 3) { if (polygon.size() <= 3) {
// A hole degenerated to an empty polygon, or a tiny triangle remained. // A hole degenerated to an empty polygon, or a tiny triangle remained.
assert(polygon.size() < 3 || (forward == Blocked && backward == Blocked) || (forward == Far && backward == Far)); bool blocked = forward == Blocked || backward == Blocked;
if (polygon.size() < 3 || forward == Far) { assert(polygon.size() < 3 ||
assert(polygon.size() < 3 || dist2_current <= shortcut_length2); // Remaining triangle is CCW oriented. Both sides must be "blocked", but the other side may have not been
// updated after the the p02 / p22 became united into a single point.
blocked ||
// Remaining triangle is concave, however both of its arms are long.
(forward == Far && backward == Far));
#ifndef _NDEBUG
if (polygon.size() == 3) {
// Verify that the remaining triangle is CCW or CW.
p02 = it0.prev()->cast<int64_t>();
p22 = it2.next()->cast<int64_t>();
assert(p02 == p22);
auto orient1 = cross2(p02 - p2, p0 - p2);
auto orient2 = cross2(p2 - p0, p22 - p0);
assert(orient1 > 0 == blocked);
assert(orient2 > 0 == blocked);
}
#endif // _NDEBUG
if (polygon.size() < 3 || (forward == Far && backward == Far)) {
polygon.clear(); polygon.clear();
} else { } else {
// The remaining triangle is CCW oriented, keep it. // The remaining triangle is CCW oriented, keep it.
assert(forward == Blocked || backward == Blocked);
} }
return true; return true;
} }
@ -168,7 +191,7 @@ static bool clip_narrow_corner(
} else if (forward == Blocked || backward == Blocked) { } else if (forward == Blocked || backward == Blocked) {
// One side is far, the other blocked. // One side is far, the other blocked.
assert(forward == Far || backward == Far); assert(forward == Far || backward == Far);
if (backward == Far) { if (forward == Far) {
// Sort, so we will clip the 1st edge. // Sort, so we will clip the 1st edge.
std::swap(p0, p2); std::swap(p0, p2);
std::swap(p02, p22); std::swap(p02, p22);
@ -177,6 +200,10 @@ static bool clip_narrow_corner(
// Circle intersects a line at two points, however because |p2 - p0| < shortcut_length, // Circle intersects a line at two points, however because |p2 - p0| < shortcut_length,
// only the second intersection is valid. Because |p2 - p02| > shortcut_length, such // only the second intersection is valid. Because |p2 - p02| > shortcut_length, such
// intersection should always be found on (p0, p02). // intersection should always be found on (p0, p02).
#ifndef NDEBUG
auto dfar2 = (p02 - p2).squaredNorm();
assert(dfar2 >= shortcut_length2);
#endif // NDEBUG
const Vec2d v = (p02 - p0).cast<double>(); const Vec2d v = (p02 - p0).cast<double>();
const Vec2d d = (p0 - p2).cast<double>(); const Vec2d d = (p0 - p2).cast<double>();
const double a = v.squaredNorm(); const double a = v.squaredNorm();
@ -273,15 +300,17 @@ void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled)
Point pt_new = p1 + (t * v2d).cast<coord_t>(); Point pt_new = p1 + (t * v2d).cast<coord_t>();
#ifndef NDEBUG #ifndef NDEBUG
double d2new = (pt_new - (swap ? p2 : p0)).cast<double>().squaredNorm(); double d2new = (pt_new - (swap ? p2 : p0)).cast<double>().squaredNorm();
assert(std::abs(d2new - clip_dist_scaled2) < sqr(10. * SCALED_EPSILON)); assert(std::abs(d2new - clip_dist_scaled2) < 1e-5 * clip_dist_scaled2);
#endif // NDEBUG #endif // NDEBUG
it2.insert(pt_new); it2.insert(pt_new);
} else { } else {
// Cut the corner with a line perpendicular to the bisector. // Cut the corner with a line perpendicular to the bisector.
double t = sqrt(0.25 * clip_dist_scaled2 / d2); double t = sqrt(0.25 * clip_dist_scaled2 / d2);
double t2 = t * lv1 / lv2;
assert(t > 0. && t < 1.); assert(t > 0. && t < 1.);
Point p0 = p1 + (v1d * t).cast<coord_t>(); assert(t2 > 0. && t2 < 1.);
Point p2 = p1 + (v2d * (t * lv2 / lv1)).cast<coord_t>(); Point p0 = p1 + (v1d * t ).cast<coord_t>();
Point p2 = p1 + (v2d * t2).cast<coord_t>();
if (swap) if (swap)
std::swap(p0, p2); std::swap(p0, p2);
it2.insert(p2).insert(p0); it2.insert(p2).insert(p0);

View File

@ -85,7 +85,7 @@ public:
} }
void advance_front() { void advance_front() {
assert(!this->empty()); assert(! this->empty());
if (m_begin == m_end) if (m_begin == m_end)
this->make_empty(); this->make_empty();
else else
@ -93,7 +93,7 @@ public:
} }
void retract_back() { void retract_back() {
assert(!this->empty()); assert(! this->empty());
if (m_begin == m_end) if (m_begin == m_end)
this->make_empty(); this->make_empty();
else else
@ -101,13 +101,13 @@ public:
} }
MutablePolygon::iterator remove_front(MutablePolygon::iterator it) { MutablePolygon::iterator remove_front(MutablePolygon::iterator it) {
if (m_begin == it) if (! this->empty() && m_begin == it)
this->advance_front(); this->advance_front();
return it.remove(); return it.remove();
} }
MutablePolygon::iterator remove_back(MutablePolygon::iterator it) { MutablePolygon::iterator remove_back(MutablePolygon::iterator it) {
if (m_end == it) if (! this->empty() && m_end == it)
this->retract_back(); this->retract_back();
return it.remove(); return it.remove();
} }