Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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
  15.  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
  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-box-inline.h"
  40. #include "cairo-slope-private.h"
  41.  
  42. cairo_bool_t
  43. _cairo_spline_intersects (const cairo_point_t *a,
  44.                           const cairo_point_t *b,
  45.                           const cairo_point_t *c,
  46.                           const cairo_point_t *d,
  47.                           const cairo_box_t *box)
  48. {
  49.     cairo_box_t bounds;
  50.  
  51.     if (_cairo_box_contains_point (box, a) ||
  52.         _cairo_box_contains_point (box, b) ||
  53.         _cairo_box_contains_point (box, c) ||
  54.         _cairo_box_contains_point (box, d))
  55.     {
  56.         return TRUE;
  57.     }
  58.  
  59.     bounds.p2 = bounds.p1 = *a;
  60.     _cairo_box_add_point (&bounds, b);
  61.     _cairo_box_add_point (&bounds, c);
  62.     _cairo_box_add_point (&bounds, d);
  63.  
  64.     if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
  65.         bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
  66.     {
  67.         return FALSE;
  68.     }
  69.  
  70. #if 0 /* worth refining? */
  71.     bounds.p2 = bounds.p1 = *a;
  72.     _cairo_box_add_curve_to (&bounds, b, c, d);
  73.     if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
  74.         bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
  75.     {
  76.         return FALSE;
  77.     }
  78. #endif
  79.  
  80.     return TRUE;
  81. }
  82.  
  83. cairo_bool_t
  84. _cairo_spline_init (cairo_spline_t *spline,
  85.                     cairo_spline_add_point_func_t add_point_func,
  86.                     void *closure,
  87.                     const cairo_point_t *a, const cairo_point_t *b,
  88.                     const cairo_point_t *c, const cairo_point_t *d)
  89. {
  90.     /* If both tangents are zero, this is just a straight line */
  91.     if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
  92.         return FALSE;
  93.  
  94.     spline->add_point_func = add_point_func;
  95.     spline->closure = closure;
  96.  
  97.     spline->knots.a = *a;
  98.     spline->knots.b = *b;
  99.     spline->knots.c = *c;
  100.     spline->knots.d = *d;
  101.  
  102.     if (a->x != b->x || a->y != b->y)
  103.         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
  104.     else if (a->x != c->x || a->y != c->y)
  105.         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
  106.     else if (a->x != d->x || a->y != d->y)
  107.         _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
  108.     else
  109.         return FALSE;
  110.  
  111.     if (c->x != d->x || c->y != d->y)
  112.         _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
  113.     else if (b->x != d->x || b->y != d->y)
  114.         _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
  115.     else
  116.         return FALSE; /* just treat this as a straight-line from a -> d */
  117.  
  118.     /* XXX if the initial, final and vector are all equal, this is just a line */
  119.  
  120.     return TRUE;
  121. }
  122.  
  123. static cairo_status_t
  124. _cairo_spline_add_point (cairo_spline_t *spline,
  125.                          const cairo_point_t *point,
  126.                          const cairo_point_t *knot)
  127. {
  128.     cairo_point_t *prev;
  129.     cairo_slope_t slope;
  130.  
  131.     prev = &spline->last_point;
  132.     if (prev->x == point->x && prev->y == point->y)
  133.         return CAIRO_STATUS_SUCCESS;
  134.  
  135.     _cairo_slope_init (&slope, point, knot);
  136.  
  137.     spline->last_point = *point;
  138.     return spline->add_point_func (spline->closure, point, &slope);
  139. }
  140.  
  141. static void
  142. _lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
  143. {
  144.     result->x = a->x + ((b->x - a->x) >> 1);
  145.     result->y = a->y + ((b->y - a->y) >> 1);
  146. }
  147.  
  148. static void
  149. _de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
  150. {
  151.     cairo_point_t ab, bc, cd;
  152.     cairo_point_t abbc, bccd;
  153.     cairo_point_t final;
  154.  
  155.     _lerp_half (&s1->a, &s1->b, &ab);
  156.     _lerp_half (&s1->b, &s1->c, &bc);
  157.     _lerp_half (&s1->c, &s1->d, &cd);
  158.     _lerp_half (&ab, &bc, &abbc);
  159.     _lerp_half (&bc, &cd, &bccd);
  160.     _lerp_half (&abbc, &bccd, &final);
  161.  
  162.     s2->a = final;
  163.     s2->b = bccd;
  164.     s2->c = cd;
  165.     s2->d = s1->d;
  166.  
  167.     s1->b = ab;
  168.     s1->c = abbc;
  169.     s1->d = final;
  170. }
  171.  
  172. /* Return an upper bound on the error (squared) that could result from
  173.  * approximating a spline as a line segment connecting the two endpoints. */
  174. static double
  175. _cairo_spline_error_squared (const cairo_spline_knots_t *knots)
  176. {
  177.     double bdx, bdy, berr;
  178.     double cdx, cdy, cerr;
  179.  
  180.     /* We are going to compute the distance (squared) between each of the the b
  181.      * and c control points and the segment a-b. The maximum of these two
  182.      * distances will be our approximation error. */
  183.  
  184.     bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
  185.     bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
  186.  
  187.     cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
  188.     cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
  189.  
  190.     if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
  191.         /* Intersection point (px):
  192.          *     px = p1 + u(p2 - p1)
  193.          *     (p - px) ∙ (p2 - p1) = 0
  194.          * Thus:
  195.          *     u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²;
  196.          */
  197.  
  198.         double dx, dy, u, v;
  199.  
  200.         dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
  201.         dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
  202.          v = dx * dx + dy * dy;
  203.  
  204.         u = bdx * dx + bdy * dy;
  205.         if (u <= 0) {
  206.             /* bdx -= 0;
  207.              * bdy -= 0;
  208.              */
  209.         } else if (u >= v) {
  210.             bdx -= dx;
  211.             bdy -= dy;
  212.         } else {
  213.             bdx -= u/v * dx;
  214.             bdy -= u/v * dy;
  215.         }
  216.  
  217.         u = cdx * dx + cdy * dy;
  218.         if (u <= 0) {
  219.             /* cdx -= 0;
  220.              * cdy -= 0;
  221.              */
  222.         } else if (u >= v) {
  223.             cdx -= dx;
  224.             cdy -= dy;
  225.         } else {
  226.             cdx -= u/v * dx;
  227.             cdy -= u/v * dy;
  228.         }
  229.     }
  230.  
  231.     berr = bdx * bdx + bdy * bdy;
  232.     cerr = cdx * cdx + cdy * cdy;
  233.     if (berr > cerr)
  234.         return berr;
  235.     else
  236.         return cerr;
  237. }
  238.  
  239. static cairo_status_t
  240. _cairo_spline_decompose_into (cairo_spline_knots_t *s1,
  241.                               double tolerance_squared,
  242.                               cairo_spline_t *result)
  243. {
  244.     cairo_spline_knots_t s2;
  245.     cairo_status_t status;
  246.  
  247.     if (_cairo_spline_error_squared (s1) < tolerance_squared)
  248.         return _cairo_spline_add_point (result, &s1->a, &s1->b);
  249.  
  250.     _de_casteljau (s1, &s2);
  251.  
  252.     status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
  253.     if (unlikely (status))
  254.         return status;
  255.  
  256.     return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
  257. }
  258.  
  259. cairo_status_t
  260. _cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
  261. {
  262.     cairo_spline_knots_t s1;
  263.     cairo_status_t status;
  264.  
  265.     s1 = spline->knots;
  266.     spline->last_point = s1.a;
  267.     status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
  268.     if (unlikely (status))
  269.         return status;
  270.  
  271.     return spline->add_point_func (spline->closure,
  272.                                    &spline->knots.d, &spline->final_slope);
  273. }
  274.  
  275. /* Note: this function is only good for computing bounds in device space. */
  276. cairo_status_t
  277. _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
  278.                      void *closure,
  279.                      const cairo_point_t *p0, const cairo_point_t *p1,
  280.                      const cairo_point_t *p2, const cairo_point_t *p3)
  281. {
  282.     double x0, x1, x2, x3;
  283.     double y0, y1, y2, y3;
  284.     double a, b, c;
  285.     double t[4];
  286.     int t_num = 0, i;
  287.     cairo_status_t status;
  288.  
  289.     x0 = _cairo_fixed_to_double (p0->x);
  290.     y0 = _cairo_fixed_to_double (p0->y);
  291.     x1 = _cairo_fixed_to_double (p1->x);
  292.     y1 = _cairo_fixed_to_double (p1->y);
  293.     x2 = _cairo_fixed_to_double (p2->x);
  294.     y2 = _cairo_fixed_to_double (p2->y);
  295.     x3 = _cairo_fixed_to_double (p3->x);
  296.     y3 = _cairo_fixed_to_double (p3->y);
  297.  
  298.     /* The spline can be written as a polynomial of the four points:
  299.      *
  300.      *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
  301.      *
  302.      * for 0≤t≤1.  Now, the X and Y components of the spline follow the
  303.      * same polynomial but with x and y replaced for p.  To find the
  304.      * bounds of the spline, we just need to find the X and Y bounds.
  305.      * To find the bound, we take the derivative and equal it to zero,
  306.      * and solve to find the t's that give the extreme points.
  307.      *
  308.      * Here is the derivative of the curve, sorted on t:
  309.      *
  310.      *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
  311.      *
  312.      * Let:
  313.      *
  314.      *   a = -p0+3p1-3p2+p3
  315.      *   b =  p0-2p1+p2
  316.      *   c = -p0+p1
  317.      *
  318.      * Gives:
  319.      *
  320.      *   a.t² + 2b.t + c = 0
  321.      *
  322.      * With:
  323.      *
  324.      *   delta = b*b - a*c
  325.      *
  326.      * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
  327.      * delta is positive, and at -b/a if delta is zero.
  328.      */
  329.  
  330. #define ADD(t0) \
  331.     { \
  332.         double _t0 = (t0); \
  333.         if (0 < _t0 && _t0 < 1) \
  334.             t[t_num++] = _t0; \
  335.     }
  336.  
  337. #define FIND_EXTREMES(a,b,c) \
  338.     { \
  339.         if (a == 0) { \
  340.             if (b != 0) \
  341.                 ADD (-c / (2*b)); \
  342.         } else { \
  343.             double b2 = b * b; \
  344.             double delta = b2 - a * c; \
  345.             if (delta > 0) { \
  346.                 cairo_bool_t feasible; \
  347.                 double _2ab = 2 * a * b; \
  348.                 /* We are only interested in solutions t that satisfy 0<t<1 \
  349.                  * here.  We do some checks to avoid sqrt if the solutions \
  350.                  * are not in that range.  The checks can be derived from: \
  351.                  * \
  352.                  *   0 < (-b±√delta)/a < 1 \
  353.                  */ \
  354.                 if (_2ab >= 0) \
  355.                     feasible = delta > b2 && delta < a*a + b2 + _2ab; \
  356.                 else if (-b / a >= 1) \
  357.                     feasible = delta < b2 && delta > a*a + b2 + _2ab; \
  358.                 else \
  359.                     feasible = delta < b2 || delta < a*a + b2 + _2ab; \
  360.                 \
  361.                 if (unlikely (feasible)) { \
  362.                     double sqrt_delta = sqrt (delta); \
  363.                     ADD ((-b - sqrt_delta) / a); \
  364.                     ADD ((-b + sqrt_delta) / a); \
  365.                 } \
  366.             } else if (delta == 0) { \
  367.                 ADD (-b / a); \
  368.             } \
  369.         } \
  370.     }
  371.  
  372.     /* Find X extremes */
  373.     a = -x0 + 3*x1 - 3*x2 + x3;
  374.     b =  x0 - 2*x1 + x2;
  375.     c = -x0 + x1;
  376.     FIND_EXTREMES (a, b, c);
  377.  
  378.     /* Find Y extremes */
  379.     a = -y0 + 3*y1 - 3*y2 + y3;
  380.     b =  y0 - 2*y1 + y2;
  381.     c = -y0 + y1;
  382.     FIND_EXTREMES (a, b, c);
  383.  
  384.     status = add_point_func (closure, p0, NULL);
  385.     if (unlikely (status))
  386.         return status;
  387.  
  388.     for (i = 0; i < t_num; i++) {
  389.         cairo_point_t p;
  390.         double x, y;
  391.         double t_1_0, t_0_1;
  392.         double t_2_0, t_0_2;
  393.         double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
  394.  
  395.         t_1_0 = t[i];          /*      t  */
  396.         t_0_1 = 1 - t_1_0;     /* (1 - t) */
  397.  
  398.         t_2_0 = t_1_0 * t_1_0; /*      t  *      t  */
  399.         t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
  400.  
  401.         t_3_0   = t_2_0 * t_1_0;     /*      t  *      t  *      t      */
  402.         t_2_1_3 = t_2_0 * t_0_1 * 3; /*      t  *      t  * (1 - t) * 3 */
  403.         t_1_2_3 = t_1_0 * t_0_2 * 3; /*      t  * (1 - t) * (1 - t) * 3 */
  404.         t_0_3   = t_0_1 * t_0_2;     /* (1 - t) * (1 - t) * (1 - t)     */
  405.  
  406.         /* Bezier polynomial */
  407.         x = x0 * t_0_3
  408.           + x1 * t_1_2_3
  409.           + x2 * t_2_1_3
  410.           + x3 * t_3_0;
  411.         y = y0 * t_0_3
  412.           + y1 * t_1_2_3
  413.           + y2 * t_2_1_3
  414.           + y3 * t_3_0;
  415.  
  416.         p.x = _cairo_fixed_from_double (x);
  417.         p.y = _cairo_fixed_from_double (y);
  418.         status = add_point_func (closure, &p, NULL);
  419.         if (unlikely (status))
  420.             return status;
  421.     }
  422.  
  423.     return add_point_func (closure, p3, NULL);
  424. }
  425.