ArcWelder: Fixing some edge conditions with least squares fitting.

This commit is contained in:
Vojtech Bubnik 2023-10-03 14:35:31 +02:00
parent a693adbeab
commit 5b45777d4b

View File

@ -264,15 +264,18 @@ static std::optional<Circle> try_create_circle(const Points::const_iterator begi
// of all points on the polyline to be fitted. // of all points on the polyline to be fitted.
Vec2i64 first_point = begin->cast<int64_t>(); Vec2i64 first_point = begin->cast<int64_t>();
Vec2i64 last_point = std::prev(end)->cast<int64_t>(); Vec2i64 last_point = std::prev(end)->cast<int64_t>();
Vec2i64 c = (first_point.cast<int64_t>() + last_point.cast<int64_t>()) / 2;
Vec2i64 v = last_point - first_point; Vec2i64 v = last_point - first_point;
Vec2d vd = v.cast<double>();
double ld = v.squaredNorm();
if (ld > sqr(scaled<double>(0.0015))) {
Vec2i64 c = (first_point.cast<int64_t>() + last_point.cast<int64_t>()) / 2;
Vec2i64 prev_point = first_point; Vec2i64 prev_point = first_point;
int prev_side = sign(v.dot(prev_point - c)); int prev_side = sign(v.dot(prev_point - c));
assert(prev_side != 0); assert(prev_side != 0);
Point point_on_bisector; Point point_on_bisector;
#ifndef NDEBUG #ifndef NDEBUG
point_on_bisector = { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; point_on_bisector = { std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
#endif // NDEBUG #endif // NDEBUG
for (auto it = std::next(begin); it != end; ++ it) { for (auto it = std::next(begin); it != end; ++ it) {
Vec2i64 this_point = it->cast<int64_t>(); Vec2i64 this_point = it->cast<int64_t>();
int64_t d = v.dot(this_point - c); int64_t d = v.dot(this_point - c);
@ -280,8 +283,7 @@ static std::optional<Circle> try_create_circle(const Points::const_iterator begi
int sideness = this_side * prev_side; int sideness = this_side * prev_side;
if (sideness < 0) { if (sideness < 0) {
// Calculate the intersection point. // Calculate the intersection point.
Vec2d vd = v.cast<double>(); Vec2d p = c.cast<double>() + vd * double(d) / ld;
Vec2d p = c.cast<double>() + vd * double(d) / vd.squaredNorm();
point_on_bisector = p.cast<coord_t>(); point_on_bisector = p.cast<coord_t>();
break; break;
} }
@ -303,6 +305,7 @@ static std::optional<Circle> try_create_circle(const Points::const_iterator begi
circle && ! circle_approximation_sufficient(*circle, begin, end, tolerance * 2)) circle && ! circle_approximation_sufficient(*circle, begin, end, tolerance * 2))
circle.reset(); circle.reset();
} }
}
if (circle) { if (circle) {
// Fit the arc between the end points by least squares. // Fit the arc between the end points by least squares.
// Optimize over all points along the path and the centers of the segments. // Optimize over all points along the path and the centers of the segments.
@ -320,8 +323,10 @@ static std::optional<Circle> try_create_circle(const Points::const_iterator begi
std::optional<Vec2d> opt_center = ArcWelder::arc_fit_center_gauss_newton_ls(first_point, last_point, std::optional<Vec2d> opt_center = ArcWelder::arc_fit_center_gauss_newton_ls(first_point, last_point,
circle->center.cast<double>(), fpts.begin(), fpts.end(), 5); circle->center.cast<double>(), fpts.begin(), fpts.end(), 5);
if (opt_center) { if (opt_center) {
// Fitted radius must not be excessively large. If so, it is better to fit with a line segment.
if (const double r2 = (*opt_center - first_point).squaredNorm(); r2 < max_radius * max_radius) {
circle->center = opt_center->cast<coord_t>(); circle->center = opt_center->cast<coord_t>();
circle->radius = (circle->radius > 0 ? 1.f : -1.f) * (*opt_center - first_point).norm(); circle->radius = (circle->radius > 0 ? 1.f : -1.f) * sqrt(r2);
if (circle_approximation_sufficient(*circle, begin, end, tolerance)) { if (circle_approximation_sufficient(*circle, begin, end, tolerance)) {
out = circle; out = circle;
} else { } else {
@ -330,6 +335,7 @@ static std::optional<Circle> try_create_circle(const Points::const_iterator begi
} }
} }
} }
}
/* /*
// From the original arc welder. // From the original arc welder.
// Such a loop makes the time complexity of the arc fitting an ugly O(n^3). // Such a loop makes the time complexity of the arc fitting an ugly O(n^3).