//-*-c++-*- #ifndef BEZIERCURVE_H #define BEZIERCURVE_H /** * @file beziercurve.h * @brief Generalized Bezier Curve class for M-Dimensional, Nth order. * @author James R. Diebel, Stanford University * * - History: * - 30 August 2006 - Started (JD) */ #include #include #include #include using namespace sla; /** * @namespace sla * @brief Simple Linear Algebra - library of matrix and vector classes */ namespace sla { ///////////////////////////////////////////////////////////////////////////// // DECLARATIONS ///////////////////////////////////////////////////////////////////////////// //////////////////// BezierCurve //////////////////// /** * An M-Dimensional, Nth order Bezier Curve. */ template class BezierCurve { public: // Constructors BezierCurve(); // Mutators void setCoefficients(const Mat& a); void computeInterpolant(const Vec& t); T fitDataFixedEnds(const Array1 t, const Array1< Vec >& p); T fitProjectedDataFixedEnds(const Array1 t, const Array1< Vec >& p); // Accessors Vec eval(const T t) const; Mat getCoefficients() const; // Utility functions Vec bezierVec(const T t) const; Vec polyVec(const T t) const; SMat polyMat(const Vec& t) const; // Data Vec< Vec, N+1> x; }; // Declare a few common typdefs typedef BezierCurve BezierCurve23f; typedef BezierCurve BezierCurve23d; //////////////////////////////////////////////////////////////////////////// // DEFINITIONS //////////////////////////////////////////////////////////////////////////// //////////////////// Vec //////////////////// // Constructors /** Ctor that does no initalization. */ template inline BezierCurve::BezierCurve() {} // Mutators /** Set the coefficients of the equivalent polynomial. */ template inline void BezierCurve::setCoefficients(const Mat& a) { int nck; for (int k=0;k<=N;k++) { if (k==0) nck = 1; else nck = (nck*(N-(k-1)))/k; x(k) = (T(1)/T(nck))*a.getCol(k); int kci; for (int i=0;i inline T BezierCurve::fitDataFixedEnds(const Array1 t, const Array1< Vec >& p) { if (t.length() != p.length()) { cerr << "BezierCurve::fitDataFixedEnds: " << "t and p arrays are not of same size. " << endl << flush; exit(1); } if (N <= 1) { cerr << "BezierCurve::fitDataFixedEnds: " << "must be at least 2nd order." << endl << flush; exit(1); } if (t.length() == 0) { cerr << "BezierCurve::fitDataFixedEnds: " << "t and p have no data!" << endl << flush; exit(1); } // Allocate some memory int num_samples = t.length(); GEMat residual(num_samples*M,1); GEMat jacobian(num_samples*M,M*(N-1)); GEMat hessian(M*(N-1),M*(N-1)); GEMat gradient(M*(N-1),1); // For each sample, add some things to the jacobian and residual jacobian.set(0.0); hessian.set(0.0); gradient.set(0.0); for (int i=0;i b = bezierVec(t(i)); // compute the residual contribution for (int j=0;j inline double BezierCurve::fitProjectedDataFixedEnds(const Array1 t, const Array1< Vec >& p) { if (t.length() != p.length()) { cerr << "BezierCurve::fitDataFixedEnds: " << "t and p arrays are not of same size. " << endl << flush; exit(1); } if (t.length() == 0) { cerr << "BezierCurve::fitDataFixedEnds: " << "t and p have no data!" << endl << flush; exit(1); } // Allocate some memory int num_samples = t.length(); GEMat residual(num_samples,1); GEMat jacobian(num_samples,4); GEMat hessian(4,4); GEMat gradient(4,1); Array1< Vec2d > normal(t.length()); Vec2d v0, v1; // For each sample, add some things to the jacobian and residual residual.set(0.0); jacobian.set(0.0); hessian.set(0.0); gradient.set(0.0); for (int i=0;i b = bezierVec(t(i)); // construct the normal vector if (i == 0) v0 = p(0) - x(0); else v0 = p(i) - p(i-1); if (i == t.length()-1) v1 = x(3) - p(i); else v1 = p(i+1) - p(i); normal(i) = 0.5*(v0 + v1); normal(i).normalize(); normal(i).set(-normal(i)(1),normal(i)(0)); // compute the residual contribution residual(i) += normal(i).dot(p(i) - (b(0)*x(0) + b(3)*x(3))); // compute the jacobian contribution jacobian(i,0) = b(1)*normal(i)(0); jacobian(i,1) = b(1)*normal(i)(1); jacobian(i,2) = b(2)*normal(i)(0); jacobian(i,3) = b(2)*normal(i)(1); } // solve the system for (int i=0;i inline void BezierCurve::computeInterpolant(const Vec& t) { // make sure no repeats in t and that strictly increasing for (int i=1;i<=N;i++) { if (t(i) <= t(i-1)) { cerr << "BezierCurve::computeInterpolant: " << "Must provide strictly increasing values for t for fit. " << "Exiting." << endl << flush; exit(1); } } // fit for each dimension independently Mat a; for (int k=0;k t_mat = polyMat(t); Vec x_vec; for (int i=0;i<=N;i++) x_vec(i) = x(i)(k); t_mat.invert(); Vec x_out = t_mat*x_vec; a.setRow(k,x_out); } setCoefficients(a); } // Accessors /** Evaluate the curve at the parameter t. * @param t is the evaluate point. */ template inline Vec BezierCurve::eval(const T t) const { T omt = T(1)-t; switch (N) { case 1: return omt*x(0) + t*x(1); case 2: return sqr(omt)*x(0) + (T(2)*t*omt)*x(1) + sqr(t)*x(2); case 3: { T omt_sqr = sqr(omt); T t_sqr = sqr(t); return ((omt*omt_sqr)*x(0) + (T(3)*t*omt_sqr)*x(1) + (T(3)*t_sqr*omt)*x(2) + (t*t_sqr)*x(3)); } default: Vec z(T(0)); Vec b = bezierVec(t); for (int k=0;k<=N;k++) z += b(k)*x(k); return z; } } /** Return the coefficients of the equivalent polynomial. */ template inline Mat BezierCurve::getCoefficients() const { Mat a; int nck; for (int k=0;k<=N;k++) { if (k==0) nck = 1; else nck = (nck*(N-(k-1)))/k; Vec ak(T(0)); int kci; for (int i=0;i<=k;i++) { if (i==0) kci = 1; else kci = (kci*(k-(i-1)))/i; int sgn = ((k+i)%2)?-1:1; ak += (T(kci)*T(sgn))*x(i); } ak *= T(nck); a.setCol(k,ak); } return a; } /** Return the bezier vector. */ template inline Vec BezierCurve::bezierVec(const T t) const { Vec tv; T omt = (T(1)-t); switch (N) { case 1: { tv(0) = omt; tv(1) = t; break; } case 2: { tv(0) = sqr(omt); tv(1) = T(2)*omt*t; tv(2) = sqr(t); break; } case 3: { T omt_sqr = sqr(omt); T t_sqr = sqr(t); tv(0) = omt*omt_sqr; tv(1) = T(3)*omt_sqr*t; tv(2) = T(3)*omt*t_sqr; tv(3) = t*t_sqr; break; } default: { int nck; for (int k=0;k<=N;k++) { if (k==0) nck = 1; else nck = (nck*(N-(k-1)))/k; tv(k) = T(nck)*T(pow(double(omt),double(N-k))*pow(double(t),double(k))); } break; } } return tv; } /** Return the polynomial vector. */ template inline Vec BezierCurve::polyVec(const T t) const { Vec tv; tv(0) = T(1); for (int i=1;i<=N;i++) tv(i) = tv(i-1)*t; return tv; } /** Return the polynomial matrix. */ template inline SMat BezierCurve::polyMat(const Vec& t) const { SMat t_mat; for (int i=0;i<=N;i++) { t_mat(i,0) = T(1); for (int j=1;j<=N;j++) { t_mat(i,j) = t_mat(i,j-1)*t(i); } } return t_mat; } } // end namespace sla #endif