1 #define __SP_BEZIER_UTILS_C__
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 */
47 #define SP_HUGE 1e5
48 #define noBEZIER_DEBUG
50 #ifdef HAVE_IEEEFP_H
51 # include <ieefp.h>
52 #endif
54 #include <2geom/bezier-utils.h>
56 #include <2geom/isnan.h>
57 #include <assert.h>
59 namespace Geom{
61 typedef Point BezierCurve[];
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);
84 static Point const unconstrained_tangent(0, 0);
87 /*
88 * B0, B1, B2, B3 : Bezier multipliers
89 */
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 )
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
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
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 }
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
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;
139 Point *uniqued_data = new Point[len];
140 unsigned uniqued_len = copy_without_nans_or_adjacent_duplicates(data, len, uniqued_data);
142 if ( uniqued_len < 2 ) {
143 delete[] uniqued_data;
144 return 0;
145 }
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 }
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
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 }
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
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 */
205 if(!(bezier != NULL) ||
206 !(data != NULL) ||
207 !(len > 0) ||
208 !(max_beziers >= 1) ||
209 !(error >= 0.0))
210 return -1;
212 if ( len < 2 ) return 0;
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 }
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 }
251 generate_bezier(bezier, data, u, len, tHat1, tHat2, error);
252 reparameterize(data, len, u, bezier);
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);
258 if ( fabs(maxErrorRatio) <= 1.0 ) {
259 BEZIER_ASSERT(bezier);
260 delete[] u;
261 return 1;
262 }
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 }
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 }
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;
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 }
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 }
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
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 }
393 static void
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. */
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;
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];
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]);
421 /* rhs for eqn */
422 Point const a1 = b1 * tHat1;
423 Point const a2 = b2 * tHat2;
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);
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 }
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;
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 }
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 }
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];
491 return;
492 }
494 static double lensq(Point const p) {
495 return dot(p, p);
496 }
498 static void
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 };
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 }
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 }
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
545 reparameterize(Point const d[],
546 unsigned const len,
547 double u[],
548 BezierCurve const bezCurve)
549 {
550 assert( 2 <= len );
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. */
559 for (unsigned i = 1; i < last; i++) {
560 u[i] = NewtonRaphsonRootFind(bezCurve, d[i], u[i]);
561 }
562 }
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
574 NewtonRaphsonRootFind(BezierCurve const Q, Point const &P, double const u)
575 {
576 assert( 0.0 <= u );
577 assert( u <= 1.0 );
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 }
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 }
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);
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);
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 }
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 }
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 }
648 DOUBLE_ASSERT(improved_u);
649 return improved_u;
650 }
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
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;
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 }
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 }
700 /*
701 * ComputeLeftTangent, ComputeRightTangent, ComputeCenterTangent :
702 * Approximate unit tangents at endpoints and "center" of digitized curve
703 */
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
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 }
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
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 }
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
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 }
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
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 }
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
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 );
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 }
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
840 chord_length_parameterize(Point const d[], double u[], unsigned const len)
841 {
842 if(!( 2 <= len ))
843 return;
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 }
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 }
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 }
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 }
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
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 */
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 }
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 }
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
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 }
993 }
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 :