Subversion Repositories Scribus

Rev

Rev 14952 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
14952 fschmid 1
/* cairo - a vector graphics library with display and print output
2
 *
3
 * Copyright © 2002 University of Southern California
4
 *
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.
12
 *
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
18122 mrdocs 15
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
14952 fschmid 16
 * You should have received a copy of the MPL along with this library
17
 * in the file COPYING-MPL-1.1
18
 *
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/
23
 *
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.
27
 *
28
 * The Original Code is the cairo graphics library.
29
 *
30
 * The Initial Developer of the Original Code is University of Southern
31
 * California.
32
 *
33
 * Contributor(s):
34
 *      Carl D. Worth <cworth@cworth.org>
35
 */
36
 
37
#include "cairoint.h"
38
 
39
#include "cairo-slope-private.h"
40
 
41
cairo_bool_t
42
_cairo_spline_init (cairo_spline_t *spline,
43
                    cairo_spline_add_point_func_t add_point_func,
44
                    void *closure,
45
                    const cairo_point_t *a, const cairo_point_t *b,
46
                    const cairo_point_t *c, const cairo_point_t *d)
47
{
48
    spline->add_point_func = add_point_func;
49
    spline->closure = closure;
50
 
51
    spline->knots.a = *a;
52
    spline->knots.b = *b;
53
    spline->knots.c = *c;
54
    spline->knots.d = *d;
55
 
56
    if (a->x != b->x || a->y != b->y)
57
        _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
58
    else if (a->x != c->x || a->y != c->y)
59
        _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
60
    else if (a->x != d->x || a->y != d->y)
61
        _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
62
    else
63
        return FALSE;
64
 
65
    if (c->x != d->x || c->y != d->y)
66
        _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
67
    else if (b->x != d->x || b->y != d->y)
68
        _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
69
    else
70
        _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d);
71
 
72
    return TRUE;
73
}
74
 
75
static cairo_status_t
76
_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
77
{
78
    cairo_point_t *prev;
79
 
80
    prev = &spline->last_point;
81
    if (prev->x == point->x && prev->y == point->y)
82
        return CAIRO_STATUS_SUCCESS;
83
 
84
    spline->last_point = *point;
85
    return spline->add_point_func (spline->closure, point);
86
}
87
 
88
static void
89
_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
90
{
91
    result->x = a->x + ((b->x - a->x) >> 1);
92
    result->y = a->y + ((b->y - a->y) >> 1);
93
}
94
 
95
static void
96
_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
97
{
98
    cairo_point_t ab, bc, cd;
99
    cairo_point_t abbc, bccd;
100
    cairo_point_t final;
101
 
102
    _lerp_half (&s1->a, &s1->b, &ab);
103
    _lerp_half (&s1->b, &s1->c, &bc);
104
    _lerp_half (&s1->c, &s1->d, &cd);
105
    _lerp_half (&ab, &bc, &abbc);
106
    _lerp_half (&bc, &cd, &bccd);
107
    _lerp_half (&abbc, &bccd, &final);
108
 
109
    s2->a = final;
110
    s2->b = bccd;
111
    s2->c = cd;
112
    s2->d = s1->d;
113
 
114
    s1->b = ab;
115
    s1->c = abbc;
116
    s1->d = final;
117
}
118
 
119
/* Return an upper bound on the error (squared) that could result from
120
 * approximating a spline as a line segment connecting the two endpoints. */
121
static double
122
_cairo_spline_error_squared (const cairo_spline_knots_t *knots)
123
{
124
    double bdx, bdy, berr;
125
    double cdx, cdy, cerr;
126
 
127
    /* Intersection point (px):
128
     *      px = p1 + u(p2 - p1)
129
     *      (p - px) ∙ (p2 - p1) = 0
130
     * Thus:
131
     *      u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
132
     */
133
    bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
134
    bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
135
 
136
    cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
137
    cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
138
 
139
    if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
140
        double dx, dy, u, v;
141
 
142
        dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
143
        dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
144
         v = dx * dx + dy * dy;
145
 
146
        u = bdx * dx + bdy * dy;
147
        if (u <= 0) {
148
            /* bdx -= 0;
149
             * bdy -= 0;
150
             */
151
        } else if (u >= v) {
152
            bdx -= dx;
153
            bdy -= dy;
154
        } else {
155
            bdx -= u/v * dx;
156
            bdy -= u/v * dy;
157
        }
158
 
159
        u = cdx * dx + cdy * dy;
160
        if (u <= 0) {
161
            /* cdx -= 0;
162
             * cdy -= 0;
163
             */
164
        } else if (u >= v) {
165
            cdx -= dx;
166
            cdy -= dy;
167
        } else {
168
            cdx -= u/v * dx;
169
            cdy -= u/v * dy;
170
        }
171
    }
172
 
173
    berr = bdx * bdx + bdy * bdy;
174
    cerr = cdx * cdx + cdy * cdy;
175
    if (berr > cerr)
176
        return berr;
177
    else
178
        return cerr;
179
}
180
 
181
static cairo_status_t
182
_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result)
183
{
184
    cairo_spline_knots_t s2;
185
    cairo_status_t status;
186
 
187
    if (_cairo_spline_error_squared (s1) < tolerance_squared)
188
        return _cairo_spline_add_point (result, &s1->a);
189
 
190
    _de_casteljau (s1, &s2);
191
 
192
    status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
193
    if (unlikely (status))
194
        return status;
195
 
196
    return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
197
}
198
 
199
cairo_status_t
200
_cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
201
{
202
    cairo_spline_knots_t s1;
203
    cairo_status_t status;
204
 
205
    s1 = spline->knots;
206
    spline->last_point = s1.a;
207
    status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
208
    if (unlikely (status))
209
        return status;
210
 
211
    return _cairo_spline_add_point (spline, &spline->knots.d);
212
}
213
 
214
/* Note: this function is only good for computing bounds in device space. */
215
cairo_status_t
216
_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
217
                     void *closure,
218
                     const cairo_point_t *p0, const cairo_point_t *p1,
219
                     const cairo_point_t *p2, const cairo_point_t *p3)
220
{
221
    double x0, x1, x2, x3;
222
    double y0, y1, y2, y3;
223
    double a, b, c;
224
    double t[4];
225
    int t_num = 0, i;
226
    cairo_status_t status;
227
 
228
    x0 = _cairo_fixed_to_double (p0->x);
229
    y0 = _cairo_fixed_to_double (p0->y);
230
    x1 = _cairo_fixed_to_double (p1->x);
231
    y1 = _cairo_fixed_to_double (p1->y);
232
    x2 = _cairo_fixed_to_double (p2->x);
233
    y2 = _cairo_fixed_to_double (p2->y);
234
    x3 = _cairo_fixed_to_double (p3->x);
235
    y3 = _cairo_fixed_to_double (p3->y);
236
 
237
    /* The spline can be written as a polynomial of the four points:
238
     *
239
     *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
240
     *
241
     * for 0≤t≤1.  Now, the X and Y components of the spline follow the
242
     * same polynomial but with x and y replaced for p.  To find the
243
     * bounds of the spline, we just need to find the X and Y bounds.
244
     * To find the bound, we take the derivative and equal it to zero,
245
     * and solve to find the t's that give the extreme points.
246
     *
247
     * Here is the derivative of the curve, sorted on t:
248
     *
249
     *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
250
     *
251
     * Let:
252
     *
253
     *   a = -p0+3p1-3p2+p3
254
     *   b =  p0-2p1+p2
255
     *   c = -p0+p1
256
     *
257
     * Gives:
258
     *
259
     *   a.t² + 2b.t + c = 0
260
     *
261
     * With:
262
     *
263
     *   delta = b*b - a*c
264
     *
265
     * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
266
     * delta is positive, and at -b/a if delta is zero.
267
     */
268
 
269
#define ADD(t0) \
270
    { \
271
        double _t0 = (t0); \
272
        if (0 < _t0 && _t0 < 1) \
273
            t[t_num++] = _t0; \
274
    }
275
 
276
#define FIND_EXTREMES(a,b,c) \
277
    { \
278
        if (a == 0) { \
279
            if (b != 0) \
280
                ADD (-c / (2*b)); \
281
        } else { \
282
            double b2 = b * b; \
283
            double delta = b2 - a * c; \
284
            if (delta > 0) { \
285
                cairo_bool_t feasible; \
286
                double _2ab = 2 * a * b; \
287
                /* We are only interested in solutions t that satisfy 0<t<1 \
288
                 * here.  We do some checks to avoid sqrt if the solutions \
289
                 * are not in that range.  The checks can be derived from: \
290
                 * \
291
                 *   0 < (-b±√delta)/a < 1 \
292
                 */ \
293
                if (_2ab >= 0) \
294
                    feasible = delta > b2 && delta < a*a + b2 + _2ab; \
295
                else if (-b / a >= 1) \
296
                    feasible = delta < b2 && delta > a*a + b2 + _2ab; \
297
                else \
298
                    feasible = delta < b2 || delta < a*a + b2 + _2ab; \
299
                \
300
                if (unlikely (feasible)) { \
301
                    double sqrt_delta = sqrt (delta); \
302
                    ADD ((-b - sqrt_delta) / a); \
303
                    ADD ((-b + sqrt_delta) / a); \
304
                } \
305
            } else if (delta == 0) { \
306
                ADD (-b / a); \
307
            } \
308
        } \
309
    }
310
 
311
    /* Find X extremes */
312
    a = -x0 + 3*x1 - 3*x2 + x3;
313
    b =  x0 - 2*x1 + x2;
314
    c = -x0 + x1;
315
    FIND_EXTREMES (a, b, c);
316
 
317
    /* Find Y extremes */
318
    a = -y0 + 3*y1 - 3*y2 + y3;
319
    b =  y0 - 2*y1 + y2;
320
    c = -y0 + y1;
321
    FIND_EXTREMES (a, b, c);
322
 
323
    status = add_point_func (closure, p0);
324
    if (unlikely (status))
325
        return status;
326
 
327
    for (i = 0; i < t_num; i++) {
328
        cairo_point_t p;
329
        double x, y;
330
        double t_1_0, t_0_1;
331
        double t_2_0, t_0_2;
332
        double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
333
 
334
        t_1_0 = t[i];          /*      t  */
335
        t_0_1 = 1 - t_1_0;     /* (1 - t) */
336
 
337
        t_2_0 = t_1_0 * t_1_0; /*      t  *      t  */
338
        t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
339
 
340
        t_3_0   = t_2_0 * t_1_0;     /*      t  *      t  *      t      */
341
        t_2_1_3 = t_2_0 * t_0_1 * 3; /*      t  *      t  * (1 - t) * 3 */
342
        t_1_2_3 = t_1_0 * t_0_2 * 3; /*      t  * (1 - t) * (1 - t) * 3 */
343
        t_0_3   = t_0_1 * t_0_2;     /* (1 - t) * (1 - t) * (1 - t)     */
344
 
345
        /* Bezier polynomial */
346
        x = x0 * t_0_3
347
          + x1 * t_1_2_3
348
          + x2 * t_2_1_3
349
          + x3 * t_3_0;
350
        y = y0 * t_0_3
351
          + y1 * t_1_2_3
352
          + y2 * t_2_1_3
353
          + y3 * t_3_0;
354
 
355
        p.x = _cairo_fixed_from_double (x);
356
        p.y = _cairo_fixed_from_double (y);
357
        status = add_point_func (closure, &p);
358
        if (unlikely (status))
359
            return status;
360
    }
361
 
362
    return add_point_func (closure, p3);
363
}