示例#1
0
/* 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);
    }
}
示例#2
0
/* 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;
    }
    }
}