/* 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); } }
static ON_BOOL32 NurbsCurveArc ( const ON_Arc& arc, int dim, ON_NurbsCurve& nurb ) { if ( !arc.IsValid() ) return false; // makes a quadratic nurbs arc const ON_3dPoint center = arc.Center(); double angle = arc.AngleRadians(); ON_Interval dom = arc.DomainRadians(); const double angle0 = dom[0]; const double angle1 = dom[1]; ON_3dPoint start_point = arc.StartPoint(); //ON_3dPoint mid_point = arc.PointAt(angle0 + 0.5*angle); ON_3dPoint end_point = arc.IsCircle() ? start_point : arc.EndPoint(); ON_4dPoint CV[9]; double knot[10]; double a, b, c, w, winv; double *cv; int j, span_count, cv_count; a = (0.5 + ON_SQRT_EPSILON)*ON_PI; if (angle <= a) span_count = 1; else if (angle <= 2.0*a) span_count = 2; else if (angle <= 3.0*a) span_count = 4; // TODO - make a 3 span case else span_count = 4; cv_count = 2*span_count + 1; switch(span_count) { case 1: CV[0] = start_point; CV[1] = arc.PointAt(angle0 + 0.50*angle); CV[2] = end_point; break; case 2: CV[0] = start_point; CV[1] = arc.PointAt(angle0 + 0.25*angle); CV[2] = arc.PointAt(angle0 + 0.50*angle); CV[3] = arc.PointAt(angle0 + 0.75*angle); CV[4] = end_point; angle *= 0.5; break; default: // 4 spans CV[0] = start_point; CV[1] = arc.PointAt(angle0 + 0.125*angle); CV[2] = arc.PointAt(angle0 + 0.250*angle); CV[3] = arc.PointAt(angle0 + 0.375*angle); CV[4] = arc.PointAt(angle0 + 0.500*angle); CV[5] = arc.PointAt(angle0 + 0.625*angle); CV[6] = arc.PointAt(angle0 + 0.750*angle); CV[7] = arc.PointAt(angle0 + 0.875*angle); CV[8] = end_point; angle *= 0.25; break; } a = cos(0.5*angle); b = a - 1.0; //c = (radius > 0.0) ? radius*angle : angle; c = angle; span_count *= 2; knot[0] = knot[1] = angle0; //0.0; for (j = 1; j < span_count; j += 2) { CV[j].x += b * center.x; CV[j].y += b * center.y; CV[j].z += b * center.z; CV[j].w = a; CV[j+1].w = 1.0; knot[j+1] = knot[j+2] = knot[j-1] + c; } knot[cv_count-1] = knot[cv_count] = angle1; for ( j = 1; j < span_count; j += 2 ) { w = CV[j].w; winv = 1.0/w; a = CV[j].x*winv; b = ArcDeFuzz(a); if ( a != b ) { CV[j].x = b*w; } a = CV[j].y*winv; b = ArcDeFuzz(a); if ( a != b ) { CV[j].y = b*w; } a = CV[j].z*winv; b = ArcDeFuzz(a); if ( a != b ) { CV[j].z = b*w; } } nurb.m_dim = (dim==2) ? 2 : 3; nurb.m_is_rat = 1; nurb.m_order = 3; nurb.m_cv_count = cv_count; nurb.m_cv_stride = (dim==2 ? 3 : 4); nurb.ReserveCVCapacity( nurb.m_cv_stride*cv_count ); nurb.ReserveKnotCapacity( cv_count+1 ); for ( j = 0; j < cv_count; j++ ) { cv = nurb.CV(j); cv[0] = CV[j].x; cv[1] = CV[j].y; if ( dim == 2 ) { cv[2] = CV[j].w; } else { cv[2] = CV[j].z; cv[3] = CV[j].w; } nurb.m_knot[j] = knot[j]; } nurb.m_knot[cv_count] = knot[cv_count]; return true; }