Back to index

lightning-sunbird  0.9+nobinonly
cairo-spline.c
Go to the documentation of this file.
00001 /* cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2002 University of Southern California
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it either under the terms of the GNU Lesser General Public
00007  * License version 2.1 as published by the Free Software Foundation
00008  * (the "LGPL") or, at your option, under the terms of the Mozilla
00009  * Public License Version 1.1 (the "MPL"). If you do not alter this
00010  * notice, a recipient may use your version of this file under either
00011  * the MPL or the LGPL.
00012  *
00013  * You should have received a copy of the LGPL along with this library
00014  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00016  * You should have received a copy of the MPL along with this library
00017  * in the file COPYING-MPL-1.1
00018  *
00019  * The contents of this file are subject to the Mozilla Public License
00020  * Version 1.1 (the "License"); you may not use this file except in
00021  * compliance with the License. You may obtain a copy of the License at
00022  * http://www.mozilla.org/MPL/
00023  *
00024  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00025  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00026  * the specific language governing rights and limitations.
00027  *
00028  * The Original Code is the cairo graphics library.
00029  *
00030  * The Initial Developer of the Original Code is University of Southern
00031  * California.
00032  *
00033  * Contributor(s):
00034  *     Carl D. Worth <cworth@cworth.org>
00035  */
00036 
00037 #include "cairoint.h"
00038 
00039 static cairo_status_t
00040 _cairo_spline_grow_by (cairo_spline_t *spline, int additional);
00041 
00042 static cairo_status_t
00043 _cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point);
00044 
00045 static void
00046 _lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result);
00047 
00048 static void
00049 _de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2);
00050 
00051 static double
00052 _cairo_spline_error_squared (cairo_spline_t *spline);
00053 
00054 static cairo_status_t
00055 _cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result);
00056 
00057 cairo_int_status_t
00058 _cairo_spline_init (cairo_spline_t *spline,
00059                   cairo_point_t *a, cairo_point_t *b,
00060                   cairo_point_t *c, cairo_point_t *d)
00061 {
00062     spline->a = *a;
00063     spline->b = *b;
00064     spline->c = *c;
00065     spline->d = *d;
00066 
00067     if (a->x != b->x || a->y != b->y)
00068        _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->b);
00069     else if (a->x != c->x || a->y != c->y)
00070        _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->c);
00071     else if (a->x != d->x || a->y != d->y)
00072        _cairo_slope_init (&spline->initial_slope, &spline->a, &spline->d);
00073     else
00074        return CAIRO_INT_STATUS_DEGENERATE;
00075 
00076     if (c->x != d->x || c->y != d->y)
00077        _cairo_slope_init (&spline->final_slope, &spline->c, &spline->d);
00078     else if (b->x != d->x || b->y != d->y)
00079        _cairo_slope_init (&spline->final_slope, &spline->b, &spline->d);
00080     else
00081        _cairo_slope_init (&spline->final_slope, &spline->a, &spline->d);
00082 
00083     spline->num_points = 0;
00084     spline->points_size = 0;
00085     spline->points = NULL;
00086 
00087     return CAIRO_STATUS_SUCCESS;
00088 }
00089 
00090 void
00091 _cairo_spline_fini (cairo_spline_t *spline)
00092 {
00093     spline->num_points = 0;
00094     spline->points_size = 0;
00095     free (spline->points);
00096     spline->points = NULL;
00097 }
00098 
00099 static cairo_status_t
00100 _cairo_spline_grow_by (cairo_spline_t *spline, int additional)
00101 {
00102     cairo_point_t *new_points;
00103     int old_size = spline->points_size;
00104     int new_size = spline->num_points + additional;
00105 
00106     if (new_size <= spline->points_size)
00107        return CAIRO_STATUS_SUCCESS;
00108 
00109     spline->points_size = new_size;
00110     new_points = realloc (spline->points, spline->points_size * sizeof (cairo_point_t));
00111 
00112     if (new_points == NULL) {
00113        spline->points_size = old_size;
00114        return CAIRO_STATUS_NO_MEMORY;
00115     }
00116 
00117     spline->points = new_points;
00118 
00119     return CAIRO_STATUS_SUCCESS;
00120 }
00121 
00122 static cairo_status_t
00123 _cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
00124 {
00125     cairo_status_t status;
00126     cairo_point_t *prev;
00127 
00128     if (spline->num_points) {
00129        prev = &spline->points[spline->num_points - 1];
00130        if (prev->x == point->x && prev->y == point->y)
00131            return CAIRO_STATUS_SUCCESS;
00132     }
00133 
00134     if (spline->num_points >= spline->points_size) {
00135        int additional = spline->points_size ? spline->points_size : 32;
00136        status = _cairo_spline_grow_by (spline, additional);
00137        if (status)
00138            return status;
00139     }
00140 
00141     spline->points[spline->num_points] = *point;
00142     spline->num_points++;
00143 
00144     return CAIRO_STATUS_SUCCESS;
00145 }
00146 
00147 static void
00148 _lerp_half (cairo_point_t *a, cairo_point_t *b, cairo_point_t *result)
00149 {
00150     result->x = a->x + ((b->x - a->x) >> 1);
00151     result->y = a->y + ((b->y - a->y) >> 1);
00152 }
00153 
00154 static void
00155 _de_casteljau (cairo_spline_t *spline, cairo_spline_t *s1, cairo_spline_t *s2)
00156 {
00157     cairo_point_t ab, bc, cd;
00158     cairo_point_t abbc, bccd;
00159     cairo_point_t final;
00160 
00161     _lerp_half (&spline->a, &spline->b, &ab);
00162     _lerp_half (&spline->b, &spline->c, &bc);
00163     _lerp_half (&spline->c, &spline->d, &cd);
00164     _lerp_half (&ab, &bc, &abbc);
00165     _lerp_half (&bc, &cd, &bccd);
00166     _lerp_half (&abbc, &bccd, &final);
00167 
00168     s1->a = spline->a;
00169     s1->b = ab;
00170     s1->c = abbc;
00171     s1->d = final;
00172 
00173     s2->a = final;
00174     s2->b = bccd;
00175     s2->c = cd;
00176     s2->d = spline->d;
00177 }
00178 
00179 static double
00180 _PointDistanceSquaredToPoint (cairo_point_t *a, cairo_point_t *b)
00181 {
00182     double dx = _cairo_fixed_to_double (b->x - a->x);
00183     double dy = _cairo_fixed_to_double (b->y - a->y);
00184 
00185     return dx*dx + dy*dy;
00186 }
00187 
00188 static double
00189 _PointDistanceSquaredToSegment (cairo_point_t *p, cairo_point_t *p1, cairo_point_t *p2)
00190 {
00191     double u;
00192     double dx, dy;
00193     double pdx, pdy;
00194     cairo_point_t px;
00195 
00196     /* intersection point (px):
00197 
00198        px = p1 + u(p2 - p1)
00199        (p - px) . (p2 - p1) = 0
00200 
00201        Thus:
00202 
00203        u = ((p - p1) . (p2 - p1)) / (||(p2 - p1)|| ^ 2);
00204     */
00205 
00206     dx = _cairo_fixed_to_double (p2->x - p1->x);
00207     dy = _cairo_fixed_to_double (p2->y - p1->y);
00208 
00209     if (dx == 0 && dy == 0)
00210        return _PointDistanceSquaredToPoint (p, p1);
00211 
00212     pdx = _cairo_fixed_to_double (p->x - p1->x);
00213     pdy = _cairo_fixed_to_double (p->y - p1->y);
00214 
00215     u = (pdx * dx + pdy * dy) / (dx*dx + dy*dy);
00216 
00217     if (u <= 0)
00218        return _PointDistanceSquaredToPoint (p, p1);
00219     else if (u >= 1)
00220        return _PointDistanceSquaredToPoint (p, p2);
00221 
00222     px.x = p1->x + u * (p2->x - p1->x);
00223     px.y = p1->y + u * (p2->y - p1->y);
00224 
00225     return _PointDistanceSquaredToPoint (p, &px);
00226 }
00227 
00228 /* Return an upper bound on the error (squared) that could result from approximating
00229    a spline as a line segment connecting the two endpoints */
00230 static double
00231 _cairo_spline_error_squared (cairo_spline_t *spline)
00232 {
00233     double berr, cerr;
00234 
00235     berr = _PointDistanceSquaredToSegment (&spline->b, &spline->a, &spline->d);
00236     cerr = _PointDistanceSquaredToSegment (&spline->c, &spline->a, &spline->d);
00237 
00238     if (berr > cerr)
00239        return berr;
00240     else
00241        return cerr;
00242 }
00243 
00244 static cairo_status_t
00245 _cairo_spline_decompose_into (cairo_spline_t *spline, double tolerance_squared, cairo_spline_t *result)
00246 {
00247     cairo_status_t status;
00248     cairo_spline_t s1, s2;
00249 
00250     if (_cairo_spline_error_squared (spline) < tolerance_squared) {
00251        return _cairo_spline_add_point (result, &spline->a);
00252     }
00253 
00254     _de_casteljau (spline, &s1, &s2);
00255 
00256     status = _cairo_spline_decompose_into (&s1, tolerance_squared, result);
00257     if (status)
00258        return status;
00259     
00260     status = _cairo_spline_decompose_into (&s2, tolerance_squared, result);
00261     if (status)
00262        return status;
00263 
00264     return CAIRO_STATUS_SUCCESS;
00265 }
00266 
00267 cairo_status_t
00268 _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
00269 {
00270     cairo_status_t status;
00271 
00272     if (spline->points_size) {
00273        _cairo_spline_fini (spline);
00274     }
00275 
00276     status = _cairo_spline_decompose_into (spline, tolerance * tolerance, spline);
00277     if (status)
00278        return status;
00279 
00280     status = _cairo_spline_add_point (spline, &spline->d);
00281     if (status)
00282        return status;
00283 
00284     return CAIRO_STATUS_SUCCESS;
00285 }
00286