1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2002 University of Southern California
5 * This library is free software; you can redistribute it and/or
6 * modify it either under the terms of the GNU Lesser General Public
7 * License version 2.1 as published by the Free Software Foundation
8 * (the "LGPL") or, at your option, under the terms of the Mozilla
9 * Public License Version 1.1 (the "MPL"). If you do not alter this
10 * notice, a recipient may use your version of this file under either
11 * the MPL or the LGPL.
13 * You should have received a copy of the LGPL along with this library
14 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 * You should have received a copy of the MPL along with this library
17 * in the file COPYING-MPL-1.1
19 * The contents of this file are subject to the Mozilla Public License
20 * Version 1.1 (the "License"); you may not use this file except in
21 * compliance with the License. You may obtain a copy of the License at
22 * http://www.mozilla.org/MPL/
24 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
25 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
26 * the specific language governing rights and limitations.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is University of Southern
34 * Carl D. Worth <cworth@cworth.org>
40 _cairo_spline_init (cairo_spline_t
*spline
,
41 cairo_spline_add_point_func_t add_point_func
,
43 const cairo_point_t
*a
, const cairo_point_t
*b
,
44 const cairo_point_t
*c
, const cairo_point_t
*d
)
46 spline
->add_point_func
= add_point_func
;
47 spline
->closure
= closure
;
54 if (a
->x
!= b
->x
|| a
->y
!= b
->y
)
55 _cairo_slope_init (&spline
->initial_slope
, &spline
->knots
.a
, &spline
->knots
.b
);
56 else if (a
->x
!= c
->x
|| a
->y
!= c
->y
)
57 _cairo_slope_init (&spline
->initial_slope
, &spline
->knots
.a
, &spline
->knots
.c
);
58 else if (a
->x
!= d
->x
|| a
->y
!= d
->y
)
59 _cairo_slope_init (&spline
->initial_slope
, &spline
->knots
.a
, &spline
->knots
.d
);
63 if (c
->x
!= d
->x
|| c
->y
!= d
->y
)
64 _cairo_slope_init (&spline
->final_slope
, &spline
->knots
.c
, &spline
->knots
.d
);
65 else if (b
->x
!= d
->x
|| b
->y
!= d
->y
)
66 _cairo_slope_init (&spline
->final_slope
, &spline
->knots
.b
, &spline
->knots
.d
);
68 _cairo_slope_init (&spline
->final_slope
, &spline
->knots
.a
, &spline
->knots
.d
);
74 _cairo_spline_add_point (cairo_spline_t
*spline
, cairo_point_t
*point
)
78 prev
= &spline
->last_point
;
79 if (prev
->x
== point
->x
&& prev
->y
== point
->y
)
80 return CAIRO_STATUS_SUCCESS
;
82 spline
->last_point
= *point
;
83 return spline
->add_point_func (spline
->closure
, point
);
87 _lerp_half (const cairo_point_t
*a
, const cairo_point_t
*b
, cairo_point_t
*result
)
89 result
->x
= a
->x
+ ((b
->x
- a
->x
) >> 1);
90 result
->y
= a
->y
+ ((b
->y
- a
->y
) >> 1);
94 _de_casteljau (cairo_spline_knots_t
*s1
, cairo_spline_knots_t
*s2
)
96 cairo_point_t ab
, bc
, cd
;
97 cairo_point_t abbc
, bccd
;
100 _lerp_half (&s1
->a
, &s1
->b
, &ab
);
101 _lerp_half (&s1
->b
, &s1
->c
, &bc
);
102 _lerp_half (&s1
->c
, &s1
->d
, &cd
);
103 _lerp_half (&ab
, &bc
, &abbc
);
104 _lerp_half (&bc
, &cd
, &bccd
);
105 _lerp_half (&abbc
, &bccd
, &final
);
117 /* Return an upper bound on the error (squared) that could result from
118 * approximating a spline as a line segment connecting the two endpoints. */
120 _cairo_spline_error_squared (const cairo_spline_knots_t
*knots
)
122 double bdx
, bdy
, berr
;
123 double cdx
, cdy
, cerr
;
125 /* Intersection point (px):
126 * px = p1 + u(p2 - p1)
127 * (p - px) ∙ (p2 - p1) = 0
129 * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
131 bdx
= _cairo_fixed_to_double (knots
->b
.x
- knots
->a
.x
);
132 bdy
= _cairo_fixed_to_double (knots
->b
.y
- knots
->a
.y
);
134 cdx
= _cairo_fixed_to_double (knots
->c
.x
- knots
->a
.x
);
135 cdy
= _cairo_fixed_to_double (knots
->c
.y
- knots
->a
.y
);
137 if (knots
->a
.x
!= knots
->d
.x
|| knots
->a
.y
!= knots
->d
.y
) {
140 dx
= _cairo_fixed_to_double (knots
->d
.x
- knots
->a
.x
);
141 dy
= _cairo_fixed_to_double (knots
->d
.y
- knots
->a
.y
);
142 v
= dx
* dx
+ dy
* dy
;
144 u
= bdx
* dx
+ bdy
* dy
;
157 u
= cdx
* dx
+ cdy
* dy
;
171 berr
= bdx
* bdx
+ bdy
* bdy
;
172 cerr
= cdx
* cdx
+ cdy
* cdy
;
179 static cairo_status_t
180 _cairo_spline_decompose_into (cairo_spline_knots_t
*s1
, double tolerance_squared
, cairo_spline_t
*result
)
182 cairo_spline_knots_t s2
;
183 cairo_status_t status
;
185 if (_cairo_spline_error_squared (s1
) < tolerance_squared
)
186 return _cairo_spline_add_point (result
, &s1
->a
);
188 _de_casteljau (s1
, &s2
);
190 status
= _cairo_spline_decompose_into (s1
, tolerance_squared
, result
);
191 if (unlikely (status
))
194 return _cairo_spline_decompose_into (&s2
, tolerance_squared
, result
);
198 _cairo_spline_decompose (cairo_spline_t
*spline
, double tolerance
)
200 cairo_spline_knots_t s1
;
201 cairo_status_t status
;
204 spline
->last_point
= s1
.a
;
205 status
= _cairo_spline_decompose_into (&s1
, tolerance
* tolerance
, spline
);
206 if (unlikely (status
))
209 return _cairo_spline_add_point (spline
, &spline
->knots
.d
);
212 /* Note: this function is only good for computing bounds in device space. */
214 _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func
,
216 const cairo_point_t
*p0
, const cairo_point_t
*p1
,
217 const cairo_point_t
*p2
, const cairo_point_t
*p3
)
219 double x0
, x1
, x2
, x3
;
220 double y0
, y1
, y2
, y3
;
224 cairo_status_t status
;
226 x0
= _cairo_fixed_to_double (p0
->x
);
227 y0
= _cairo_fixed_to_double (p0
->y
);
228 x1
= _cairo_fixed_to_double (p1
->x
);
229 y1
= _cairo_fixed_to_double (p1
->y
);
230 x2
= _cairo_fixed_to_double (p2
->x
);
231 y2
= _cairo_fixed_to_double (p2
->y
);
232 x3
= _cairo_fixed_to_double (p3
->x
);
233 y3
= _cairo_fixed_to_double (p3
->y
);
235 /* The spline can be written as a polynomial of the four points:
237 * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
239 * for 0≤t≤1. Now, the X and Y components of the spline follow the
240 * same polynomial but with x and y replaced for p. To find the
241 * bounds of the spline, we just need to find the X and Y bounds.
242 * To find the bound, we take the derivative and equal it to zero,
243 * and solve to find the t's that give the extreme points.
245 * Here is the derivative of the curve, sorted on t:
247 * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
257 * a.t² + 2b.t + c = 0
263 * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
264 * delta is positive, and at -b/a if delta is zero.
270 if (0 < _t0 && _t0 < 1) \
274 #define FIND_EXTREMES(a,b,c) \
281 double delta = b2 - a * c; \
283 cairo_bool_t feasible; \
284 double _2ab = 2 * a * b; \
285 /* We are only interested in solutions t that satisfy 0<t<1 \
286 * here. We do some checks to avoid sqrt if the solutions \
287 * are not in that range. The checks can be derived from: \
289 * 0 < (-b±√delta)/a < 1 \
292 feasible = delta > b2 && delta < a*a + b2 + _2ab; \
293 else if (-b / a >= 1) \
294 feasible = delta < b2 && delta > a*a + b2 + _2ab; \
296 feasible = delta < b2 || delta < a*a + b2 + _2ab; \
298 if (unlikely (feasible)) { \
299 double sqrt_delta = sqrt (delta); \
300 ADD ((-b - sqrt_delta) / a); \
301 ADD ((-b + sqrt_delta) / a); \
303 } else if (delta == 0) { \
309 /* Find X extremes */
310 a
= -x0
+ 3*x1
- 3*x2
+ x3
;
313 FIND_EXTREMES (a
, b
, c
);
315 /* Find Y extremes */
316 a
= -y0
+ 3*y1
- 3*y2
+ y3
;
319 FIND_EXTREMES (a
, b
, c
);
321 status
= add_point_func (closure
, p0
);
322 if (unlikely (status
))
325 for (i
= 0; i
< t_num
; i
++) {
330 double t_3_0
, t_2_1_3
, t_1_2_3
, t_0_3
;
332 t_1_0
= t
[i
]; /* t */
333 t_0_1
= 1 - t_1_0
; /* (1 - t) */
335 t_2_0
= t_1_0
* t_1_0
; /* t * t */
336 t_0_2
= t_0_1
* t_0_1
; /* (1 - t) * (1 - t) */
338 t_3_0
= t_2_0
* t_1_0
; /* t * t * t */
339 t_2_1_3
= t_2_0
* t_0_1
* 3; /* t * t * (1 - t) * 3 */
340 t_1_2_3
= t_1_0
* t_0_2
* 3; /* t * (1 - t) * (1 - t) * 3 */
341 t_0_3
= t_0_1
* t_0_2
; /* (1 - t) * (1 - t) * (1 - t) */
343 /* Bezier polynomial */
353 p
.x
= _cairo_fixed_from_double (x
);
354 p
.y
= _cairo_fixed_from_double (y
);
355 status
= add_point_func (closure
, &p
);
356 if (unlikely (status
))
360 return add_point_func (closure
, p3
);