mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-01-07 00:00:12 +01:00
709 lines
20 KiB
C
709 lines
20 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright 2009 VMware, Inc. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
* of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
|
* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include "arc.h"
|
|
|
|
#include "matrix.h"
|
|
#include "bezier.h"
|
|
#include "polygon.h"
|
|
#include "stroker.h"
|
|
#include "path.h"
|
|
|
|
#include "util/u_debug.h"
|
|
#include "util/u_math.h"
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846
|
|
#endif
|
|
|
|
#define DEBUG_ARCS 0
|
|
|
|
static const VGfloat two_pi = M_PI * 2;
|
|
|
|
|
|
static const double coeffs3Low[2][4][4] = {
|
|
{
|
|
{ 3.85268, -21.229, -0.330434, 0.0127842 },
|
|
{ -1.61486, 0.706564, 0.225945, 0.263682 },
|
|
{ -0.910164, 0.388383, 0.00551445, 0.00671814 },
|
|
{ -0.630184, 0.192402, 0.0098871, 0.0102527 }
|
|
},
|
|
{
|
|
{ -0.162211, 9.94329, 0.13723, 0.0124084 },
|
|
{ -0.253135, 0.00187735, 0.0230286, 0.01264 },
|
|
{ -0.0695069, -0.0437594, 0.0120636, 0.0163087 },
|
|
{ -0.0328856, -0.00926032, -0.00173573, 0.00527385 }
|
|
}
|
|
};
|
|
|
|
/* coefficients for error estimation
|
|
while using cubic Bézier curves for approximation
|
|
1/4 <= b/a <= 1 */
|
|
static const double coeffs3High[2][4][4] = {
|
|
{
|
|
{ 0.0899116, -19.2349, -4.11711, 0.183362 },
|
|
{ 0.138148, -1.45804, 1.32044, 1.38474 },
|
|
{ 0.230903, -0.450262, 0.219963, 0.414038 },
|
|
{ 0.0590565, -0.101062, 0.0430592, 0.0204699 }
|
|
},
|
|
{
|
|
{ 0.0164649, 9.89394, 0.0919496, 0.00760802 },
|
|
{ 0.0191603, -0.0322058, 0.0134667, -0.0825018 },
|
|
{ 0.0156192, -0.017535, 0.00326508, -0.228157 },
|
|
{ -0.0236752, 0.0405821, -0.0173086, 0.176187 }
|
|
}
|
|
};
|
|
|
|
/* safety factor to convert the "best" error approximation
|
|
into a "max bound" error */
|
|
static const double safety3[] = {
|
|
0.001, 4.98, 0.207, 0.0067
|
|
};
|
|
|
|
/* The code below is from the OpenVG 1.1 Spec
|
|
* Section 18.4 */
|
|
|
|
/* Given: Points (x0, y0) and (x1, y1)
|
|
* Return: TRUE if a solution exists, FALSE otherwise
|
|
* Circle centers are written to (cx0, cy0) and (cx1, cy1)
|
|
*/
|
|
static VGboolean
|
|
find_unit_circles(double x0, double y0, double x1, double y1,
|
|
double *cx0, double *cy0,
|
|
double *cx1, double *cy1)
|
|
{
|
|
/* Compute differences and averages */
|
|
double dx = x0 - x1;
|
|
double dy = y0 - y1;
|
|
double xm = (x0 + x1)/2;
|
|
double ym = (y0 + y1)/2;
|
|
double dsq, disc, s, sdx, sdy;
|
|
|
|
/* Solve for intersecting unit circles */
|
|
dsq = dx*dx + dy*dy;
|
|
if (dsq == 0.0) return VG_FALSE; /* Points are coincident */
|
|
disc = 1.0/dsq - 1.0/4.0;
|
|
|
|
/* the precision we care about here is around float so if we're
|
|
* around the float defined zero then make it official to avoid
|
|
* precision problems later on */
|
|
if (floatIsZero(disc))
|
|
disc = 0.0;
|
|
|
|
if (disc < 0.0) return VG_FALSE; /* Points are too far apart */
|
|
s = sqrt(disc);
|
|
sdx = s*dx;
|
|
sdy = s*dy;
|
|
*cx0 = xm + sdy;
|
|
*cy0 = ym - sdx;
|
|
*cx1 = xm - sdy;
|
|
*cy1 = ym + sdx;
|
|
return VG_TRUE;
|
|
}
|
|
|
|
|
|
/* Given: Ellipse parameters rh, rv, rot (in degrees),
|
|
* endpoints (x0, y0) and (x1, y1)
|
|
* Return: TRUE if a solution exists, FALSE otherwise
|
|
* Ellipse centers are written to (cx0, cy0) and (cx1, cy1)
|
|
*/
|
|
static VGboolean
|
|
find_ellipses(double rh, double rv, double rot,
|
|
double x0, double y0, double x1, double y1,
|
|
double *cx0, double *cy0, double *cx1, double *cy1)
|
|
{
|
|
double COS, SIN, x0p, y0p, x1p, y1p, pcx0, pcy0, pcx1, pcy1;
|
|
/* Convert rotation angle from degrees to radians */
|
|
rot *= M_PI/180.0;
|
|
/* Pre-compute rotation matrix entries */
|
|
COS = cos(rot); SIN = sin(rot);
|
|
/* Transform (x0, y0) and (x1, y1) into unit space */
|
|
/* using (inverse) rotate, followed by (inverse) scale */
|
|
x0p = (x0*COS + y0*SIN)/rh;
|
|
y0p = (-x0*SIN + y0*COS)/rv;
|
|
x1p = (x1*COS + y1*SIN)/rh;
|
|
y1p = (-x1*SIN + y1*COS)/rv;
|
|
if (!find_unit_circles(x0p, y0p, x1p, y1p,
|
|
&pcx0, &pcy0, &pcx1, &pcy1)) {
|
|
return VG_FALSE;
|
|
}
|
|
/* Transform back to original coordinate space */
|
|
/* using (forward) scale followed by (forward) rotate */
|
|
pcx0 *= rh; pcy0 *= rv;
|
|
pcx1 *= rh; pcy1 *= rv;
|
|
*cx0 = pcx0*COS - pcy0*SIN;
|
|
*cy0 = pcx0*SIN + pcy0*COS;
|
|
*cx1 = pcx1*COS - pcy1*SIN;
|
|
*cy1 = pcx1*SIN + pcy1*COS;
|
|
return VG_TRUE;
|
|
}
|
|
|
|
static INLINE VGboolean
|
|
try_to_fix_radii(struct arc *arc)
|
|
{
|
|
double COS, SIN, rot, x0p, y0p, x1p, y1p;
|
|
double dx, dy, dsq, scale;
|
|
|
|
/* Convert rotation angle from degrees to radians */
|
|
rot = DEGREES_TO_RADIANS(arc->theta);
|
|
|
|
/* Pre-compute rotation matrix entries */
|
|
COS = cos(rot); SIN = sin(rot);
|
|
|
|
/* Transform (x0, y0) and (x1, y1) into unit space */
|
|
/* using (inverse) rotate, followed by (inverse) scale */
|
|
x0p = (arc->x1*COS + arc->y1*SIN)/arc->a;
|
|
y0p = (-arc->x1*SIN + arc->y1*COS)/arc->b;
|
|
x1p = (arc->x2*COS + arc->y2*SIN)/arc->a;
|
|
y1p = (-arc->x2*SIN + arc->y2*COS)/arc->b;
|
|
/* Compute differences and averages */
|
|
dx = x0p - x1p;
|
|
dy = y0p - y1p;
|
|
|
|
dsq = dx*dx + dy*dy;
|
|
#if 0
|
|
if (dsq <= 0.001) {
|
|
debug_printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaa\n");
|
|
}
|
|
#endif
|
|
scale = 1/(2/sqrt(dsq));
|
|
arc->a *= scale;
|
|
arc->b *= scale;
|
|
return VG_TRUE;
|
|
}
|
|
|
|
static INLINE double vector_normalize(double *v)
|
|
{
|
|
double sq = v[0] * v[0] + v[1] * v[1];
|
|
return sqrt(sq);
|
|
}
|
|
static INLINE double vector_orientation(double *v)
|
|
{
|
|
double norm = vector_normalize(v);
|
|
double cosa = v[0] / norm;
|
|
double sina = v[1] / norm;
|
|
return (sina>=0 ? acos(cosa) : 2*M_PI - acos(cosa));
|
|
}
|
|
static INLINE double vector_dot(double *v0,
|
|
double *v1)
|
|
{
|
|
return v0[0] * v1[0] + v0[1] * v1[1];
|
|
}
|
|
|
|
static INLINE double vector_angles(double *v0,
|
|
double *v1)
|
|
{
|
|
double dot = vector_dot(v0, v1);
|
|
double norm0 = vector_normalize(v0);
|
|
double norm1 = vector_normalize(v1);
|
|
|
|
return acos(dot / (norm0 * norm1));
|
|
}
|
|
|
|
static VGboolean find_angles(struct arc *arc)
|
|
{
|
|
double vec0[2], vec1[2];
|
|
double lambda1, lambda2;
|
|
double angle;
|
|
struct matrix matrix;
|
|
|
|
if (floatIsZero(arc->a) || floatIsZero(arc->b)) {
|
|
return VG_FALSE;
|
|
}
|
|
/* map the points to an identity circle */
|
|
matrix_load_identity(&matrix);
|
|
matrix_scale(&matrix, 1.f, arc->a/arc->b);
|
|
matrix_rotate(&matrix, -arc->theta);
|
|
matrix_map_point(&matrix,
|
|
arc->x1, arc->y1,
|
|
&arc->x1, &arc->y1);
|
|
matrix_map_point(&matrix,
|
|
arc->x2, arc->y2,
|
|
&arc->x2, &arc->y2);
|
|
matrix_map_point(&matrix,
|
|
arc->cx, arc->cy,
|
|
&arc->cx, &arc->cy);
|
|
|
|
#if DEBUG_ARCS
|
|
debug_printf("Matrix 3 [%f, %f, %f| %f, %f, %f| %f, %f, %f]\n",
|
|
matrix.m[0], matrix.m[1], matrix.m[2],
|
|
matrix.m[3], matrix.m[4], matrix.m[5],
|
|
matrix.m[6], matrix.m[7], matrix.m[8]);
|
|
debug_printf("Endpoints [%f, %f], [%f, %f]\n",
|
|
arc->x1, arc->y1, arc->x2, arc->y2);
|
|
#endif
|
|
|
|
vec0[0] = arc->x1 - arc->cx;
|
|
vec0[1] = arc->y1 - arc->cy;
|
|
vec1[0] = arc->x2 - arc->cx;
|
|
vec1[1] = arc->y2 - arc->cy;
|
|
|
|
#if DEBUG_ARCS
|
|
debug_printf("Vec is [%f, %f], [%f, %f], [%f, %f]\n",
|
|
vec0[0], vec0[1], vec1[0], vec1[1], arc->cx, arc->cy);
|
|
#endif
|
|
|
|
lambda1 = vector_orientation(vec0);
|
|
|
|
if (isnan(lambda1))
|
|
lambda1 = 0.f;
|
|
|
|
if (arc->type == VG_SCWARC_TO ||
|
|
arc->type == VG_SCCWARC_TO)
|
|
angle = vector_angles(vec0, vec1);
|
|
else if (arc->type == VG_LCWARC_TO ||
|
|
arc->type == VG_LCCWARC_TO) {
|
|
angle = 2*M_PI - vector_angles(vec0, vec1);
|
|
} else
|
|
abort();
|
|
|
|
if (isnan(angle))
|
|
angle = M_PI;
|
|
|
|
|
|
if (arc->type == VG_SCWARC_TO ||
|
|
arc->type == VG_LCWARC_TO)
|
|
lambda2 = lambda1 - angle;
|
|
else
|
|
lambda2 = lambda1 + angle;
|
|
|
|
#if DEBUG_ARCS
|
|
debug_printf("Angle is %f and (%f, %f)\n", angle, lambda1, lambda2);
|
|
#endif
|
|
|
|
#if 0
|
|
arc->eta1 = atan2(sin(lambda1) / arc->b,
|
|
cos(lambda1) / arc->a);
|
|
arc->eta2 = atan2(sin(lambda2) / arc->b,
|
|
cos(lambda2) / arc->a);
|
|
|
|
/* make sure we have eta1 <= eta2 <= eta1 + 2 PI */
|
|
arc->eta2 -= two_pi * floor((arc->eta2 - arc->eta1) / two_pi);
|
|
|
|
/* the preceding correction fails if we have exactly et2 - eta1 = 2 PI
|
|
it reduces the interval to zero length */
|
|
if ((lambda2 - lambda1 > M_PI) && (arc->eta2 - arc->eta1 < M_PI)) {
|
|
arc->eta2 += 2 * M_PI;
|
|
}
|
|
#else
|
|
arc->eta1 = lambda1;
|
|
arc->eta2 = lambda2;
|
|
#endif
|
|
|
|
return VG_TRUE;
|
|
}
|
|
|
|
#if DEBUG_ARCS
|
|
static void check_endpoints(struct arc *arc)
|
|
{
|
|
double x1, y1, x2, y2;
|
|
|
|
double a_cos_eta1 = arc->a * cos(arc->eta1);
|
|
double b_sin_eta1 = arc->b * sin(arc->eta1);
|
|
x1 = arc->cx + a_cos_eta1 * arc->cos_theta -
|
|
b_sin_eta1 * arc->sin_theta;
|
|
y1 = arc->cy + a_cos_eta1 * arc->sin_theta +
|
|
b_sin_eta1 * arc->cos_theta;
|
|
|
|
double a_cos_eta2 = arc->a * cos(arc->eta2);
|
|
double b_sin_eta2 = arc->b * sin(arc->eta2);
|
|
x2 = arc->cx + a_cos_eta2 * arc->cos_theta -
|
|
b_sin_eta2 * arc->sin_theta;
|
|
y2 = arc->cy + a_cos_eta2 * arc->sin_theta +
|
|
b_sin_eta2 * arc->cos_theta;
|
|
|
|
debug_printf("Computed (%f, %f), (%f, %f)\n",
|
|
x1, y1, x2, y2);
|
|
debug_printf("Real (%f, %f), (%f, %f)\n",
|
|
arc->x1, arc->y1,
|
|
arc->x2, arc->y2);
|
|
}
|
|
#endif
|
|
|
|
void arc_init(struct arc *arc,
|
|
VGPathSegment type,
|
|
VGfloat x1, VGfloat y1,
|
|
VGfloat x2, VGfloat y2,
|
|
VGfloat rh, VGfloat rv,
|
|
VGfloat rot)
|
|
{
|
|
assert(type == VG_SCCWARC_TO ||
|
|
type == VG_SCWARC_TO ||
|
|
type == VG_LCCWARC_TO ||
|
|
type == VG_LCWARC_TO);
|
|
arc->type = type;
|
|
arc->x1 = x1;
|
|
arc->y1 = y1;
|
|
arc->x2 = x2;
|
|
arc->y2 = y2;
|
|
arc->a = rh;
|
|
arc->b = rv;
|
|
arc->theta = rot;
|
|
arc->cos_theta = cos(arc->theta);
|
|
arc->sin_theta = sin(arc->theta);
|
|
{
|
|
double cx0, cy0, cx1, cy1;
|
|
double cx, cy;
|
|
arc->is_valid = find_ellipses(rh, rv, rot, x1, y1, x2, y2,
|
|
&cx0, &cy0, &cx1, &cy1);
|
|
|
|
if (!arc->is_valid && try_to_fix_radii(arc)) {
|
|
rh = arc->a;
|
|
rv = arc->b;
|
|
arc->is_valid =
|
|
find_ellipses(rh, rv, rot, x1, y1, x2, y2,
|
|
&cx0, &cy0, &cx1, &cy1);
|
|
}
|
|
|
|
if (type == VG_SCWARC_TO ||
|
|
type == VG_LCCWARC_TO) {
|
|
cx = cx1;
|
|
cy = cy1;
|
|
} else {
|
|
cx = cx0;
|
|
cy = cy0;
|
|
}
|
|
#if DEBUG_ARCS
|
|
debug_printf("Centers are : (%f, %f) , (%f, %f). Real (%f, %f)\n",
|
|
cx0, cy0, cx1, cy1, cx, cy);
|
|
#endif
|
|
arc->cx = cx;
|
|
arc->cy = cy;
|
|
if (arc->is_valid) {
|
|
arc->is_valid = find_angles(arc);
|
|
#if DEBUG_ARCS
|
|
check_endpoints(arc);
|
|
#endif
|
|
/* remap a few points. find_angles requires
|
|
* rot in angles, the rest of the code
|
|
* will need them in radians. and find_angles
|
|
* modifies the center to match an identity
|
|
* circle so lets reset it */
|
|
arc->theta = DEGREES_TO_RADIANS(rot);
|
|
arc->cos_theta = cos(arc->theta);
|
|
arc->sin_theta = sin(arc->theta);
|
|
arc->cx = cx;
|
|
arc->cy = cy;
|
|
}
|
|
}
|
|
}
|
|
|
|
static INLINE double rational_function(double x, const double *c)
|
|
{
|
|
return (x * (x * c[0] + c[1]) + c[2]) / (x + c[3]);
|
|
}
|
|
|
|
static double estimate_error(struct arc *arc,
|
|
double etaA, double etaB)
|
|
{
|
|
double eta = 0.5 * (etaA + etaB);
|
|
|
|
double x = arc->b / arc->a;
|
|
double dEta = etaB - etaA;
|
|
double cos2 = cos(2 * eta);
|
|
double cos4 = cos(4 * eta);
|
|
double cos6 = cos(6 * eta);
|
|
double c0, c1;
|
|
|
|
/* select the right coeficients set according to degree and b/a */
|
|
const double (*coeffs)[4][4];
|
|
const double *safety;
|
|
coeffs = (x < 0.25) ? coeffs3Low : coeffs3High;
|
|
safety = safety3;
|
|
|
|
c0 = rational_function(x, coeffs[0][0])
|
|
+ cos2 * rational_function(x, coeffs[0][1])
|
|
+ cos4 * rational_function(x, coeffs[0][2])
|
|
+ cos6 * rational_function(x, coeffs[0][3]);
|
|
|
|
c1 = rational_function(x, coeffs[1][0])
|
|
+ cos2 * rational_function(x, coeffs[1][1])
|
|
+ cos4 * rational_function(x, coeffs[1][2])
|
|
+ cos6 * rational_function(x, coeffs[1][3]);
|
|
|
|
return rational_function(x, safety) * arc->a * exp(c0 + c1 * dEta);
|
|
}
|
|
|
|
struct arc_cb {
|
|
void (*move)(struct arc_cb *cb, VGfloat x, VGfloat y);
|
|
void (*point)(struct arc_cb *cb, VGfloat x, VGfloat y);
|
|
void (*bezier)(struct arc_cb *cb, struct bezier *bezier);
|
|
|
|
void *user_data;
|
|
};
|
|
|
|
static void cb_null_move(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
}
|
|
|
|
static void polygon_point(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
struct polygon *poly = (struct polygon*)cb->user_data;
|
|
polygon_vertex_append(poly, x, y);
|
|
}
|
|
|
|
static void polygon_bezier(struct arc_cb *cb, struct bezier *bezier)
|
|
{
|
|
struct polygon *poly = (struct polygon*)cb->user_data;
|
|
bezier_add_to_polygon(bezier, poly);
|
|
}
|
|
|
|
static void stroke_point(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
struct stroker *stroker = (struct stroker*)cb->user_data;
|
|
stroker_line_to(stroker, x, y);
|
|
}
|
|
|
|
static void stroke_curve(struct arc_cb *cb, struct bezier *bezier)
|
|
{
|
|
struct stroker *stroker = (struct stroker*)cb->user_data;
|
|
stroker_curve_to(stroker,
|
|
bezier->x2, bezier->y2,
|
|
bezier->x3, bezier->y3,
|
|
bezier->x4, bezier->y4);
|
|
}
|
|
|
|
static void stroke_emit_point(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
struct stroker *stroker = (struct stroker*)cb->user_data;
|
|
stroker_emit_line_to(stroker, x, y);
|
|
}
|
|
|
|
static void stroke_emit_curve(struct arc_cb *cb, struct bezier *bezier)
|
|
{
|
|
struct stroker *stroker = (struct stroker*)cb->user_data;
|
|
stroker_emit_curve_to(stroker,
|
|
bezier->x2, bezier->y2,
|
|
bezier->x3, bezier->y3,
|
|
bezier->x4, bezier->y4);
|
|
}
|
|
|
|
static void arc_path_move(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
struct path *path = (struct path*)cb->user_data;
|
|
path_move_to(path, x, y);
|
|
}
|
|
|
|
static void arc_path_point(struct arc_cb *cb, VGfloat x, VGfloat y)
|
|
{
|
|
struct path *path = (struct path*)cb->user_data;
|
|
path_line_to(path, x, y);
|
|
}
|
|
|
|
static void arc_path_bezier(struct arc_cb *cb, struct bezier *bezier)
|
|
{
|
|
struct path *path = (struct path*)cb->user_data;
|
|
path_cubic_to(path,
|
|
bezier->x2, bezier->y2,
|
|
bezier->x3, bezier->y3,
|
|
bezier->x4, bezier->y4);
|
|
}
|
|
|
|
static INLINE int num_beziers_needed(struct arc *arc)
|
|
{
|
|
double threshold = 0.05;
|
|
VGboolean found = VG_FALSE;
|
|
int n = 1;
|
|
double min_eta, max_eta;
|
|
|
|
min_eta = MIN2(arc->eta1, arc->eta2);
|
|
max_eta = MAX2(arc->eta1, arc->eta2);
|
|
|
|
while ((! found) && (n < 1024)) {
|
|
double d_eta = (max_eta - min_eta) / n;
|
|
if (d_eta <= 0.5 * M_PI) {
|
|
double eta_b = min_eta;
|
|
int i;
|
|
found = VG_TRUE;
|
|
for (i = 0; found && (i < n); ++i) {
|
|
double etaA = eta_b;
|
|
eta_b += d_eta;
|
|
found = (estimate_error(arc, etaA, eta_b) <= threshold);
|
|
}
|
|
}
|
|
n = n << 1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static void arc_to_beziers(struct arc *arc,
|
|
struct arc_cb cb,
|
|
struct matrix *matrix)
|
|
{
|
|
int i;
|
|
int n = 1;
|
|
double d_eta, eta_b, cos_eta_b,
|
|
sin_eta_b, a_cos_eta_b, b_sin_eta_b, a_sin_eta_b,
|
|
b_cos_eta_b, x_b, y_b, x_b_dot, y_b_dot, lx, ly;
|
|
double t, alpha;
|
|
|
|
{ /* always move to the start of the arc */
|
|
VGfloat x = arc->x1;
|
|
VGfloat y = arc->y1;
|
|
matrix_map_point(matrix, x, y, &x, &y);
|
|
cb.move(&cb, x, y);
|
|
}
|
|
|
|
if (!arc->is_valid) {
|
|
VGfloat x = arc->x2;
|
|
VGfloat y = arc->y2;
|
|
matrix_map_point(matrix, x, y, &x, &y);
|
|
cb.point(&cb, x, y);
|
|
return;
|
|
}
|
|
|
|
/* find the number of Bézier curves needed */
|
|
n = num_beziers_needed(arc);
|
|
|
|
d_eta = (arc->eta2 - arc->eta1) / n;
|
|
eta_b = arc->eta1;
|
|
|
|
cos_eta_b = cos(eta_b);
|
|
sin_eta_b = sin(eta_b);
|
|
a_cos_eta_b = arc->a * cos_eta_b;
|
|
b_sin_eta_b = arc->b * sin_eta_b;
|
|
a_sin_eta_b = arc->a * sin_eta_b;
|
|
b_cos_eta_b = arc->b * cos_eta_b;
|
|
x_b = arc->cx + a_cos_eta_b * arc->cos_theta -
|
|
b_sin_eta_b * arc->sin_theta;
|
|
y_b = arc->cy + a_cos_eta_b * arc->sin_theta +
|
|
b_sin_eta_b * arc->cos_theta;
|
|
x_b_dot = -a_sin_eta_b * arc->cos_theta -
|
|
b_cos_eta_b * arc->sin_theta;
|
|
y_b_dot = -a_sin_eta_b * arc->sin_theta +
|
|
b_cos_eta_b * arc->cos_theta;
|
|
|
|
{
|
|
VGfloat x = x_b, y = y_b;
|
|
matrix_map_point(matrix, x, y, &x, &y);
|
|
cb.point(&cb, x, y);
|
|
}
|
|
lx = x_b;
|
|
ly = y_b;
|
|
|
|
t = tan(0.5 * d_eta);
|
|
alpha = sin(d_eta) * (sqrt(4 + 3 * t * t) - 1) / 3;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
struct bezier bezier;
|
|
double xA = x_b;
|
|
double yA = y_b;
|
|
double xADot = x_b_dot;
|
|
double yADot = y_b_dot;
|
|
|
|
eta_b += d_eta;
|
|
cos_eta_b = cos(eta_b);
|
|
sin_eta_b = sin(eta_b);
|
|
a_cos_eta_b = arc->a * cos_eta_b;
|
|
b_sin_eta_b = arc->b * sin_eta_b;
|
|
a_sin_eta_b = arc->a * sin_eta_b;
|
|
b_cos_eta_b = arc->b * cos_eta_b;
|
|
x_b = arc->cx + a_cos_eta_b * arc->cos_theta -
|
|
b_sin_eta_b * arc->sin_theta;
|
|
y_b = arc->cy + a_cos_eta_b * arc->sin_theta +
|
|
b_sin_eta_b * arc->cos_theta;
|
|
x_b_dot = -a_sin_eta_b * arc->cos_theta -
|
|
b_cos_eta_b * arc->sin_theta;
|
|
y_b_dot = -a_sin_eta_b * arc->sin_theta +
|
|
b_cos_eta_b * arc->cos_theta;
|
|
|
|
bezier_init(&bezier,
|
|
lx, ly,
|
|
(float) (xA + alpha * xADot), (float) (yA + alpha * yADot),
|
|
(float) (x_b - alpha * x_b_dot), (float) (y_b - alpha * y_b_dot),
|
|
(float) x_b, (float) y_b);
|
|
#if 0
|
|
debug_printf("%d) Bezier (%f, %f), (%f, %f), (%f, %f), (%f, %f)\n",
|
|
i,
|
|
bezier.x1, bezier.y1,
|
|
bezier.x2, bezier.y2,
|
|
bezier.x3, bezier.y3,
|
|
bezier.x4, bezier.y4);
|
|
#endif
|
|
bezier_transform(&bezier, matrix);
|
|
cb.bezier(&cb, &bezier);
|
|
lx = x_b;
|
|
ly = y_b;
|
|
}
|
|
}
|
|
|
|
|
|
void arc_add_to_polygon(struct arc *arc,
|
|
struct polygon *poly,
|
|
struct matrix *matrix)
|
|
{
|
|
struct arc_cb cb;
|
|
|
|
cb.move = cb_null_move;
|
|
cb.point = polygon_point;
|
|
cb.bezier = polygon_bezier;
|
|
cb.user_data = poly;
|
|
|
|
arc_to_beziers(arc, cb, matrix);
|
|
}
|
|
|
|
void arc_stroke_cb(struct arc *arc,
|
|
struct stroker *stroke,
|
|
struct matrix *matrix)
|
|
{
|
|
struct arc_cb cb;
|
|
|
|
cb.move = cb_null_move;
|
|
cb.point = stroke_point;
|
|
cb.bezier = stroke_curve;
|
|
cb.user_data = stroke;
|
|
|
|
arc_to_beziers(arc, cb, matrix);
|
|
}
|
|
|
|
void arc_stroker_emit(struct arc *arc,
|
|
struct stroker *stroker,
|
|
struct matrix *matrix)
|
|
{
|
|
struct arc_cb cb;
|
|
|
|
cb.move = cb_null_move;
|
|
cb.point = stroke_emit_point;
|
|
cb.bezier = stroke_emit_curve;
|
|
cb.user_data = stroker;
|
|
|
|
arc_to_beziers(arc, cb, matrix);
|
|
}
|
|
|
|
void arc_to_path(struct arc *arc,
|
|
struct path *path,
|
|
struct matrix *matrix)
|
|
{
|
|
struct arc_cb cb;
|
|
|
|
cb.move = arc_path_move;
|
|
cb.point = arc_path_point;
|
|
cb.bezier = arc_path_bezier;
|
|
cb.user_data = path;
|
|
|
|
arc_to_beziers(arc, cb, matrix);
|
|
}
|