Code

add missing files
[inkscape.git] / src / 2geom / solve-bezier-one-d.cpp
1 #include <2geom/solver.h>
2 #include <2geom/point.h>
3 #include <algorithm>
5 /*** Find the zeros of the bernstein function.  The code subdivides until it is happy with the
6  * linearity of the function.  This requires an O(degree^2) subdivision for each step, even when
7  * there is only one solution.
8  */
10 namespace Geom{
12 template<class t>
13 static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); } 
15 const unsigned MAXDEPTH = 23; // Maximum depth for recursion.  Using floats means 23 bits precision max
17 const double BEPSILON = ldexp(1.0,(-MAXDEPTH-1)); /*Flatness control value */
18 const double SECANT_EPSILON = 1e-13; // secant method converges much faster, get a bit more precision
19 /**
20  * This function is called _a lot_.  We have included various manual memory management stuff to reduce the amount of mallocing that goes on.  In the future it is possible that this will hurt performance.
21  **/
22 class Bernsteins{
23 public:
24     double *Vtemp;
25     unsigned N,degree;
26     std::vector<double> &solutions;
27     bool use_secant;
28     Bernsteins(int degr, std::vector<double> &so) : N(degr+1), degree(degr),solutions(so), use_secant(false) {
29         Vtemp = new double[N*2];
30     }
31     ~Bernsteins() {
32         delete[] Vtemp;
33     }
34     void subdivide(double const *V,
35                    double t,
36                    double *Left,
37                    double *Right);
39     double horner(const double *b, double t);
40     
41     unsigned 
42     control_poly_flat_enough(double const *V);
44     void
45     find_bernstein_roots(double const *w, /* The control points  */
46                          unsigned depth,  /* The depth of the recursion */
47                          double left_t, double right_t);
48 };
49 /*
50  *  find_bernstein_roots : Given an equation in Bernstein-Bernstein form, find all 
51  *    of the roots in the open interval (0, 1).  Return the number of roots found.
52  */
53 void
54 find_bernstein_roots(double const *w, /* The control points  */
55                      unsigned degree,   /* The degree of the polynomial */
56                      std::vector<double> &solutions, /* RETURN candidate t-values */
57                      unsigned depth,    /* The depth of the recursion */
58                      double left_t, double right_t, bool use_secant)
59 {  
60     Bernsteins B(degree, solutions);
61     B.use_secant = use_secant;
62     B.find_bernstein_roots(w, depth, left_t, right_t);
63 }
65 void
66 Bernsteins::find_bernstein_roots(double const *w, /* The control points  */
67                      unsigned depth,    /* The depth of the recursion */
68                      double left_t, double right_t) 
69 {
71     unsigned    n_crossings = 0;        /*  Number of zero-crossings */
72     
73     int old_sign = SGN(w[0]);
74     for (unsigned i = 1; i < N; i++) {
75         int sign = SGN(w[i]);
76         if (sign) {
77             if (sign != old_sign && old_sign) {
78                n_crossings++;
79             }
80             old_sign = sign;
81         }
82     }
83     
84     if (n_crossings == 0) // no solutions here
85         return;
86         
87     if (n_crossings == 1) {
88         /* Unique solution      */
89         /* Stop recursion when the tree is deep enough  */
90         /* if deep enough, return 1 solution at midpoint  */
91         if (depth >= MAXDEPTH) {
92             //printf("bottom out %d\n", depth);
93             const double Ax = right_t - left_t;
94             const double Ay = w[degree] - w[0];
95             
96             solutions.push_back(left_t - Ax*w[0] / Ay);
97             return;
98             solutions.push_back((left_t + right_t) / 2.0);
99             return;
100         }
101         
102         // I thought secant method would be faster here, but it'aint. -- njh
103         // Actually, it was, I just was using the wrong method for bezier evaluation.  Horner's rule results in a very efficient algorithm - 10* faster (20080816)
104         // Future work: try using brent's method
105         if(use_secant) { // false position
106             double s = 0;double t = 1;
107             double e = 1e-10;
108             int n,side=0;
109             double r,fr,fs = w[0],ft = w[degree];
110  
111             for (n = 1; n <= 100; n++)
112             {
113                 r = (fs*t - ft*s) / (fs - ft);
114                 if (fabs(t-s) < e*fabs(t+s)) break;
115                 fr = horner(w, r);
116  
117                 if (fr * ft > 0)
118                 {
119                     t = r; ft = fr;
120                     if (side==-1) fs /= 2;
121                     side = -1;
122                 }
123                 else if (fs * fr > 0)
124                 {
125                     s = r;  fs = fr;
126                     if (side==+1) ft /= 2;
127                     side = +1;
128                 }
129                 else break;
130             }
131             solutions.push_back(r*right_t + (1-r)*left_t);
132             return;
133         }
134     }
136     /* Otherwise, solve recursively after subdividing control polygon  */
137     double Left[N],     /* New left and right  */
138            Right[N];    /* control polygons  */
139     const double t = 0.5;
142 /*
143  *  Bernstein : 
144  *      Evaluate a Bernstein function at a particular parameter value
145  *      Fill in control points for resulting sub-curves.
146  * 
147  */
148     for (unsigned i = 0; i < N; i++)
149         Vtemp[i] = w[i];
151     /* Triangle computation     */
152     const double omt = (1-t);
153     Left[0] = Vtemp[0];
154     Right[degree] = Vtemp[degree];
155     double *prev_row = Vtemp;
156     double *row = Vtemp + N;
157     for (unsigned i = 1; i < N; i++) {
158         for (unsigned j = 0; j < N - i; j++) {
159             row[j] = omt*prev_row[j] + t*prev_row[j+1];
160         }
161         Left[i] = row[0];
162         Right[degree-i] = row[degree-i];
163         std::swap(prev_row, row);
164     }
165     
166     double mid_t = left_t*(1-t) + right_t*t;
167     
168     find_bernstein_roots(Left, depth+1, left_t, mid_t);
169             
170     /* Solution is exactly on the subdivision point. */
171     if (Right[0] == 0)
172         solutions.push_back(mid_t);
173         
174     find_bernstein_roots(Right, depth+1, mid_t, right_t);
177 /*
178  *  control_poly_flat_enough :
179  *      Check if the control polygon of a Bernstein curve is flat enough
180  *      for recursive subdivision to bottom out.
181  *
182  */
183 unsigned 
184 Bernsteins::control_poly_flat_enough(double const *V)
186     /* Find the perpendicular distance from each interior control point to line connecting V[0] and
187      * V[degree] */
189     /* Derive the implicit equation for line connecting first */
190     /*  and last control points */
191     const double a = V[0] - V[degree];
193     double max_distance_above = 0.0;
194     double max_distance_below = 0.0;
195     double ii = 0, dii = 1./degree;
196     for (unsigned i = 1; i < degree; i++) {
197         ii += dii;
198         /* Compute distance from each of the points to that line */
199         const double d = (a + V[i]) * ii  - a;
200         double dist = d*d;
201     // Find the largest distance
202         if (d < 0.0)
203             max_distance_below = std::min(max_distance_below, -dist);
204         else
205             max_distance_above = std::max(max_distance_above, dist);
206     }
207     
208     const double abSquared = 1./((a * a) + 1);
210     const double intercept_1 = (a - max_distance_above * abSquared);
211     const double intercept_2 = (a - max_distance_below * abSquared);
213     /* Compute bounding interval*/
214     const double left_intercept = std::min(intercept_1, intercept_2);
215     const double right_intercept = std::max(intercept_1, intercept_2);
217     const double error = 0.5 * (right_intercept - left_intercept);
218     //printf("error %g %g %g\n", error, a, BEPSILON * a);
219     return error < BEPSILON * a;
222 // suggested by Sederberg.
223 double Bernsteins::horner(const double *b, double t) {
224     int n = degree;
225     double u, bc, tn, tmp;
226     int i;
227     u = 1.0 - t;
228     bc = 1;
229     tn = 1;
230     tmp = b[0]*u;
231     for(i=1; i<n; i++){
232         tn = tn*t;
233         bc = bc*(n-i+1)/i;
234         tmp = (tmp + tn*bc*b[i])*u;
235     }
236     return (tmp + tn*t*b[n]);
240 };
242 /*
243   Local Variables:
244   mode:c++
245   c-file-style:"stroustrup"
246   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
247   indent-tabs-mode:nil
248   fill-column:99
249   End:
250 */
251 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :