1 #define __SP_BEZIER_UTILS_C__
2 
3 /** \file
4  * Bezier interpolation for inkscape drawing code.
5  */
6 /*
7  * Original code published in:
8  *   An Algorithm for Automatically Fitting Digitized Curves
9  *   by Philip J. Schneider
10  *  "Graphics Gems", Academic Press, 1990
11  *
12  * Authors:
13  *   Philip J. Schneider
14  *   Lauris Kaplinski <lauris@kaplinski.com>
15  *   Peter Moulder <pmoulder@mail.csse.monash.edu.au>
16  *
17  * Copyright (C) 1990 Philip J. Schneider
18  * Copyright (C) 2001 Lauris Kaplinski
19  * Copyright (C) 2001 Ximian, Inc.
20  * Copyright (C) 2003,2004 Monash University
21  *
22  * This library is free software; you can redistribute it and/or
23  * modify it either under the terms of the GNU Lesser General Public
24  * License version 2.1 as published by the Free Software Foundation
25  * (the "LGPL") or, at your option, under the terms of the Mozilla
26  * Public License Version 1.1 (the "MPL"). If you do not alter this
27  * notice, a recipient may use your version of this file under either
28  * the MPL or the LGPL.
29  *
30  * You should have received a copy of the LGPL along with this library
31  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
32  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33  * You should have received a copy of the MPL along with this library
34  * in the file COPYING-MPL-1.1
35  *
36  * The contents of this file are subject to the Mozilla Public License
37  * Version 1.1 (the "License"); you may not use this file except in
38  * compliance with the License. You may obtain a copy of the License at
39  * http://www.mozilla.org/MPL/
40  *
41  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
42  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
43  * the specific language governing rights and limitations.
44  *
45  */
46 
47 #define SP_HUGE 1e5
48 #define noBEZIER_DEBUG
49 
50 #ifdef HAVE_IEEEFP_H
51 # include <ieefp.h>
52 #endif
53 
54 #include "bezier-utils.h"
55 
56 #include "isnan.h"
57 #include <assert.h>
58 
59 namespace Geom{
60 
61 typedef Point BezierCurve[];
62 
63 /* Forward declarations */
64 static void generate_bezier(Point b[], Point const d[], double const u[], unsigned len,
65                             Point const &tHat1, Point const &tHat2, double tolerance_sq);
66 static void estimate_lengths(Point bezier[],
67                              Point const data[], double const u[], unsigned len,
68                              Point const &tHat1, Point const &tHat2);
69 static void estimate_bi(Point b[4], unsigned ei,
70                         Point const data[], double const u[], unsigned len);
71 static void reparameterize(Point const d[], unsigned len, double u[], BezierCurve const bezCurve);
72 static double NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double u);
73 static Point darray_center_tangent(Point const d[], unsigned center, unsigned length);
74 static Point darray_right_tangent(Point const d[], unsigned const len);
75 static unsigned copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[]);
76 static void chord_length_parameterize(Point const d[], double u[], unsigned len);
77 static double compute_max_error_ratio(Point const d[], double const u[], unsigned len,
78                                       BezierCurve const bezCurve, double tolerance,
79                                       unsigned *splitPoint);
80 static double compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
81                            double const tolerance);
82 
83 
84 static Point const unconstrained_tangent(0, 0);
85 
86 
87 /*
88  *  B0, B1, B2, B3 : Bezier multipliers
89  */
90 
91 #define B0(u) ( ( 1.0 - u )  *  ( 1.0 - u )  *  ( 1.0 - u ) )
92 #define B1(u) ( 3 * u  *  ( 1.0 - u )  *  ( 1.0 - u ) )
93 #define B2(u) ( 3 * u * u  *  ( 1.0 - u ) )
94 #define B3(u) ( u * u * u )
95 
96 #ifdef BEZIER_DEBUG
97 # define DOUBLE_ASSERT(x) assert( ( (x) > -SP_HUGE ) && ( (x) < SP_HUGE ) )
98 # define BEZIER_ASSERT(b) do { \
99            DOUBLE_ASSERT((b)[0][X]); DOUBLE_ASSERT((b)[0][Y]);  \
100            DOUBLE_ASSERT((b)[1][X]); DOUBLE_ASSERT((b)[1][Y]);  \
101            DOUBLE_ASSERT((b)[2][X]); DOUBLE_ASSERT((b)[2][Y]);  \
102            DOUBLE_ASSERT((b)[3][X]); DOUBLE_ASSERT((b)[3][Y]);  \
103          } while(0)
104 #else
105 # define DOUBLE_ASSERT(x) do { } while(0)
106 # define BEZIER_ASSERT(b) do { } while(0)
107 #endif
108 
109 
110 /**
111  * Fit a single-segment Bezier curve to a set of digitized points.
112  *
113  * \return Number of segments generated, or -1 on error.
114  */
115 int
bezier_fit_cubic(Point * bezier,Point const * data,int len,double error)116 bezier_fit_cubic(Point *bezier, Point const *data, int len, double error)
117 {
118     return bezier_fit_cubic_r(bezier, data, len, error, 1);
119 }
120 
121 /**
122  * Fit a multi-segment Bezier curve to a set of digitized points, with
123  * possible weedout of identical points and NaNs.
124  *
125  * \param max_beziers Maximum number of generated segments
126  * \param Result array, must be large enough for n. segments * 4 elements.
127  *
128  * \return Number of segments generated, or -1 on error.
129  */
130 int
bezier_fit_cubic_r(Point bezier[],Point const data[],int const len,double const error,unsigned const max_beziers)131 bezier_fit_cubic_r(Point bezier[], Point const data[], int const len, double const error, unsigned const max_beziers)
132 {
133     if(bezier == NULL ||
134        data == NULL ||
135        len <= 0 ||
136        max_beziers >= (1ul << (31 - 2 - 1 - 3)))
137         return -1;
138 
139     Point *uniqued_data = new Point[len];
140     unsigned uniqued_len = copy_without_nans_or_adjacent_duplicates(data, len, uniqued_data);
141 
142     if ( uniqued_len < 2 ) {
143         delete[] uniqued_data;
144         return 0;
145     }
146 
147     /* Call fit-cubic function with recursion. */
148     int const ret = bezier_fit_cubic_full(bezier, NULL, uniqued_data, uniqued_len,
149                                           unconstrained_tangent, unconstrained_tangent,
150                                           error, max_beziers);
151     delete[] uniqued_data;
152     return ret;
153 }
154 
155 /**
156  * Copy points from src to dest, filter out points containing NaN and
157  * adjacent points with equal x and y.
158  * \return length of dest
159  */
160 static unsigned
copy_without_nans_or_adjacent_duplicates(Point const src[],unsigned src_len,Point dest[])161 copy_without_nans_or_adjacent_duplicates(Point const src[], unsigned src_len, Point dest[])
162 {
163     unsigned si = 0;
164     for (;;) {
165         if ( si == src_len ) {
166             return 0;
167         }
168         if (!is_nan(src[si][X]) &&
169             !is_nan(src[si][Y])) {
170             dest[0] = Point(src[si]);
171             ++si;
172             break;
173         }
174     }
175     unsigned di = 0;
176     for (; si < src_len; ++si) {
177         Point const src_pt = Point(src[si]);
178         if ( src_pt != dest[di]
179              && !is_nan(src_pt[X])
180              && !is_nan(src_pt[Y])) {
181             dest[++di] = src_pt;
182         }
183     }
184     unsigned dest_len = di + 1;
185     assert( dest_len <= src_len );
186     return dest_len;
187 }
188 
189 /**
190  * Fit a multi-segment Bezier curve to a set of digitized points, without
191  * possible weedout of identical points and NaNs.
192  *
193  * \pre data is uniqued, i.e. not exist i: data[i] == data[i + 1].
194  * \param max_beziers Maximum number of generated segments
195  * \param Result array, must be large enough for n. segments * 4 elements.
196  */
197 int
bezier_fit_cubic_full(Point bezier[],int split_points[],Point const data[],int const len,Point const & tHat1,Point const & tHat2,double const error,unsigned const max_beziers)198 bezier_fit_cubic_full(Point bezier[], int split_points[],
199                       Point const data[], int const len,
200                       Point const &tHat1, Point const &tHat2,
201                       double const error, unsigned const max_beziers)
202 {
203     int const maxIterations = 4;   /* std::max times to try iterating */
204 
205     if(!(bezier != NULL) ||
206        !(data != NULL) ||
207        !(len > 0) ||
208        !(max_beziers >= 1) ||
209        !(error >= 0.0))
210         return -1;
211 
212     if ( len < 2 ) return 0;
213 
214     if ( len == 2 ) {
215         /* We have 2 points, which can be fitted trivially. */
216         bezier[0] = data[0];
217         bezier[3] = data[len - 1];
218         double const dist = distance(bezier[0], bezier[3]) / 3.0;
219         if (is_nan(dist)) {
220             /* Numerical problem, fall back to straight line segment. */
221             bezier[1] = bezier[0];
222             bezier[2] = bezier[3];
223         } else {
224             bezier[1] = ( is_zero(tHat1)
225                           ? ( 2 * bezier[0] + bezier[3] ) / 3.
226                           : bezier[0] + dist * tHat1 );
227             bezier[2] = ( is_zero(tHat2)
228                           ? ( bezier[0] + 2 * bezier[3] ) / 3.
229                           : bezier[3] + dist * tHat2 );
230         }
231         BEZIER_ASSERT(bezier);
232         return 1;
233     }
234 
235     /*  Parameterize points, and attempt to fit curve */
236     unsigned splitPoint;   /* Point to split point set at. */
237     bool is_corner;
238     {
239         double *u = new double[len];
240         chord_length_parameterize(data, u, len);
241         if ( u[len - 1] == 0.0 ) {
242             /* Zero-length path: every point in data[] is the same.
243              *
244              * (Clients aren't allowed to pass such data; handling the case is defensive
245              * programming.)
246              */
247             delete[] u;
248             return 0;
249         }
250 
251         generate_bezier(bezier, data, u, len, tHat1, tHat2, error);
252         reparameterize(data, len, u, bezier);
253 
254         /* Find max deviation of points to fitted curve. */
255         double const tolerance = sqrt(error + 1e-9);
256         double maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint);
257 
258         if ( fabs(maxErrorRatio) <= 1.0 ) {
259             BEZIER_ASSERT(bezier);
260             delete[] u;
261             return 1;
262         }
263 
264         /* If error not too large, then try some reparameterization and iteration. */
265         if ( 0.0 <= maxErrorRatio && maxErrorRatio <= 3.0 ) {
266             for (int i = 0; i < maxIterations; i++) {
267                 generate_bezier(bezier, data, u, len, tHat1, tHat2, error);
268                 reparameterize(data, len, u, bezier);
269                 maxErrorRatio = compute_max_error_ratio(data, u, len, bezier, tolerance, &splitPoint);
270                 if ( fabs(maxErrorRatio) <= 1.0 ) {
271                     BEZIER_ASSERT(bezier);
272                     delete[] u;
273                     return 1;
274                 }
275             }
276         }
277         delete[] u;
278         is_corner = (maxErrorRatio < 0);
279     }
280 
281     if (is_corner) {
282         assert(splitPoint < unsigned(len));
283         if (splitPoint == 0) {
284             if (is_zero(tHat1)) {
285                 /* Got spike even with unconstrained initial tangent. */
286                 ++splitPoint;
287             } else {
288                 return bezier_fit_cubic_full(bezier, split_points, data, len, unconstrained_tangent, tHat2,
289                                                 error, max_beziers);
290             }
291         } else if (splitPoint == unsigned(len - 1)) {
292             if (is_zero(tHat2)) {
293                 /* Got spike even with unconstrained final tangent. */
294                 --splitPoint;
295             } else {
296                 return bezier_fit_cubic_full(bezier, split_points, data, len, tHat1, unconstrained_tangent,
297                                                 error, max_beziers);
298             }
299         }
300     }
301 
302     if ( 1 < max_beziers ) {
303         /*
304          *  Fitting failed -- split at max error point and fit recursively
305          */
306         unsigned const rec_max_beziers1 = max_beziers - 1;
307 
308         Point recTHat2, recTHat1;
309         if (is_corner) {
310             if(!(0 < splitPoint && splitPoint < unsigned(len - 1)))
311                return -1;
312             recTHat1 = recTHat2 = unconstrained_tangent;
313         } else {
314             /* Unit tangent vector at splitPoint. */
315             recTHat2 = darray_center_tangent(data, splitPoint, len);
316             recTHat1 = -recTHat2;
317         }
318         int const nsegs1 = bezier_fit_cubic_full(bezier, split_points, data, splitPoint + 1,
319                                                      tHat1, recTHat2, error, rec_max_beziers1);
320         if ( nsegs1 < 0 ) {
321 #ifdef BEZIER_DEBUG
322             g_print("fit_cubic[1]: recursive call failed\n");
323 #endif
324             return -1;
325         }
326         assert( nsegs1 != 0 );
327         if (split_points != NULL) {
328             split_points[nsegs1 - 1] = splitPoint;
329         }
330         unsigned const rec_max_beziers2 = max_beziers - nsegs1;
331         int const nsegs2 = bezier_fit_cubic_full(bezier + nsegs1*4,
332                                                      ( split_points == NULL
333                                                        ? NULL
334                                                        : split_points + nsegs1 ),
335                                                      data + splitPoint, len - splitPoint,
336                                                      recTHat1, tHat2, error, rec_max_beziers2);
337         if ( nsegs2 < 0 ) {
338 #ifdef BEZIER_DEBUG
339             g_print("fit_cubic[2]: recursive call failed\n");
340 #endif
341             return -1;
342         }
343 
344 #ifdef BEZIER_DEBUG
345         g_print("fit_cubic: success[nsegs: %d+%d=%d] on max_beziers:%u\n",
346                 nsegs1, nsegs2, nsegs1 + nsegs2, max_beziers);
347 #endif
348         return nsegs1 + nsegs2;
349     } else {
350         return -1;
351     }
352 }
353 
354 
355 /**
356  * Fill in \a bezier[] based on the given data and tangent requirements, using
357  * a least-squares fit.
358  *
359  * Each of tHat1 and tHat2 should be either a zero vector or a unit vector.
360  * If it is zero, then bezier[1 or 2] is estimated without constraint; otherwise,
361  * it bezier[1 or 2] is placed in the specified direction from bezier[0 or 3].
362  *
363  * \param tolerance_sq Used only for an initial guess as to tangent directions
364  *   when \a tHat1 or \a tHat2 is zero.
365  */
366 static void
generate_bezier(Point bezier[],Point const data[],double const u[],unsigned const len,Point const & tHat1,Point const & tHat2,double const tolerance_sq)367 generate_bezier(Point bezier[],
368                 Point const data[], double const u[], unsigned const len,
369                 Point const &tHat1, Point const &tHat2,
370                 double const tolerance_sq)
371 {
372     bool const est1 = is_zero(tHat1);
373     bool const est2 = is_zero(tHat2);
374     Point est_tHat1( est1
375                          ? darray_left_tangent(data, len, tolerance_sq)
376                          : tHat1 );
377     Point est_tHat2( est2
378                          ? darray_right_tangent(data, len, tolerance_sq)
379                          : tHat2 );
380     estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2);
381     /* We find that darray_right_tangent tends to produce better results
382        for our current freehand tool than full estimation. */
383     if (est1) {
384         estimate_bi(bezier, 1, data, u, len);
385         if (bezier[1] != bezier[0]) {
386             est_tHat1 = unit_vector(bezier[1] - bezier[0]);
387         }
388         estimate_lengths(bezier, data, u, len, est_tHat1, est_tHat2);
389     }
390 }
391 
392 
393 static void
estimate_lengths(Point bezier[],Point const data[],double const uPrime[],unsigned const len,Point const & tHat1,Point const & tHat2)394 estimate_lengths(Point bezier[],
395                  Point const data[], double const uPrime[], unsigned const len,
396                  Point const &tHat1, Point const &tHat2)
397 {
398     double C[2][2];   /* Matrix C. */
399     double X[2];      /* Matrix X. */
400 
401     /* Create the C and X matrices. */
402     C[0][0] = 0.0;
403     C[0][1] = 0.0;
404     C[1][0] = 0.0;
405     C[1][1] = 0.0;
406     X[0]    = 0.0;
407     X[1]    = 0.0;
408 
409     /* First and last control points of the Bezier curve are positioned exactly at the first and
410        last data points. */
411     bezier[0] = data[0];
412     bezier[3] = data[len - 1];
413 
414     for (unsigned i = 0; i < len; i++) {
415         /* Bezier control point coefficients. */
416         double const b0 = B0(uPrime[i]);
417         double const b1 = B1(uPrime[i]);
418         double const b2 = B2(uPrime[i]);
419         double const b3 = B3(uPrime[i]);
420 
421         /* rhs for eqn */
422         Point const a1 = b1 * tHat1;
423         Point const a2 = b2 * tHat2;
424 
425         C[0][0] += dot(a1, a1);
426         C[0][1] += dot(a1, a2);
427         C[1][0] = C[0][1];
428         C[1][1] += dot(a2, a2);
429 
430         /* Additional offset to the data point from the predicted point if we were to set bezier[1]
431            to bezier[0] and bezier[2] to bezier[3]. */
432         Point const shortfall
433             = ( data[i]
434                 - ( ( b0 + b1 ) * bezier[0] )
435                 - ( ( b2 + b3 ) * bezier[3] ) );
436         X[0] += dot(a1, shortfall);
437         X[1] += dot(a2, shortfall);
438     }
439 
440     /* We've constructed a pair of equations in the form of a matrix product C * alpha = X.
441        Now solve for alpha. */
442     double alpha_l, alpha_r;
443 
444     /* Compute the determinants of C and X. */
445     double const det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
446     if ( det_C0_C1 != 0 ) {
447         /* Apparently Kramer's rule. */
448         double const det_C0_X  = C[0][0] * X[1]    - C[0][1] * X[0];
449         double const det_X_C1  = X[0]    * C[1][1] - X[1]    * C[0][1];
450         alpha_l = det_X_C1 / det_C0_C1;
451         alpha_r = det_C0_X / det_C0_C1;
452     } else {
453         /* The matrix is under-determined.  Try requiring alpha_l == alpha_r.
454          *
455          * One way of implementing the constraint alpha_l == alpha_r is to treat them as the same
456          * variable in the equations.  We can do this by adding the columns of C to form a single
457          * column, to be multiplied by alpha to give the column vector X.
458          *
459          * We try each row in turn.
460          */
461         double const c0 = C[0][0] + C[0][1];
462         if (c0 != 0) {
463             alpha_l = alpha_r = X[0] / c0;
464         } else {
465             double const c1 = C[1][0] + C[1][1];
466             if (c1 != 0) {
467                 alpha_l = alpha_r = X[1] / c1;
468             } else {
469                 /* Let the below code handle this. */
470                 alpha_l = alpha_r = 0.;
471             }
472         }
473     }
474 
475     /* If alpha negative, use the Wu/Barsky heuristic (see text).  (If alpha is 0, you get
476        coincident control points that lead to divide by zero in any subsequent
477        NewtonRaphsonRootFind() call.) */
478     /// \todo Check whether this special-casing is necessary now that
479     /// NewtonRaphsonRootFind handles non-positive denominator.
480     if ( alpha_l < 1.0e-6 ||
481          alpha_r < 1.0e-6   )
482     {
483         alpha_l = alpha_r = distance(data[0], data[len-1]) / 3.0;
484     }
485 
486     /* Control points 1 and 2 are positioned an alpha distance out on the tangent vectors, left and
487        right, respectively. */
488     bezier[1] = alpha_l * tHat1 + bezier[0];
489     bezier[2] = alpha_r * tHat2 + bezier[3];
490 
491     return;
492 }
493 
lensq(Point const p)494 static double lensq(Point const p) {
495     return dot(p, p);
496 }
497 
498 static void
estimate_bi(Point bezier[4],unsigned const ei,Point const data[],double const u[],unsigned const len)499 estimate_bi(Point bezier[4], unsigned const ei,
500             Point const data[], double const u[], unsigned const len)
501 {
502     if(!(1 <= ei && ei <= 2))
503         return;
504     unsigned const oi = 3 - ei;
505     double num[2] = {0., 0.};
506     double den = 0.;
507     for (unsigned i = 0; i < len; ++i) {
508         double const ui = u[i];
509         double const b[4] = {
510             B0(ui),
511             B1(ui),
512             B2(ui),
513             B3(ui)
514         };
515 
516         for (unsigned d = 0; d < 2; ++d) {
517             num[d] += b[ei] * (b[0]  * bezier[0][d] +
518                                b[oi] * bezier[oi][d] +
519                                b[3]  * bezier[3][d] +
520                                - data[i][d]);
521         }
522         den -= b[ei] * b[ei];
523     }
524 
525     if (den != 0.) {
526         for (unsigned d = 0; d < 2; ++d) {
527             bezier[ei][d] = num[d] / den;
528         }
529     } else {
530         bezier[ei] = ( oi * bezier[0] + ei * bezier[3] ) / 3.;
531     }
532 }
533 
534 /**
535  * Given set of points and their parameterization, try to find a better assignment of parameter
536  * values for the points.
537  *
538  *  \param d  Array of digitized points.
539  *  \param u  Current parameter values.
540  *  \param bezCurve  Current fitted curve.
541  *  \param len  Number of values in both d and u arrays.
542  *              Also the size of the array that is allocated for return.
543  */
544 static void
reparameterize(Point const d[],unsigned const len,double u[],BezierCurve const bezCurve)545 reparameterize(Point const d[],
546                unsigned const len,
547                double u[],
548                BezierCurve const bezCurve)
549 {
550     assert( 2 <= len );
551 
552     unsigned const last = len - 1;
553     assert( bezCurve[0] == d[0] );
554     assert( bezCurve[3] == d[last] );
555     assert( u[0] == 0.0 );
556     assert( u[last] == 1.0 );
557     /* Otherwise, consider including 0 and last in the below loop. */
558 
559     for (unsigned i = 1; i < last; i++) {
560         u[i] = NewtonRaphsonRootFind(bezCurve, d[i], u[i]);
561     }
562 }
563 
564 /**
565  *  Use Newton-Raphson iteration to find better root.
566  *
567  *  \param Q  Current fitted curve
568  *  \param P  Digitized point
569  *  \param u  Parameter value for "P"
570  *
571  *  \return Improved u
572  */
573 static double
NewtonRaphsonRootFind(BezierCurve const Q,Point const & P,double const u)574 NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double const u)
575 {
576     assert( 0.0 <= u );
577     assert( u <= 1.0 );
578 
579     /* Generate control vertices for Q'. */
580     Point Q1[3];
581     for (unsigned i = 0; i < 3; i++) {
582         Q1[i] = 3.0 * ( Q[i+1] - Q[i] );
583     }
584 
585     /* Generate control vertices for Q''. */
586     Point Q2[2];
587     for (unsigned i = 0; i < 2; i++) {
588         Q2[i] = 2.0 * ( Q1[i+1] - Q1[i] );
589     }
590 
591     /* Compute Q(u), Q'(u) and Q''(u). */
592     Point const Q_u  = bezier_pt(3, Q, u);
593     Point const Q1_u = bezier_pt(2, Q1, u);
594     Point const Q2_u = bezier_pt(1, Q2, u);
595 
596     /* Compute f(u)/f'(u), where f is the derivative wrt u of distsq(u) = 0.5 * the square of the
597        distance from P to Q(u).  Here we're using Newton-Raphson to find a stationary point in the
598        distsq(u), hopefully corresponding to a local minimum in distsq (and hence a local minimum
599        distance from P to Q(u)). */
600     Point const diff = Q_u - P;
601     double numerator = dot(diff, Q1_u);
602     double denominator = dot(Q1_u, Q1_u) + dot(diff, Q2_u);
603 
604     double improved_u;
605     if ( denominator > 0. ) {
606         /* One iteration of Newton-Raphson:
607            improved_u = u - f(u)/f'(u) */
608         improved_u = u - ( numerator / denominator );
609     } else {
610         /* Using Newton-Raphson would move in the wrong direction (towards a local maximum rather
611            than local minimum), so we move an arbitrary amount in the right direction. */
612         if ( numerator > 0. ) {
613             improved_u = u * .98 - .01;
614         } else if ( numerator < 0. ) {
615             /* Deliberately asymmetrical, to reduce the chance of cycling. */
616             improved_u = .031 + u * .98;
617         } else {
618             improved_u = u;
619         }
620     }
621 
622     if (!is_finite(improved_u)) {
623         improved_u = u;
624     } else if ( improved_u < 0.0 ) {
625         improved_u = 0.0;
626     } else if ( improved_u > 1.0 ) {
627         improved_u = 1.0;
628     }
629 
630     /* Ensure that improved_u isn't actually worse. */
631     {
632         double const diff_lensq = lensq(diff);
633         for (double proportion = .125; ; proportion += .125) {
634             if ( lensq( bezier_pt(3, Q, improved_u) - P ) > diff_lensq ) {
635                 if ( proportion > 1.0 ) {
636                     //g_warning("found proportion %g", proportion);
637                     improved_u = u;
638                     break;
639                 }
640                 improved_u = ( ( 1 - proportion ) * improved_u  +
641                                proportion         * u            );
642             } else {
643                 break;
644             }
645         }
646     }
647 
648     DOUBLE_ASSERT(improved_u);
649     return improved_u;
650 }
651 
652 /**
653  * Evaluate a Bezier curve at parameter value \a t.
654  *
655  * \param degree The degree of the Bezier curve: 3 for cubic, 2 for quadratic etc. Must be less
656  *    than 4.
657  * \param V The control points for the Bezier curve.  Must have (\a degree+1)
658  *    elements.
659  * \param t The "parameter" value, specifying whereabouts along the curve to
660  *    evaluate.  Typically in the range [0.0, 1.0].
661  *
662  * Let s = 1 - t.
663  * BezierII(1, V) gives (s, t) * V, i.e. t of the way
664  * from V[0] to V[1].
665  * BezierII(2, V) gives (s**2, 2*s*t, t**2) * V.
666  * BezierII(3, V) gives (s**3, 3 s**2 t, 3s t**2, t**3) * V.
667  *
668  * The derivative of BezierII(i, V) with respect to t
669  * is i * BezierII(i-1, V'), where for all j, V'[j] =
670  * V[j + 1] - V[j].
671  */
672 Point
bezier_pt(unsigned const degree,Point const V[],double const t)673 bezier_pt(unsigned const degree, Point const V[], double const t)
674 {
675     /** Pascal's triangle. */
676     static int const pascal[4][4] = {{1},
677                                      {1, 1},
678                                      {1, 2, 1},
679                                      {1, 3, 3, 1}};
680     assert( degree < 4);
681     double const s = 1.0 - t;
682 
683     /* Calculate powers of t and s. */
684     double spow[4];
685     double tpow[4];
686     spow[0] = 1.0; spow[1] = s;
687     tpow[0] = 1.0; tpow[1] = t;
688     for (unsigned i = 1; i < degree; ++i) {
689         spow[i + 1] = spow[i] * s;
690         tpow[i + 1] = tpow[i] * t;
691     }
692 
693     Point ret = spow[degree] * V[0];
694     for (unsigned i = 1; i <= degree; ++i) {
695         ret += pascal[degree][i] * spow[degree - i] * tpow[i] * V[i];
696     }
697     return ret;
698 }
699 
700 /*
701  * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent :
702  * Approximate unit tangents at endpoints and "center" of digitized curve
703  */
704 
705 /**
706  * Estimate the (forward) tangent at point d[first + 0.5].
707  *
708  * Unlike the center and right versions, this calculates the tangent in
709  * the way one might expect, i.e., wrt increasing index into d.
710  * \pre (2 \<= len) and (d[0] != d[1]).
711  **/
712 Point
darray_left_tangent(Point const d[],unsigned const len)713 darray_left_tangent(Point const d[], unsigned const len)
714 {
715     assert( len >= 2 );
716     assert( d[0] != d[1] );
717     return unit_vector( d[1] - d[0] );
718 }
719 
720 /**
721  * Estimates the (backward) tangent at d[last - 0.5].
722  *
723  * \note The tangent is "backwards", i.e. it is with respect to
724  * decreasing index rather than increasing index.
725  *
726  * \pre 2 \<= len.
727  * \pre d[len - 1] != d[len - 2].
728  * \pre all[p in d] in_svg_plane(p).
729  */
730 static Point
darray_right_tangent(Point const d[],unsigned const len)731 darray_right_tangent(Point const d[], unsigned const len)
732 {
733     assert( 2 <= len );
734     unsigned const last = len - 1;
735     unsigned const prev = last - 1;
736     assert( d[last] != d[prev] );
737     return unit_vector( d[prev] - d[last] );
738 }
739 
740 /**
741  * Estimate the (forward) tangent at point d[0].
742  *
743  * Unlike the center and right versions, this calculates the tangent in
744  * the way one might expect, i.e., wrt increasing index into d.
745  *
746  * \pre 2 \<= len.
747  * \pre d[0] != d[1].
748  * \pre all[p in d] in_svg_plane(p).
749  * \post is_unit_vector(ret).
750  **/
751 Point
darray_left_tangent(Point const d[],unsigned const len,double const tolerance_sq)752 darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq)
753 {
754     assert( 2 <= len );
755     assert( 0 <= tolerance_sq );
756     for (unsigned i = 1;;) {
757         Point const pi(d[i]);
758         Point const t(pi - d[0]);
759         double const distsq = dot(t, t);
760         if ( tolerance_sq < distsq ) {
761             return unit_vector(t);
762         }
763         ++i;
764         if (i == len) {
765             return ( distsq == 0
766                      ? darray_left_tangent(d, len)
767                      : unit_vector(t) );
768         }
769     }
770 }
771 
772 /**
773  * Estimates the (backward) tangent at d[last].
774  *
775  * \note The tangent is "backwards", i.e. it is with respect to
776  * decreasing index rather than increasing index.
777  *
778  * \pre 2 \<= len.
779  * \pre d[len - 1] != d[len - 2].
780  * \pre all[p in d] in_svg_plane(p).
781  */
782 Point
darray_right_tangent(Point const d[],unsigned const len,double const tolerance_sq)783 darray_right_tangent(Point const d[], unsigned const len, double const tolerance_sq)
784 {
785     assert( 2 <= len );
786     assert( 0 <= tolerance_sq );
787     unsigned const last = len - 1;
788     for (unsigned i = last - 1;; i--) {
789         Point const pi(d[i]);
790         Point const t(pi - d[last]);
791         double const distsq = dot(t, t);
792         if ( tolerance_sq < distsq ) {
793             return unit_vector(t);
794         }
795         if (i == 0) {
796             return ( distsq == 0
797                      ? darray_right_tangent(d, len)
798                      : unit_vector(t) );
799         }
800     }
801 }
802 
803 /**
804  * Estimates the (backward) tangent at d[center], by averaging the two
805  * segments connected to d[center] (and then normalizing the result).
806  *
807  * \note The tangent is "backwards", i.e. it is with respect to
808  * decreasing index rather than increasing index.
809  *
810  * \pre (0 \< center \< len - 1) and d is uniqued (at least in
811  * the immediate vicinity of \a center).
812  */
813 static Point
darray_center_tangent(Point const d[],unsigned const center,unsigned const len)814 darray_center_tangent(Point const d[],
815                          unsigned const center,
816                          unsigned const len)
817 {
818     assert( center != 0 );
819     assert( center < len - 1 );
820 
821     Point ret;
822     if ( d[center + 1] == d[center - 1] ) {
823         /* Rotate 90 degrees in an arbitrary direction. */
824         Point const diff = d[center] - d[center - 1];
825         ret = rot90(diff);
826     } else {
827         ret = d[center - 1] - d[center + 1];
828     }
829     ret.normalize();
830     return ret;
831 }
832 
833 
834 /**
835  *  Assign parameter values to digitized points using relative distances between points.
836  *
837  *  \pre Parameter array u must have space for \a len items.
838  */
839 static void
chord_length_parameterize(Point const d[],double u[],unsigned const len)840 chord_length_parameterize(Point const d[], double u[], unsigned const len)
841 {
842     if(!( 2 <= len ))
843         return;
844 
845     /* First let u[i] equal the distance travelled along the path from d[0] to d[i]. */
846     u[0] = 0.0;
847     for (unsigned i = 1; i < len; i++) {
848         double const dist = distance(d[i], d[i-1]);
849         u[i] = u[i-1] + dist;
850     }
851 
852     /* Then scale to [0.0 .. 1.0]. */
853     double tot_len = u[len - 1];
854     if(!( tot_len != 0 ))
855         return;
856     if (is_finite(tot_len)) {
857         for (unsigned i = 1; i < len; ++i) {
858             u[i] /= tot_len;
859         }
860     } else {
861         /* We could do better, but this probably never happens anyway. */
862         for (unsigned i = 1; i < len; ++i) {
863             u[i] = i / (double) ( len - 1 );
864         }
865     }
866 
867     /** \todo
868      * It's been reported that u[len - 1] can differ from 1.0 on some
869      * systems (amd64), despite it having been calculated as x / x where x
870      * is isFinite and non-zero.
871      */
872     if (u[len - 1] != 1) {
873         double const diff = u[len - 1] - 1;
874         if (fabs(diff) > 1e-13) {
875             assert(0); // No warnings in 2geom
876             //g_warning("u[len - 1] = %19g (= 1 + %19g), expecting exactly 1",
877             //          u[len - 1], diff);
878         }
879         u[len - 1] = 1;
880     }
881 
882 #ifdef BEZIER_DEBUG
883     assert( u[0] == 0.0 && u[len - 1] == 1.0 );
884     for (unsigned i = 1; i < len; i++) {
885         assert( u[i] >= u[i-1] );
886     }
887 #endif
888 }
889 
890 
891 
892 
893 /**
894  * Find the maximum squared distance of digitized points to fitted curve, and (if this maximum
895  * error is non-zero) set \a *splitPoint to the corresponding index.
896  *
897  * \pre 2 \<= len.
898  * \pre u[0] == 0.
899  * \pre u[len - 1] == 1.0.
900  * \post ((ret == 0.0)
901  *        || ((*splitPoint \< len - 1)
902  *            \&\& (*splitPoint != 0 || ret \< 0.0))).
903  */
904 static double
compute_max_error_ratio(Point const d[],double const u[],unsigned const len,BezierCurve const bezCurve,double const tolerance,unsigned * const splitPoint)905 compute_max_error_ratio(Point const d[], double const u[], unsigned const len,
906                         BezierCurve const bezCurve, double const tolerance,
907                         unsigned *const splitPoint)
908 {
909     assert( 2 <= len );
910     unsigned const last = len - 1;
911     assert( bezCurve[0] == d[0] );
912     assert( bezCurve[3] == d[last] );
913     assert( u[0] == 0.0 );
914     assert( u[last] == 1.0 );
915     /* I.e. assert that the error for the first & last points is zero.
916      * Otherwise we should include those points in the below loop.
917      * The assertion is also necessary to ensure 0 < splitPoint < last.
918      */
919 
920     double maxDistsq = 0.0; /* Maximum error */
921     double max_hook_ratio = 0.0;
922     unsigned snap_end = 0;
923     Point prev = bezCurve[0];
924     for (unsigned i = 1; i <= last; i++) {
925         Point const curr = bezier_pt(3, bezCurve, u[i]);
926         double const distsq = lensq( curr - d[i] );
927         if ( distsq > maxDistsq ) {
928             maxDistsq = distsq;
929             *splitPoint = i;
930         }
931         double const hook_ratio = compute_hook(prev, curr, .5 * (u[i - 1] + u[i]), bezCurve, tolerance);
932         if (max_hook_ratio < hook_ratio) {
933             max_hook_ratio = hook_ratio;
934             snap_end = i;
935         }
936         prev = curr;
937     }
938 
939     double const dist_ratio = sqrt(maxDistsq) / tolerance;
940     double ret;
941     if (max_hook_ratio <= dist_ratio) {
942         ret = dist_ratio;
943     } else {
944         assert(0 < snap_end);
945         ret = -max_hook_ratio;
946         *splitPoint = snap_end - 1;
947     }
948     assert( ret == 0.0
949               || ( ( *splitPoint < last )
950                    && ( *splitPoint != 0 || ret < 0. ) ) );
951     return ret;
952 }
953 
954 /**
955  * Whereas compute_max_error_ratio() checks for itself that each data point
956  * is near some point on the curve, this function checks that each point on
957  * the curve is near some data point (or near some point on the polyline
958  * defined by the data points, or something like that: we allow for a
959  * "reasonable curviness" from such a polyline).  "Reasonable curviness"
960  * means we draw a circle centred at the midpoint of a..b, of radius
961  * proportional to the length |a - b|, and require that each point on the
962  * segment of bezCurve between the parameters of a and b be within that circle.
963  * If any point P on the bezCurve segment is outside of that allowable
964  * region (circle), then we return some metric that increases with the
965  * distance from P to the circle.
966  *
967  *  Given that this is a fairly arbitrary criterion for finding appropriate
968  *  places for sharp corners, we test only one point on bezCurve, namely
969  *  the point on bezCurve with parameter halfway between our estimated
970  *  parameters for a and b.  (Alternatives are taking the farthest of a
971  *  few parameters between those of a and b, or even using a variant of
972  *  NewtonRaphsonFindRoot() for finding the maximum rather than minimum
973  *  distance.)
974  */
975 static double
compute_hook(Point const & a,Point const & b,double const u,BezierCurve const bezCurve,double const tolerance)976 compute_hook(Point const &a, Point const &b, double const u, BezierCurve const bezCurve,
977              double const tolerance)
978 {
979     Point const P = bezier_pt(3, bezCurve, u);
980     double const dist = distance((a+b)*.5, P);
981     if (dist < tolerance) {
982         return 0;
983     }
984     double const allowed = distance(a, b) + tolerance;
985     return dist / allowed;
986     /** \todo
987      * effic: Hooks are very rare.  We could start by comparing
988      * distsq, only resorting to the more expensive L2 in cases of
989      * uncertainty.
990      */
991 }
992 
993 }
994 
995 /*
996   Local Variables:
997   mode:c++
998   c-file-style:"stroustrup"
999   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1000   indent-tabs-mode:nil
1001   fill-column:99
1002   End:
1003 */
1004 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
1005