2 * Copyright 2008 Chris Wilson
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Chris Wilson not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior
11 * permission. Chris Wilson makes no representations about the
12 * suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
15 * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
18 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: Chris Wilson <chris@chris-wilson.co.uk>
26 #include "cairo-test.h"
28 static cairo_test_draw_function_t draw
;
30 static const cairo_test_t test
= {
31 "spline-decomposition",
32 "Tests splines with various inflection points",
37 typedef struct _point
{
41 typedef struct _knots
{
45 static knots_t knots
[5] = {
46 { {0, 0}, {0, 100}, {100, 100}, {100, 0} },
47 { {0, 0}, {75, 100}, {25, 100}, {100, 0} },
48 { {0, 0}, {100, 100}, {0, 100}, {100, 0} },
49 { {0, 0}, {150, 100}, {-50, 100}, {100, 0} },
50 { {0, 0}, {100, 200}, {0, -100}, {100, 100} },
55 _lerp_half (const point_t
*a
, const point_t
*b
, point_t
*result
)
57 result
->x
= .5 * (a
->x
+ b
->x
);
58 result
->y
= .5 * (a
->y
+ b
->y
);
62 _de_casteljau (knots_t
*k1
, knots_t
*k2
)
68 _lerp_half (&k1
->a
, &k1
->b
, &ab
);
69 _lerp_half (&k1
->b
, &k1
->c
, &bc
);
70 _lerp_half (&k1
->c
, &k1
->d
, &cd
);
71 _lerp_half (&ab
, &bc
, &abbc
);
72 _lerp_half (&bc
, &cd
, &bccd
);
73 _lerp_half (&abbc
, &bccd
, &final
);
86 _spline_error_squared (const knots_t
*knots
)
88 double bdx
, bdy
, berr
;
89 double cdx
, cdy
, cerr
;
92 /* Intersection point (px):
93 * px = p1 + u(p2 - p1)
94 * (p - px) ∙ (p2 - p1) = 0
96 * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
98 bdx
= knots
->b
.x
- knots
->a
.x
;
99 bdy
= knots
->b
.y
- knots
->a
.y
;
101 cdx
= knots
->c
.x
- knots
->a
.x
;
102 cdy
= knots
->c
.y
- knots
->a
.y
;
104 dx
= knots
->d
.x
- knots
->a
.x
;
105 dy
= knots
->d
.y
- knots
->a
.y
;
106 v
= dx
* dx
+ dy
* dy
;
110 u
= bdx
* dx
+ bdy
* dy
;
123 u
= cdx
* dx
+ cdy
* dy
;
137 berr
= bdx
* bdx
+ bdy
* bdy
;
138 cerr
= cdx
* cdx
+ cdy
* cdy
;
146 _offset_line_to (cairo_t
*cr
,
170 cairo_line_to (cr
, p0
->x
, p0
->y
);
172 cairo_line_to (cr
, p0
->x
- offset
* dy
/ v
, p0
->y
+ offset
* dx
/ v
);
176 _spline_decompose_into (knots_t
*k1
,
177 double tolerance_squared
,
183 if (_spline_error_squared (k1
) < tolerance_squared
) {
184 _offset_line_to (cr
, &k1
->a
, &k1
->b
, &k1
->c
, &k1
->d
, offset
);
188 _de_casteljau (k1
, &k2
);
190 _spline_decompose_into (k1
, tolerance_squared
, offset
, cr
);
191 _spline_decompose_into (&k2
, tolerance_squared
, offset
, cr
);
195 _spline_decompose (const knots_t
*knots
,
196 double tolerance
, double offset
,
202 _spline_decompose_into (&k
, tolerance
* tolerance
, offset
, cr
);
204 _offset_line_to (cr
, &knots
->d
, &knots
->c
, &knots
->b
, &knots
->a
, -offset
);
208 _knots_reverse (knots_t
*knots
)
222 thick_splines (cairo_t
*cr
, double offset
)
227 cairo_translate (cr
, 15, 15);
232 _spline_decompose (&k
, .1, offset
, cr
);
234 _spline_decompose (&k
, .1, offset
, cr
);
235 cairo_close_path (cr
);
238 cairo_translate (cr
, 130, 0);
243 _spline_decompose (&k
, .1, offset
, cr
);
245 _spline_decompose (&k
, .1, offset
, cr
);
246 cairo_close_path (cr
);
249 cairo_translate (cr
, 130, 0);
254 _spline_decompose (&k
, .1, offset
, cr
);
256 _spline_decompose (&k
, .1, offset
, cr
);
257 cairo_close_path (cr
);
260 cairo_translate (cr
, -130 - 65, 130);
265 _spline_decompose (&k
, .1, offset
, cr
);
267 _spline_decompose (&k
, .1, offset
, cr
);
268 cairo_close_path (cr
);
271 cairo_translate (cr
, 130, 0);
276 _spline_decompose (&k
, .1, offset
, cr
);
278 _spline_decompose (&k
, .1, offset
, cr
);
279 cairo_close_path (cr
);
285 thin_splines (cairo_t
*cr
)
288 cairo_translate (cr
, 15, 15);
291 _spline_decompose (&knots
[0], .1, 0, cr
);
294 cairo_translate (cr
, 130, 0);
297 _spline_decompose (&knots
[1], .1, 0, cr
);
300 cairo_translate (cr
, 130, 0);
303 _spline_decompose (&knots
[2], .1, 0, cr
);
306 cairo_translate (cr
, -130 - 65, 130);
309 _spline_decompose (&knots
[3], .1, 0, cr
);
312 cairo_translate (cr
, 130, 0);
315 _spline_decompose (&knots
[4], .1, 0, cr
);
322 stroke_splines (cairo_t
*cr
)
325 cairo_translate (cr
, 15, 15);
329 knots
[0].a
.x
, knots
[0].a
.y
);
331 knots
[0].b
.x
, knots
[0].b
.y
,
332 knots
[0].c
.x
, knots
[0].c
.y
,
333 knots
[0].d
.x
, knots
[0].d
.y
);
336 cairo_translate (cr
, 130, 0);
340 knots
[1].a
.x
, knots
[1].a
.y
);
342 knots
[1].b
.x
, knots
[1].b
.y
,
343 knots
[1].c
.x
, knots
[1].c
.y
,
344 knots
[1].d
.x
, knots
[1].d
.y
);
347 cairo_translate (cr
, 130, 0);
351 knots
[2].a
.x
, knots
[2].a
.y
);
353 knots
[2].b
.x
, knots
[2].b
.y
,
354 knots
[2].c
.x
, knots
[2].c
.y
,
355 knots
[2].d
.x
, knots
[2].d
.y
);
358 cairo_translate (cr
, -130 - 65, 130);
362 knots
[3].a
.x
, knots
[3].a
.y
);
364 knots
[3].b
.x
, knots
[3].b
.y
,
365 knots
[3].c
.x
, knots
[3].c
.y
,
366 knots
[3].d
.x
, knots
[3].d
.y
);
369 cairo_translate (cr
, 130, 0);
373 knots
[4].a
.x
, knots
[4].a
.y
);
375 knots
[4].b
.x
, knots
[4].b
.y
,
376 knots
[4].c
.x
, knots
[4].c
.y
,
377 knots
[4].d
.x
, knots
[4].d
.y
);
382 static cairo_test_status_t
383 draw (cairo_t
*cr
, int width
, int height
)
385 cairo_set_source_rgb (cr
, 1, 1, 1);
389 cairo_set_source_rgb (cr
, 0, 0, 0);
390 thick_splines (cr
, 5);
392 cairo_set_source_rgb (cr
, 1, 1, 1);
397 * Use a high tolerance to reduce dependence upon algorithm used for
398 * spline decomposition.
400 cairo_set_tolerance (cr
, 0.001);
402 cairo_set_line_width (cr
, 10);
403 cairo_set_source_rgb (cr
, 0, 0, 0);
405 cairo_set_line_width (cr
, 2);
406 cairo_set_source_rgb (cr
, 1, 1, 1);
409 return CAIRO_TEST_SUCCESS
;
415 return cairo_test (&test
);