/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* This library is free software ; you can redistribute it and / or
* modify it either under the terms of the GNU Lesser General Public
* License version 2 . 1 as published by the Free Software Foundation
* ( the " LGPL " ) or , at your option , under the terms of the Mozilla
* Public License Version 1 . 1 ( the " MPL " ) . If you do not alter this
* notice , a recipient may use your version of this file under either
* the MPL or the LGPL .
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING - LGPL - 2 . 1 ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Suite 500 , Boston , MA 02110 - 1335 , USA
* You should have received a copy of the MPL along with this library
* in the file COPYING - MPL - 1 . 1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1 . 1 ( the " License " ) ; you may not use this file except in
* compliance with the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* This software is distributed on an " AS IS " basis , WITHOUT WARRANTY
* OF ANY KIND , either express or implied . See the LGPL or the MPL for
* the specific language governing rights and limitations .
*
* The Original Code is the cairo graphics library .
*
* The Initial Developer of the Original Code is University of Southern
* California .
*
* Contributor ( s ) :
* Carl D . Worth < cworth @ cworth . org >
*/
#include "cairoint.h"
#include "cairo-box-inline.h"
#include "cairo-slope-private.h"
cairo_bool_t
_cairo_spline_intersects (const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d,
const cairo_box_t *box)
{
cairo_box_t bounds;
if (_cairo_box_contains_point (box, a) ||
_cairo_box_contains_point (box, b) ||
_cairo_box_contains_point (box, c) ||
_cairo_box_contains_point (box, d))
{
return TRUE;
}
bounds.p2 = bounds.p1 = *a;
_cairo_box_add_point (&bounds, b);
_cairo_box_add_point (&bounds, c);
_cairo_box_add_point (&bounds, d);
if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
{
return FALSE ;
}
#if 0 /* worth refining? */
bounds.p2 = bounds.p1 = *a;
_cairo_box_add_curve_to (&bounds, b, c, d);
if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
{
return FALSE ;
}
#endif
return TRUE;
}
cairo_bool_t
_cairo_spline_init (cairo_spline_t *spline,
cairo_spline_add_point_func_t add_point_func,
void *closure,
const cairo_point_t *a, const cairo_point_t *b,
const cairo_point_t *c, const cairo_point_t *d)
{
/* If both tangents are zero, this is just a straight line */
if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
return FALSE ;
spline->add_point_func = add_point_func;
spline->closure = closure;
spline->knots.a = *a;
spline->knots.b = *b;
spline->knots.c = *c;
spline->knots.d = *d;
if (a->x != b->x || a->y != b->y)
_cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b);
else if (a->x != c->x || a->y != c->y)
_cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c);
else if (a->x != d->x || a->y != d->y)
_cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d);
else
return FALSE ;
if (c->x != d->x || c->y != d->y)
_cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d);
else if (b->x != d->x || b->y != d->y)
_cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
else
return FALSE ; /* just treat this as a straight-line from a -> d */
/* XXX if the initial, final and vector are all equal, this is just a line */
return TRUE;
}
static cairo_status_t
_cairo_spline_add_point (cairo_spline_t *spline,
const cairo_point_t *point,
const cairo_point_t *knot)
{
cairo_point_t *prev;
cairo_slope_t slope;
prev = &spline->last_point;
if (prev->x == point->x && prev->y == point->y)
return CAIRO_STATUS_SUCCESS;
_cairo_slope_init (&slope, point, knot);
spline->last_point = *point;
return spline->add_point_func (spline->closure, point, &slope);
}
static void
_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result)
{
result->x = a->x + ((b->x - a->x) >> 1 );
result->y = a->y + ((b->y - a->y) >> 1 );
}
static void
_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2)
{
cairo_point_t ab, bc, cd;
cairo_point_t abbc, bccd;
cairo_point_t final;
_lerp_half (&s1->a, &s1->b, &ab);
_lerp_half (&s1->b, &s1->c, &bc);
_lerp_half (&s1->c, &s1->d, &cd);
_lerp_half (&ab, &bc, &abbc);
_lerp_half (&bc, &cd, &bccd);
_lerp_half (&abbc, &bccd, &final);
s2->a = final;
s2->b = bccd;
s2->c = cd;
s2->d = s1->d;
s1->b = ab;
s1->c = abbc;
s1->d = final;
}
/* Return an upper bound on the error (squared) that could result from
* approximating a spline as a line segment connecting the two endpoints. */
static double
_cairo_spline_error_squared (const cairo_spline_knots_t *knots)
{
double bdx, bdy, berr;
double cdx, cdy, cerr;
/* We are going to compute the distance (squared) between each of the b
* and c control points and the segment a - b . The maximum of these two
* distances will be our approximation error. */
bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x);
bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y);
cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x);
cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y);
if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) {
/* Intersection point (px):
* px = p1 + u ( p2 - p1 )
* ( p - px ) ∙ ( p2 - p1 ) = 0
* Thus :
* u = ( ( p - p1 ) ∙ ( p2 - p1 ) ) / ∥ p2 - p1 ∥ ² ;
*/
double dx, dy, u, v;
dx = _cairo_fixed_to_double (knots->d.x - knots->a.x);
dy = _cairo_fixed_to_double (knots->d.y - knots->a.y);
v = dx * dx + dy * dy;
u = bdx * dx + bdy * dy;
if (u <= 0 ) {
/* bdx -= 0;
* bdy - = 0 ;
*/
} else if (u >= v) {
bdx -= dx;
bdy -= dy;
} else {
bdx -= u/v * dx;
bdy -= u/v * dy;
}
u = cdx * dx + cdy * dy;
if (u <= 0 ) {
/* cdx -= 0;
* cdy - = 0 ;
*/
} else if (u >= v) {
cdx -= dx;
cdy -= dy;
} else {
cdx -= u/v * dx;
cdy -= u/v * dy;
}
}
berr = bdx * bdx + bdy * bdy;
cerr = cdx * cdx + cdy * cdy;
if (berr > cerr)
return berr;
else
return cerr;
}
static cairo_status_t
_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
double tolerance_squared,
cairo_spline_t *result)
{
cairo_spline_knots_t s2;
cairo_status_t status;
if (_cairo_spline_error_squared (s1) < tolerance_squared)
return _cairo_spline_add_point (result, &s1->a, &s1->b);
_de_casteljau (s1, &s2);
status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
if (unlikely (status))
return status;
return _cairo_spline_decompose_into (&s2, tolerance_squared, result);
}
cairo_status_t
_cairo_spline_decompose (cairo_spline_t *spline, double tolerance)
{
cairo_spline_knots_t s1;
cairo_status_t status;
s1 = spline->knots;
spline->last_point = s1.a;
status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline);
if (unlikely (status))
return status;
return spline->add_point_func (spline->closure,
&spline->knots.d, &spline->final_slope);
}
/* Note: this function is only good for computing bounds in device space. */
cairo_status_t
_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
void *closure,
const cairo_point_t *p0, const cairo_point_t *p1,
const cairo_point_t *p2, const cairo_point_t *p3)
{
double x0, x1, x2, x3;
double y0, y1, y2, y3;
double a, b, c;
double t[4 ];
int t_num = 0 , i;
cairo_status_t status;
x0 = _cairo_fixed_to_double (p0->x);
y0 = _cairo_fixed_to_double (p0->y);
x1 = _cairo_fixed_to_double (p1->x);
y1 = _cairo_fixed_to_double (p1->y);
x2 = _cairo_fixed_to_double (p2->x);
y2 = _cairo_fixed_to_double (p2->y);
x3 = _cairo_fixed_to_double (p3->x);
y3 = _cairo_fixed_to_double (p3->y);
/* The spline can be written as a polynomial of the four points:
*
* ( 1 - t ) ³ p0 + 3 t ( 1 - t ) ² p1 + 3 t ² ( 1 - t ) p2 + t ³ p3
*
* for 0 ≤ t ≤ 1 . Now , the X and Y components of the spline follow the
* same polynomial but with x and y replaced for p . To find the
* bounds of the spline , we just need to find the X and Y bounds .
* To find the bound , we take the derivative and equal it to zero ,
* and solve to find the t ' s that give the extreme points .
*
* Here is the derivative of the curve , sorted on t :
*
* 3 t ² ( - p0 + 3 p1 - 3 p2 + p3 ) + 2 t ( 3 p0 - 6 p1 + 3 p2 ) - 3 p0 + 3 p1
*
* Let :
*
* a = - p0 + 3 p1 - 3 p2 + p3
* b = p0 - 2 p1 + p2
* c = - p0 + p1
*
* Gives :
*
* a . t ² + 2 b . t + c = 0
*
* With :
*
* delta = b * b - a * c
*
* the extreme points are at - c / 2 b if a is zero , at ( - b ± √ delta ) / a if
* delta is positive , and at - b / a if delta is zero .
*/
#define ADD(t0) \
{ \
double _t0 = (t0); \
if (0 < _t0 && _t0 < 1 ) \
t[t_num++] = _t0; \
}
#define FIND_EXTREMES(a,b,c) \
{ \
if (a == 0 ) { \
if (b != 0 ) \
ADD (-c / (2 *b)); \
} else { \
double b2 = b * b; \
double delta = b2 - a * c; \
if (delta > 0 ) { \
cairo_bool_t feasible; \
double _2 ab = 2 * a * b; \
/* We are only interested in solutions t that satisfy 0<t<1 \
* here . We do some checks to avoid sqrt if the solutions \
* are not in that range . The checks can be derived from : \
* \
* 0 < ( - b ± √ delta ) / a < 1 \
*/ \
if (_2 ab >= 0 ) \
feasible = delta > b2 && delta < a*a + b2 + _2 ab; \
else if (-b / a >= 1 ) \
feasible = delta < b2 && delta > a*a + b2 + _2 ab; \
else \
feasible = delta < b2 || delta < a*a + b2 + _2 ab; \
\
if (unlikely (feasible)) { \
double sqrt_delta = sqrt (delta); \
ADD ((-b - sqrt_delta) / a); \
ADD ((-b + sqrt_delta) / a); \
} \
} else if (delta == 0 ) { \
ADD (-b / a); \
} \
} \
}
/* Find X extremes */
a = -x0 + 3 *x1 - 3 *x2 + x3;
b = x0 - 2 *x1 + x2;
c = -x0 + x1;
FIND_EXTREMES (a, b, c);
/* Find Y extremes */
a = -y0 + 3 *y1 - 3 *y2 + y3;
b = y0 - 2 *y1 + y2;
c = -y0 + y1;
FIND_EXTREMES (a, b, c);
status = add_point_func (closure, p0, NULL);
if (unlikely (status))
return status;
for (i = 0 ; i < t_num; i++) {
cairo_point_t p;
double x, y;
double t_1_0, t_0_1;
double t_2_0, t_0_2;
double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
t_1_0 = t[i]; /* t */
t_0_1 = 1 - t_1_0; /* (1 - t) */
t_2_0 = t_1_0 * t_1_0; /* t * t */
t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
t_3_0 = t_2_0 * t_1_0; /* t * t * t */
t_2_1_3 = t_2_0 * t_0_1 * 3 ; /* t * t * (1 - t) * 3 */
t_1_2_3 = t_1_0 * t_0_2 * 3 ; /* t * (1 - t) * (1 - t) * 3 */
t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */
/* Bezier polynomial */
x = x0 * t_0_3
+ x1 * t_1_2_3
+ x2 * t_2_1_3
+ x3 * t_3_0;
y = y0 * t_0_3
+ y1 * t_1_2_3
+ y2 * t_2_1_3
+ y3 * t_3_0;
p.x = _cairo_fixed_from_double (x);
p.y = _cairo_fixed_from_double (y);
status = add_point_func (closure, &p, NULL);
if (unlikely (status))
return status;
}
return add_point_func (closure, p3, NULL);
}
Messung V0.5 in Prozent C=97 H=99 G=97
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland