X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=ETL%2Ftrunk%2FETL%2F_bezier.h;fp=ETL%2Ftrunk%2FETL%2F_bezier.h;h=d693fa4c97a1499fb3060cec28a595f7e82805a4;hb=b3016b249333ac0ab0008d8c6c4d9029b2ff30c9;hp=0000000000000000000000000000000000000000;hpb=7f4273493da74d7d5746a65551e20c7786d0f155;p=synfig.git diff --git a/ETL/trunk/ETL/_bezier.h b/ETL/trunk/ETL/_bezier.h new file mode 100644 index 0000000..d693fa4 --- /dev/null +++ b/ETL/trunk/ETL/_bezier.h @@ -0,0 +1,641 @@ +/*! ======================================================================== +** Extended Template Library +** Bezier Template Class Implementation +** $Id: _bezier.h,v 1.1.1.1 2005/01/04 01:31:46 darco Exp $ +** +** Copyright (c) 2002 Robert B. Quattlebaum Jr. +** +** This package is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License as +** published by the Free Software Foundation; either version 2 of +** the License, or (at your option) any later version. +** +** This package is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** === N O T E S =========================================================== +** +** This is an internal header file, included by other ETL headers. +** You should not attempt to use it directly. +** +** ========================================================================= */ + +/* === S T A R T =========================================================== */ + +#ifndef __ETL_BEZIER_H +#define __ETL_BEZIER_H + +/* === H E A D E R S ======================================================= */ + +#include "_curve_func.h" +#include + +/* === M A C R O S ========================================================= */ + +/* === T Y P E D E F S ===================================================== */ + +/* === C L A S S E S & S T R U C T S ======================================= */ + +_ETL_BEGIN_NAMESPACE + +template class bezier; + +//! Cubic Bezier Curve Base Class +// This generic implementation uses the DeCasteljau algorithm. +// Works for just about anything that has an affine combination function +template +class bezier_base : public std::unary_function +{ +public: + typedef V value_type; + typedef T time_type; + +private: + value_type a,b,c,d; + time_type r,s; + +protected: + affine_combo affine_func; + +public: + bezier_base():r(0.0),s(1.0) { } + bezier_base( + const value_type &a, const value_type &b, const value_type &c, const value_type &d, + const time_type &r=0.0, const time_type &s=1.0): + a(a),b(b),c(c),d(d),r(r),s(s) { sync(); } + + void sync() + { + } + + value_type + operator()(time_type t)const + { + t=(t-r)/(s-r); + return + affine_func( + affine_func( + affine_func(a,b,t), + affine_func(b,c,t) + ,t), + affine_func( + affine_func(b,c,t), + affine_func(c,d,t) + ,t) + ,t); + } + + /* + void evaluate(time_type t, value_type &f, value_type &df) const + { + t=(t-r)/(s-r); + + value_type p1 = affine_func( + affine_func(a,b,t), + affine_func(b,c,t) + ,t); + value_type p2 = affine_func( + affine_func(b,c,t), + affine_func(c,d,t) + ,t); + + f = affine_func(p1,p2,t); + df = (p2-p1)*3; + } + */ + + void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; } + void set_r(time_type new_r) { r=new_r; } + void set_s(time_type new_s) { s=new_s; } + const time_type &get_r()const { return r; } + const time_type &get_s()const { return s; } + time_type get_dt()const { return s-r; } + + bool intersect_hull(const bezier_base &x)const + { + return 0; + } + + //! Bezier curve intersection function + /*! Calculates the time of intersection + ** for the calling curve. + ** + ** I still have not figured out a good generic + ** method of doing this for a bi-infinite + ** cubic bezier curve calculated with the DeCasteljau + ** algorithm. + ** + ** One method, although it does not work for the + ** entire bi-infinite curve, is to iteratively + ** intersect the hulls. However, we would only detect + ** intersections that occur between R and S. + ** + ** It is entirely possible that a new construct similar + ** to the affine combination function will be necessary + ** for this to work properly. + ** + ** For now, this function is BROKEN. (although it works + ** for the floating-point specializations, using newton's method) + */ + time_type intersect(const bezier_base &x, time_type near=0.0)const + { + return 0; + } + + /* subdivide at some time t into 2 separate curves left and right + + b0 l1 + * 0+1 l2 + b1 * 1+2*1+2 l3 + * 1+2 * 0+3*1+3*2+3 l4,r1 + b2 * 1+2*2+2 r2 * + * 2+3 r3 * + b3 r4 * + * + + 0.1 2.3 -> 0.1 2 3 4 5.6 + */ +/* void subdivide(bezier_base *left, bezier_base *right, const time_type &time = (time_type)0.5) const + { + time_type t = (time-r)/(s-r); + bezier_base lt,rt; + + value_type temp; + + //1st stage points to keep + lt.a = a; + rt.d = d; + + //2nd stage calc + lt.b = affine_func(a,b,t); + temp = affine_func(b,c,t); + rt.c = affine_func(c,d,t); + + //3rd stage calc + lt.c = affine_func(lt.b,temp,t); + rt.b = affine_func(temp,rt.c,t); + + //last stage calc + lt.d = rt.a = affine_func(lt.c,rt.b,t); + + //set the time range for l,r (the inside values should be 1, 0 respectively) + lt.r = r; + rt.s = s; + + //give back the curves + if(left) *left = lt; + if(right) *right = rt; + } + */ + value_type & + operator[](int i) + { return (&a)[i]; } + + const value_type & + operator[](int i) const + { return (&a)[i]; } +}; + + +#if 1 +// Fast float implementation of a cubic bezier curve +template <> +class bezier_base : public std::unary_function +{ +public: + typedef float value_type; + typedef float time_type; +private: + affine_combo affine_func; + value_type a,b,c,d; + time_type r,s; + + value_type _coeff[4]; + time_type drs; // reciprocal of (s-r) +public: + bezier_base():r(0.0),s(1.0),drs(1.0) { } + bezier_base( + const value_type &a, const value_type &b, const value_type &c, const value_type &d, + const time_type &r=0.0, const time_type &s=1.0): + a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); } + + void sync() + { +// drs=1.0/(s-r); + _coeff[0]= a; + _coeff[1]= b*3 - a*3; + _coeff[2]= c*3 - b*6 + a*3; + _coeff[3]= d - c*3 + b*3 - a; + } + + // Cost Summary: 4 products, 3 sums, and 1 difference. + inline value_type + operator()(time_type t)const + { t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; } + + void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=1.0/(s-r); } + void set_r(time_type new_r) { r=new_r; drs=1.0/(s-r); } + void set_s(time_type new_s) { s=new_s; drs=1.0/(s-r); } + const time_type &get_r()const { return r; } + const time_type &get_s()const { return s; } + time_type get_dt()const { return s-r; } + + //! Bezier curve intersection function + /*! Calculates the time of intersection + ** for the calling curve. + */ + time_type intersect(const bezier_base &x, time_type t=0.0,int i=15)const + { + //BROKEN - the time values of the 2 curves should be independent + value_type system[4]; + system[0]=_coeff[0]-x._coeff[0]; + system[1]=_coeff[1]-x._coeff[1]; + system[2]=_coeff[2]-x._coeff[2]; + system[3]=_coeff[3]-x._coeff[3]; + + t-=r; + t*=drs; + + // Newton's method + // Inner loop cost summary: 7 products, 5 sums, 1 difference + for(;i;i--) + t-= (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/ + (system[1]+(system[2]*2+(system[3]*3)*t)*t); + + t*=(s-r); + t+=r; + + return t; + } + + value_type & + operator[](int i) + { return (&a)[i]; } + + const value_type & + operator[](int i) const + { return (&a)[i]; } +}; + + +// Fast double implementation of a cubic bezier curve +template <> +class bezier_base : public std::unary_function +{ +public: + typedef double value_type; + typedef float time_type; +private: + affine_combo affine_func; + value_type a,b,c,d; + time_type r,s; + + value_type _coeff[4]; + time_type drs; // reciprocal of (s-r) +public: + bezier_base():r(0.0),s(1.0),drs(1.0) { } + bezier_base( + const value_type &a, const value_type &b, const value_type &c, const value_type &d, + const time_type &r=0.0, const time_type &s=1.0): + a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); } + + void sync() + { +// drs=1.0/(s-r); + _coeff[0]= a; + _coeff[1]= b*3 - a*3; + _coeff[2]= c*3 - b*6 + a*3; + _coeff[3]= d - c*3 + b*3 - a; + } + + // 4 products, 3 sums, and 1 difference. + inline value_type + operator()(time_type t)const + { t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; } + + void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=1.0/(s-r); } + void set_r(time_type new_r) { r=new_r; drs=1.0/(s-r); } + void set_s(time_type new_s) { s=new_s; drs=1.0/(s-r); } + const time_type &get_r()const { return r; } + const time_type &get_s()const { return s; } + time_type get_dt()const { return s-r; } + + //! Bezier curve intersection function + /*! Calculates the time of intersection + ** for the calling curve. + */ + time_type intersect(const bezier_base &x, time_type t=0.0,int i=15)const + { + //BROKEN - the time values of the 2 curves should be independent + value_type system[4]; + system[0]=_coeff[0]-x._coeff[0]; + system[1]=_coeff[1]-x._coeff[1]; + system[2]=_coeff[2]-x._coeff[2]; + system[3]=_coeff[3]-x._coeff[3]; + + t-=r; + t*=drs; + + // Newton's method + // Inner loop: 7 products, 5 sums, 1 difference + for(;i;i--) + t-= (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/ + (system[1]+(system[2]*2+(system[3]*3)*t)*t); + + t*=(s-r); + t+=r; + + return t; + } + + value_type & + operator[](int i) + { return (&a)[i]; } + + const value_type & + operator[](int i) const + { return (&a)[i]; } +}; + +//#ifdef __FIXED__ + +// Fast double implementation of a cubic bezier curve +/* +template <> +template +class bezier_base > : std::unary_function,fixed_base > +{ +public: + typedef fixed_base value_type; + typedef fixed_base time_type; + +private: + affine_combo affine_func; + value_type a,b,c,d; + time_type r,s; + + value_type _coeff[4]; + time_type drs; // reciprocal of (s-r) +public: + bezier_base():r(0.0),s(1.0),drs(1.0) { } + bezier_base( + const value_type &a, const value_type &b, const value_type &c, const value_type &d, + const time_type &r=0, const time_type &s=1): + a(a),b(b),c(c),d(d),r(r),s(s),drs(1.0/(s-r)) { sync(); } + + void sync() + { + drs=time_type(1)/(s-r); + _coeff[0]= a; + _coeff[1]= b*3 - a*3; + _coeff[2]= c*3 - b*6 + a*3; + _coeff[3]= d - c*3 + b*3 - a; + } + + // 4 products, 3 sums, and 1 difference. + inline value_type + operator()(time_type t)const + { t-=r; t*=drs; return _coeff[0]+(_coeff[1]+(_coeff[2]+(_coeff[3])*t)*t)*t; } + + void set_rs(time_type new_r, time_type new_s) { r=new_r; s=new_s; drs=time_type(1)/(s-r); } + void set_r(time_type new_r) { r=new_r; drs=time_type(1)/(s-r); } + void set_s(time_type new_s) { s=new_s; drs=time_type(1)/(s-r); } + const time_type &get_r()const { return r; } + const time_type &get_s()const { return s; } + time_type get_dt()const { return s-r; } + + //! Bezier curve intersection function + //! Calculates the time of intersection + // for the calling curve. + // + time_type intersect(const bezier_base &x, time_type t=0,int i=15)const + { + value_type system[4]; + system[0]=_coeff[0]-x._coeff[0]; + system[1]=_coeff[1]-x._coeff[1]; + system[2]=_coeff[2]-x._coeff[2]; + system[3]=_coeff[3]-x._coeff[3]; + + t-=r; + t*=drs; + + // Newton's method + // Inner loop: 7 products, 5 sums, 1 difference + for(;i;i--) + t-=(time_type) ( (system[0]+(system[1]+(system[2]+(system[3])*t)*t)*t)/ + (system[1]+(system[2]*2+(system[3]*3)*t)*t) ); + + t*=(s-r); + t+=r; + + return t; + } + + value_type & + operator[](int i) + { return (&a)[i]; } + + const value_type & + operator[](int i) const + { return (&a)[i]; } +}; +*/ +//#endif + +#endif + + + +template +class bezier_iterator +{ +public: + + struct iterator_category {}; + typedef V value_type; + typedef T difference_type; + typedef V reference; + +private: + difference_type t; + difference_type dt; + bezier_base curve; + +public: + +/* + reference + operator*(void)const { return curve(t); } + const surface_iterator& + + operator++(void) + { t+=dt; return &this; } + + const surface_iterator& + operator++(int) + { hermite_iterator _tmp=*this; t+=dt; return _tmp; } + + const surface_iterator& + operator--(void) + { t-=dt; return &this; } + + const surface_iterator& + operator--(int) + { hermite_iterator _tmp=*this; t-=dt; return _tmp; } + + + surface_iterator + operator+(difference_type __n) const + { return surface_iterator(data+__n[0]+__n[1]*pitch,pitch); } + + surface_iterator + operator-(difference_type __n) const + { return surface_iterator(data-__n[0]-__n[1]*pitch,pitch); } +*/ + +}; + +template +class bezier : public bezier_base +{ +public: + typedef V value_type; + typedef T time_type; + typedef float distance_type; + typedef bezier_iterator iterator; + typedef bezier_iterator const_iterator; + + distance_func dist; + + using bezier_base::get_r; + using bezier_base::get_s; + using bezier_base::get_dt; + +public: + bezier() { } + bezier(const value_type &a, const value_type &b, const value_type &c, const value_type &d): + bezier_base(a,b,c,d) { } + + + const_iterator begin()const; + const_iterator end()const; + + time_type find_closest(const value_type& x, int i=7, time_type r=(0), time_type s=(1))const + { + float t((r+s)*0.5); + for(;i;i--) + { + if(dist(operator()((s-r)*(1.0/3.0)+r),x) < dist(operator()((s-r)*(2.0/3.0)+r),x)) + s=t; + else + r=t; + t=((r+s)*0.5); + } + return t; + } + + + distance_type find_distance(time_type r, time_type s, int steps=7)const + { + const time_type inc((s-r)/steps); + distance_type ret(0); + value_type last(operator()(r)); + + for(r+=inc;r 0.1 2 3 4 5.6 + */ + void subdivide(bezier *left, bezier *right, const time_type &time = (time_type)0.5) const + { + time_type t=(t-get_r())/get_dt(); + bezier lt,rt; + + value_type temp; + const value_type& a((*this)[0]); + const value_type& b((*this)[1]); + const value_type& c((*this)[2]); + const value_type& d((*this)[3]); + + //1st stage points to keep + lt[0] = a; + rt[3] = d; + + //2nd stage calc + lt[1] = affine_func(a,b,t); + temp = affine_func(b,c,t); + rt[2] = affine_func(c,d,t); + + //3rd stage calc + lt[2] = affine_func(lt[1],temp,t); + rt[1] = affine_func(temp,rt[2],t); + + //last stage calc + lt[3] = rt[0] = affine_func(lt[2],rt[1],t); + + //set the time range for l,r (the inside values should be 1, 0 respectively) + lt.set_r(get_r()); + rt.set_s(get_s()); + + lt.sync(); + rt.sync(); + + //give back the curves + if(left) *left = lt; + if(right) *right = rt; + } + + + void evaluate(time_type t, value_type &f, value_type &df) const + { + t=(t-get_r())/get_dt(); + + const value_type& a((*this)[0]); + const value_type& b((*this)[1]); + const value_type& c((*this)[2]); + const value_type& d((*this)[3]); + + const value_type p1 = affine_func( + affine_func(a,b,t), + affine_func(b,c,t) + ,t); + const value_type p2 = affine_func( + affine_func(b,c,t), + affine_func(c,d,t) + ,t); + + f = affine_func(p1,p2,t); + df = (p2-p1)*3; + } +}; + +_ETL_END_NAMESPACE + +/* === E X T E R N S ======================================================= */ + +/* === E N D =============================================================== */ + +#endif