logo

Inkscape

  • Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

bezier.h

Go to the documentation of this file.
00001 
00034 #ifndef SEEN_BEZIER_H
00035 #define SEEN_BEZIER_H
00036 
00037 #include <2geom/coord.h>
00038 #include <valarray>
00039 #include <2geom/isnan.h>
00040 #include <2geom/d2.h>
00041 #include <2geom/solver.h>
00042 #include <boost/optional/optional.hpp>
00043 
00044 namespace Geom {
00045 
00046 inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) {
00047 /*
00048  *  Bernstein : 
00049  *      Evaluate a Bernstein function at a particular parameter value
00050  *      Fill in control points for resulting sub-curves.
00051  * 
00052  */
00053 
00054     unsigned N = order+1;
00055     std::valarray<Coord> row(N);
00056     for (unsigned i = 0; i < N; i++)
00057         row[i] = v[i];
00058 
00059     // Triangle computation
00060     const double omt = (1-t);
00061     if(left)
00062         left[0] = row[0];
00063     if(right)
00064         right[order] = row[order];
00065     for (unsigned i = 1; i < N; i++) {
00066         for (unsigned j = 0; j < N - i; j++) {
00067             row[j] = omt*row[j] + t*row[j+1];
00068         }
00069         if(left)
00070             left[i] = row[0];
00071         if(right)
00072             right[order-i] = row[order-i];
00073     }
00074     return (row[0]);
00075 /*
00076     Coord vtemp[order+1][order+1];
00077 
00078     // Copy control points
00079     std::copy(v, v+order+1, vtemp[0]);
00080 
00081     // Triangle computation
00082     for (unsigned i = 1; i <= order; i++) {
00083         for (unsigned j = 0; j <= order - i; j++) {
00084             vtemp[i][j] = lerp(t, vtemp[i-1][j], vtemp[i-1][j+1]);
00085         }
00086     }
00087     if(left != NULL)
00088         for (unsigned j = 0; j <= order; j++)
00089             left[j]  = vtemp[j][0];
00090     if(right != NULL)
00091         for (unsigned j = 0; j <= order; j++)
00092             right[j] = vtemp[order-j][j];
00093 
00094             return (vtemp[order][0]);*/
00095 }
00096 
00097 template <typename T>
00098 inline T bernsteinValueAt(double t, T const *c_, unsigned n) {
00099     double u = 1.0 - t;
00100     double bc = 1;
00101     double tn = 1;
00102     T tmp = c_[0]*u;
00103     for(unsigned i=1; i<n; i++){
00104         tn = tn*t;
00105         bc = bc*(n-i+1)/i;
00106         tmp = (tmp + tn*bc*c_[i])*u;
00107     }
00108     return (tmp + tn*t*c_[n]);
00109 }
00110 
00111 
00112 class Bezier {
00113 private:
00114     std::valarray<Coord> c_;
00115 
00116     friend Bezier portion(const Bezier & a, Coord from, Coord to);
00117 
00118     friend OptInterval bounds_fast(Bezier const & b);
00119 
00120     friend Bezier derivative(const Bezier & a);
00121 
00122 protected:
00123     Bezier(Coord const c[], unsigned ord) : c_(c, ord+1){
00124         //std::copy(c, c+order()+1, &c_[0]);
00125     }
00126 
00127 public:
00128     unsigned int order() const { return c_.size()-1;}
00129     unsigned int size() const { return c_.size();}
00130 
00131     Bezier() {}
00132     Bezier(const Bezier& b) :c_(b.c_) {}
00133     Bezier &operator=(Bezier const &other) {
00134         if ( c_.size() != other.c_.size() ) {
00135             c_.resize(other.c_.size());
00136         }
00137         c_ = other.c_;
00138         return *this;
00139     }
00140 
00141     struct Order {
00142         unsigned order;
00143         explicit Order(Bezier const &b) : order(b.order()) {}
00144         explicit Order(unsigned o) : order(o) {}
00145         operator unsigned() const { return order; }
00146     };
00147 
00148     //Construct an arbitrary order bezier
00149     Bezier(Order ord) : c_(0., ord.order+1) {
00150         assert(ord.order ==  order());
00151     }
00152 
00153     explicit Bezier(Coord c0) : c_(0., 1) {
00154         c_[0] = c0;
00155     }
00156 
00157     //Construct an order-1 bezier (linear Bézier)
00158     Bezier(Coord c0, Coord c1) : c_(0., 2) {
00159         c_[0] = c0; c_[1] = c1;
00160     }
00161 
00162     //Construct an order-2 bezier (quadratic Bézier)
00163     Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) {
00164         c_[0] = c0; c_[1] = c1; c_[2] = c2;
00165     }
00166 
00167     //Construct an order-3 bezier (cubic Bézier)
00168     Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(0., 4) {
00169         c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3;
00170     }
00171 
00172     void resize (unsigned int n, Coord v = 0)
00173     {
00174         c_.resize (n, v);
00175     }
00176 
00177     void clear()
00178     {
00179         c_.resize(0);
00180     }
00181 
00182     inline unsigned degree() const { return order(); }
00183 
00184     //IMPL: FragmentConcept
00185     typedef Coord output_type;
00186     inline bool isZero() const {
00187         for(unsigned i = 0; i <= order(); i++) {
00188             if(c_[i] != 0) return false;
00189         }
00190         return true;
00191     }
00192     inline bool isConstant() const {
00193         for(unsigned i = 1; i <= order(); i++) {
00194             if(c_[i] != c_[0]) return false;
00195         }
00196         return true;
00197     }
00198     inline bool isFinite() const {
00199         for(unsigned i = 0; i <= order(); i++) {
00200             if(!IS_FINITE(c_[i])) return false;
00201         }
00202         return true;
00203     }
00204     inline Coord at0() const { return c_[0]; }
00205     inline Coord at1() const { return c_[order()]; }
00206 
00207     inline Coord valueAt(double t) const {
00208         int n = order();
00209         double u, bc, tn, tmp;
00210         int i;
00211         u = 1.0 - t;
00212         bc = 1;
00213         tn = 1;
00214         tmp = c_[0]*u;
00215         for(i=1; i<n; i++){
00216             tn = tn*t;
00217             bc = bc*(n-i+1)/i;
00218             tmp = (tmp + tn*bc*c_[i])*u;
00219         }
00220         return (tmp + tn*t*c_[n]);
00221         //return subdivideArr(t, &c_[0], NULL, NULL, order());
00222     }
00223     inline Coord operator()(double t) const { return valueAt(t); }
00224 
00225     SBasis toSBasis() const;
00226 //    inline SBasis toSBasis() const {
00227 //        SBasis sb;
00228 //        bezier_to_sbasis(sb, (*this));
00229 //        return sb;
00230 //        //return bezier_to_sbasis(&c_[0], order());
00231 //    }
00232 
00233     //Only mutator
00234     inline Coord &operator[](unsigned ix) { return c_[ix]; }
00235     inline Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
00236     //inline Coord const &operator[](unsigned ix) const { return c_[ix]; }
00237     inline void setPoint(unsigned ix, double val) { c_[ix] = val; }
00238 
00242     std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const {
00243         /* This is inelegant, as it uses several extra stores.  I think there might be a way to
00244          * evaluate roughly in situ. */
00245 
00246          // initialize return vector with zeroes, such that we only need to replace the non-zero derivs
00247         std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0));
00248 
00249         // initialize temp storage variables
00250         std::valarray<Coord> d_(order()+1);
00251         for (unsigned i = 0; i < size(); i++) {
00252             d_[i] = c_[i];
00253         }
00254 
00255         unsigned nn = n_derivs + 1;
00256         if(n_derivs > order()) {
00257             nn = order()+1; // only calculate the non zero derivs
00258         }
00259         for (unsigned di = 0; di < nn; di++) {
00260             //val_n_der[di] = (subdivideArr(t, &d_[0], NULL, NULL, order() - di));
00261             val_n_der[di] = bernsteinValueAt(t, &d_[0], order() - di);
00262             for (unsigned i = 0; i < order() - di; i++) {
00263                 d_[i] = (order()-di)*(d_[i+1] - d_[i]);
00264             }
00265         }
00266 
00267         return val_n_der;
00268     }
00269 
00270     std::pair<Bezier, Bezier > subdivide(Coord t) const {
00271         Bezier a(Bezier::Order(*this)), b(Bezier::Order(*this));
00272         subdivideArr(t, &const_cast<std::valarray<Coord>&>(c_)[0], &a.c_[0], &b.c_[0], order());
00273         return std::pair<Bezier, Bezier >(a, b);
00274     }
00275 
00276     std::vector<double> roots() const {
00277         std::vector<double> solutions;
00278         find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, 0.0, 1.0);
00279         return solutions;
00280     }
00281     std::vector<double> roots(Interval const ivl) const {
00282         std::vector<double> solutions;
00283         find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl[0], ivl[1]);
00284         return solutions;
00285     }
00286 };
00287 
00288 
00289 void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
00290 
00291 
00292 inline
00293 SBasis Bezier::toSBasis() const {
00294     SBasis sb;
00295     bezier_to_sbasis(sb, (*this));
00296     return sb;
00297     //return bezier_to_sbasis(&c_[0], order());
00298 }
00299 
00300 //TODO: implement others
00301 inline Bezier operator+(const Bezier & a, double v) {
00302     Bezier result = Bezier(Bezier::Order(a));
00303     for(unsigned i = 0; i <= a.order(); i++)
00304         result[i] = a[i] + v;
00305     return result;
00306 }
00307 
00308 inline Bezier operator-(const Bezier & a, double v) {
00309     Bezier result = Bezier(Bezier::Order(a));
00310     for(unsigned i = 0; i <= a.order(); i++)
00311         result[i] = a[i] - v;
00312     return result;
00313 }
00314 
00315 inline Bezier operator*(const Bezier & a, double v) {
00316     Bezier result = Bezier(Bezier::Order(a));
00317     for(unsigned i = 0; i <= a.order(); i++)
00318         result[i] = a[i] * v;
00319     return result;
00320 }
00321 
00322 inline Bezier operator/(const Bezier & a, double v) {
00323     Bezier result = Bezier(Bezier::Order(a));
00324     for(unsigned i = 0; i <= a.order(); i++)
00325         result[i] = a[i] / v;
00326     return result;
00327 }
00328 
00329 inline Bezier reverse(const Bezier & a) {
00330     Bezier result = Bezier(Bezier::Order(a));
00331     for(unsigned i = 0; i <= a.order(); i++)
00332         result[i] = a[a.order() - i];
00333     return result;
00334 }
00335 
00336 inline Bezier portion(const Bezier & a, double from, double to) {
00337     //TODO: implement better?
00338     std::valarray<Coord> res(a.order() + 1);
00339     if(from == 0) {
00340         if(to == 1) { return Bezier(a); }
00341         subdivideArr(to, &const_cast<Bezier&>(a).c_[0], &res[0], NULL, a.order());
00342         return Bezier(&res[0], a.order());
00343     }
00344     subdivideArr(from, &const_cast<Bezier&>(a).c_[0], NULL, &res[0], a.order());
00345     if(to == 1) return Bezier(&res[0], a.order());
00346     std::valarray<Coord> res2(a.order()+1);
00347     subdivideArr((to - from)/(1 - from), &res[0], &res2[0], NULL, a.order());
00348     return Bezier(&res2[0], a.order());
00349 }
00350 
00351 // XXX Todo: how to handle differing orders
00352 inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
00353     std::vector<Point> result;
00354     for(unsigned i = 0; i <= a[0].order(); i++) {
00355         Point p;
00356         for(unsigned d = 0; d < 2; d++) p[d] = a[d][i];
00357         result.push_back(p);
00358     }
00359     return result;
00360 }
00361 
00362 inline Bezier derivative(const Bezier & a) {
00363     //if(a.order() == 1) return Bezier(0.0);
00364     if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]);
00365     Bezier der(Bezier::Order(a.order()-1));
00366 
00367     for(unsigned i = 0; i < a.order(); i++) {
00368         der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]);
00369     }
00370     return der;
00371 }
00372 
00373 inline Bezier integral(const Bezier & a) {
00374     Bezier inte(Bezier::Order(a.order()+1));
00375 
00376     inte[0] = 0;
00377     for(unsigned i = 0; i < inte.order(); i++) {
00378         inte[i+1] = inte[i] + a[i]/(inte.order());
00379     }
00380     return inte;
00381 }
00382 
00383 inline OptInterval bounds_fast(Bezier const & b) {
00384     return Interval::fromArray(&const_cast<Bezier&>(b).c_[0], b.size());
00385 }
00386 
00387 //TODO: better bounds exact
00388 inline OptInterval bounds_exact(Bezier const & b) {
00389     return bounds_exact(b.toSBasis());
00390 }
00391 
00392 inline OptInterval bounds_local(Bezier const & b, OptInterval i) {
00393     //return bounds_local(b.toSBasis(), i);
00394     if (i) {
00395         return bounds_fast(portion(b, i->min(), i->max()));
00396     } else {
00397         return OptInterval();
00398     }
00399 }
00400 
00401 inline std::ostream &operator<< (std::ostream &out_file, const Bezier & b) {
00402     for(unsigned i = 0; i < b.size(); i++) {
00403         out_file << b[i] << ", ";
00404     }
00405     return out_file;
00406 }
00407 
00408 }
00409 #endif //SEEN_BEZIER_H
00410 
00411 /*
00412   Local Variables:
00413   mode:c++
00414   c-file-style:"stroustrup"
00415   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
00416   indent-tabs-mode:nil
00417   fill-column:99
00418   End:
00419 */
00420 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :