/* approximates a bezier curve with a set of circular arcs by dividing where * the bezier's deviation from its approximating biarc is at a maximum, then * recursively calling on the subsections until it is approximated to * tolerance by the biarc */ HIDDEN void approx_bezier(const ON_BezierCurve& bezier, const ON_Arc& biarc, const struct bn_tol *tol, std::vector<ON_Arc>& approx) { fastf_t t = 0.0, step = 0.0; fastf_t crv = 0.0, err = 0.0, max_t = 0.0, max_err = 0.0; ON_3dPoint test; ON_3dVector d1, d2; // walk the bezier curve at interval given by step for (t = 0; t <= 1.0; t += step) { bezier.Ev2Der(t, test, d1, d2); err = fabs((test - biarc.Center()).Length() - biarc.Radius()); // find the maximum point of deviation if (err > max_err) { max_t = t; max_err = err; } crv = CURVATURE(d1, d2); // step size decreases as |crv| -> 1 step = GETSTEPSIZE(1.0 - fabs(crv)); } if (max_err + VDIVIDE_TOL < tol->dist) { // max deviation is less than the given tolerance, add the biarc approximation approx.push_back(biarc); } else { ON_BezierCurve head, tail; // split bezier at point of maximum deviation and recurse on the new subsections bezier.Split(max_t, head, tail); approx_bezier(head, make_biarc(head), tol, approx); approx_bezier(tail, make_biarc(tail), tol, approx); } }
/* approximates a bezier curve with a set of circular arcs. * returns approximation in carcs */ HIDDEN void bezier_to_carcs(const ON_BezierCurve& bezier, const struct bn_tol *tol, std::vector<ON_Arc>& carcs) { bool skip_while = true, curvature_changed = false; fastf_t inflection_pt, biarc_angle; ON_Arc biarc; ON_BezierCurve current, next; std::vector<ON_BezierCurve> rest; // find inflection point, if it exists if (bezier_inflection(bezier, inflection_pt)) { curvature_changed = true; bezier.Split(inflection_pt, current, next); rest.push_back(next); } else { current = bezier; } while (skip_while || !rest.empty()) { if (skip_while) skip_while = false; biarc = make_biarc(current); if ((biarc_angle = biarc.AngleRadians()) <= M_PI_2) { // approximate the current bezier segment and add its biarc // approximation to carcs approx_bezier(current, biarc, tol, carcs); } else if (biarc_angle <= M_PI) { // divide the current bezier segment in half current.Split(0.5, current, next); // approximate first bezier segment approx_bezier(current, biarc, tol, carcs); // approximate second bezier segment approx_bezier(next, biarc, tol, carcs); } else { fastf_t t = 1.0; ON_Arc test_biarc; ON_BezierCurve test_bezier; // divide the current bezier such that the first curve segment would // have an approximating biarc segment <=90 degrees do { t *= 0.5; current.Split(t, test_bezier, next); test_biarc = make_biarc(test_bezier); } while(test_biarc.AngleRadians() > M_PI_2); approx_bezier(test_bezier, test_biarc, tol, carcs); current = next; skip_while = true; continue; } if (curvature_changed) { curvature_changed = false; current = rest.back(); rest.pop_back(); // continue even if we just popped the last element skip_while = true; } } }