1 /**************************************************************************
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 **************************************************************************/
35 #include "util/u_debug.h"
36 #include "util/u_math.h"
39 #define M_PI 3.14159265358979323846
44 #if !defined(__AROS__)
45 static const VGfloat two_pi
= M_PI
* 2;
48 static const double coeffs3Low
[2][4][4] = {
50 { 3.85268, -21.229, -0.330434, 0.0127842 },
51 { -1.61486, 0.706564, 0.225945, 0.263682 },
52 { -0.910164, 0.388383, 0.00551445, 0.00671814 },
53 { -0.630184, 0.192402, 0.0098871, 0.0102527 }
56 { -0.162211, 9.94329, 0.13723, 0.0124084 },
57 { -0.253135, 0.00187735, 0.0230286, 0.01264 },
58 { -0.0695069, -0.0437594, 0.0120636, 0.0163087 },
59 { -0.0328856, -0.00926032, -0.00173573, 0.00527385 }
63 /* coefficients for error estimation
64 while using cubic Bézier curves for approximation
66 static const double coeffs3High
[2][4][4] = {
68 { 0.0899116, -19.2349, -4.11711, 0.183362 },
69 { 0.138148, -1.45804, 1.32044, 1.38474 },
70 { 0.230903, -0.450262, 0.219963, 0.414038 },
71 { 0.0590565, -0.101062, 0.0430592, 0.0204699 }
74 { 0.0164649, 9.89394, 0.0919496, 0.00760802 },
75 { 0.0191603, -0.0322058, 0.0134667, -0.0825018 },
76 { 0.0156192, -0.017535, 0.00326508, -0.228157 },
77 { -0.0236752, 0.0405821, -0.0173086, 0.176187 }
81 /* safety factor to convert the "best" error approximation
82 into a "max bound" error */
83 static const double safety3
[] = {
84 0.001, 4.98, 0.207, 0.0067
87 /* The code below is from the OpenVG 1.1 Spec
90 /* Given: Points (x0, y0) and (x1, y1)
91 * Return: TRUE if a solution exists, FALSE otherwise
92 * Circle centers are written to (cx0, cy0) and (cx1, cy1)
95 find_unit_circles(double x0
, double y0
, double x1
, double y1
,
96 double *cx0
, double *cy0
,
97 double *cx1
, double *cy1
)
99 /* Compute differences and averages */
102 double xm
= (x0
+ x1
)/2;
103 double ym
= (y0
+ y1
)/2;
104 double dsq
, disc
, s
, sdx
, sdy
;
106 /* Solve for intersecting unit circles */
108 if (dsq
== 0.0) return VG_FALSE
; /* Points are coincident */
109 disc
= 1.0/dsq
- 1.0/4.0;
111 /* the precision we care about here is around float so if we're
112 * around the float defined zero then make it official to avoid
113 * precision problems later on */
114 if (floatIsZero(disc
))
117 if (disc
< 0.0) return VG_FALSE
; /* Points are too far apart */
129 /* Given: Ellipse parameters rh, rv, rot (in degrees),
130 * endpoints (x0, y0) and (x1, y1)
131 * Return: TRUE if a solution exists, FALSE otherwise
132 * Ellipse centers are written to (cx0, cy0) and (cx1, cy1)
135 find_ellipses(double rh
, double rv
, double rot
,
136 double x0
, double y0
, double x1
, double y1
,
137 double *cx0
, double *cy0
, double *cx1
, double *cy1
)
139 double COS
, SIN
, x0p
, y0p
, x1p
, y1p
, pcx0
, pcy0
, pcx1
, pcy1
;
140 /* Convert rotation angle from degrees to radians */
142 /* Pre-compute rotation matrix entries */
143 COS
= cos(rot
); SIN
= sin(rot
);
144 /* Transform (x0, y0) and (x1, y1) into unit space */
145 /* using (inverse) rotate, followed by (inverse) scale */
146 x0p
= (x0
*COS
+ y0
*SIN
)/rh
;
147 y0p
= (-x0
*SIN
+ y0
*COS
)/rv
;
148 x1p
= (x1
*COS
+ y1
*SIN
)/rh
;
149 y1p
= (-x1
*SIN
+ y1
*COS
)/rv
;
150 if (!find_unit_circles(x0p
, y0p
, x1p
, y1p
,
151 &pcx0
, &pcy0
, &pcx1
, &pcy1
)) {
154 /* Transform back to original coordinate space */
155 /* using (forward) scale followed by (forward) rotate */
156 pcx0
*= rh
; pcy0
*= rv
;
157 pcx1
*= rh
; pcy1
*= rv
;
158 *cx0
= pcx0
*COS
- pcy0
*SIN
;
159 *cy0
= pcx0
*SIN
+ pcy0
*COS
;
160 *cx1
= pcx1
*COS
- pcy1
*SIN
;
161 *cy1
= pcx1
*SIN
+ pcy1
*COS
;
165 static INLINE VGboolean
166 try_to_fix_radii(struct arc
*arc
)
168 double COS
, SIN
, rot
, x0p
, y0p
, x1p
, y1p
;
169 double dx
, dy
, dsq
, scale
;
171 /* Convert rotation angle from degrees to radians */
172 rot
= DEGREES_TO_RADIANS(arc
->theta
);
174 /* Pre-compute rotation matrix entries */
175 COS
= cos(rot
); SIN
= sin(rot
);
177 /* Transform (x0, y0) and (x1, y1) into unit space */
178 /* using (inverse) rotate, followed by (inverse) scale */
179 x0p
= (arc
->x1
*COS
+ arc
->y1
*SIN
)/arc
->a
;
180 y0p
= (-arc
->x1
*SIN
+ arc
->y1
*COS
)/arc
->b
;
181 x1p
= (arc
->x2
*COS
+ arc
->y2
*SIN
)/arc
->a
;
182 y1p
= (-arc
->x2
*SIN
+ arc
->y2
*COS
)/arc
->b
;
183 /* Compute differences and averages */
190 debug_printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaa\n");
193 scale
= 1/(2/sqrt(dsq
));
199 static INLINE
double vector_normalize(double *v
)
201 double sq
= v
[0] * v
[0] + v
[1] * v
[1];
204 static INLINE
double vector_orientation(double *v
)
206 double norm
= vector_normalize(v
);
207 double cosa
= v
[0] / norm
;
208 double sina
= v
[1] / norm
;
209 return (sina
>=0 ? acos(cosa
) : 2*M_PI
- acos(cosa
));
211 static INLINE
double vector_dot(double *v0
,
214 return v0
[0] * v1
[0] + v0
[1] * v1
[1];
217 static INLINE
double vector_angles(double *v0
,
220 double dot
= vector_dot(v0
, v1
);
221 double norm0
= vector_normalize(v0
);
222 double norm1
= vector_normalize(v1
);
224 return acos(dot
/ (norm0
* norm1
));
227 static VGboolean
find_angles(struct arc
*arc
)
229 double vec0
[2], vec1
[2];
230 double lambda1
, lambda2
;
232 struct matrix matrix
;
234 if (floatIsZero(arc
->a
) || floatIsZero(arc
->b
)) {
237 /* map the points to an identity circle */
238 matrix_load_identity(&matrix
);
239 matrix_scale(&matrix
, 1.f
, arc
->a
/arc
->b
);
240 matrix_rotate(&matrix
, -arc
->theta
);
241 matrix_map_point(&matrix
,
244 matrix_map_point(&matrix
,
247 matrix_map_point(&matrix
,
252 debug_printf("Matrix 3 [%f, %f, %f| %f, %f, %f| %f, %f, %f]\n",
253 matrix
.m
[0], matrix
.m
[1], matrix
.m
[2],
254 matrix
.m
[3], matrix
.m
[4], matrix
.m
[5],
255 matrix
.m
[6], matrix
.m
[7], matrix
.m
[8]);
256 debug_printf("Endpoints [%f, %f], [%f, %f]\n",
257 arc
->x1
, arc
->y1
, arc
->x2
, arc
->y2
);
260 vec0
[0] = arc
->x1
- arc
->cx
;
261 vec0
[1] = arc
->y1
- arc
->cy
;
262 vec1
[0] = arc
->x2
- arc
->cx
;
263 vec1
[1] = arc
->y2
- arc
->cy
;
266 debug_printf("Vec is [%f, %f], [%f, %f], [%f, %f]\n",
267 vec0
[0], vec0
[1], vec1
[0], vec1
[1], arc
->cx
, arc
->cy
);
270 lambda1
= vector_orientation(vec0
);
275 if (arc
->type
== VG_SCWARC_TO
||
276 arc
->type
== VG_SCCWARC_TO
)
277 angle
= vector_angles(vec0
, vec1
);
278 else if (arc
->type
== VG_LCWARC_TO
||
279 arc
->type
== VG_LCCWARC_TO
) {
280 angle
= 2*M_PI
- vector_angles(vec0
, vec1
);
288 if (arc
->type
== VG_SCWARC_TO
||
289 arc
->type
== VG_LCWARC_TO
)
290 lambda2
= lambda1
- angle
;
292 lambda2
= lambda1
+ angle
;
295 debug_printf("Angle is %f and (%f, %f)\n", angle
, lambda1
, lambda2
);
299 arc
->eta1
= atan2(sin(lambda1
) / arc
->b
,
300 cos(lambda1
) / arc
->a
);
301 arc
->eta2
= atan2(sin(lambda2
) / arc
->b
,
302 cos(lambda2
) / arc
->a
);
304 /* make sure we have eta1 <= eta2 <= eta1 + 2 PI */
305 arc
->eta2
-= two_pi
* floor((arc
->eta2
- arc
->eta1
) / two_pi
);
307 /* the preceding correction fails if we have exactly et2 - eta1 = 2 PI
308 it reduces the interval to zero length */
309 if ((lambda2
- lambda1
> M_PI
) && (arc
->eta2
- arc
->eta1
< M_PI
)) {
310 arc
->eta2
+= 2 * M_PI
;
321 static void check_endpoints(struct arc
*arc
)
323 double x1
, y1
, x2
, y2
;
325 double a_cos_eta1
= arc
->a
* cos(arc
->eta1
);
326 double b_sin_eta1
= arc
->b
* sin(arc
->eta1
);
327 x1
= arc
->cx
+ a_cos_eta1
* arc
->cos_theta
-
328 b_sin_eta1
* arc
->sin_theta
;
329 y1
= arc
->cy
+ a_cos_eta1
* arc
->sin_theta
+
330 b_sin_eta1
* arc
->cos_theta
;
332 double a_cos_eta2
= arc
->a
* cos(arc
->eta2
);
333 double b_sin_eta2
= arc
->b
* sin(arc
->eta2
);
334 x2
= arc
->cx
+ a_cos_eta2
* arc
->cos_theta
-
335 b_sin_eta2
* arc
->sin_theta
;
336 y2
= arc
->cy
+ a_cos_eta2
* arc
->sin_theta
+
337 b_sin_eta2
* arc
->cos_theta
;
339 debug_printf("Computed (%f, %f), (%f, %f)\n",
341 debug_printf("Real (%f, %f), (%f, %f)\n",
347 void arc_init(struct arc
*arc
,
349 VGfloat x1
, VGfloat y1
,
350 VGfloat x2
, VGfloat y2
,
351 VGfloat rh
, VGfloat rv
,
354 assert(type
== VG_SCCWARC_TO
||
355 type
== VG_SCWARC_TO
||
356 type
== VG_LCCWARC_TO
||
357 type
== VG_LCWARC_TO
);
366 arc
->cos_theta
= cos(arc
->theta
);
367 arc
->sin_theta
= sin(arc
->theta
);
369 double cx0
, cy0
, cx1
, cy1
;
371 arc
->is_valid
= find_ellipses(rh
, rv
, rot
, x1
, y1
, x2
, y2
,
372 &cx0
, &cy0
, &cx1
, &cy1
);
374 if (!arc
->is_valid
&& try_to_fix_radii(arc
)) {
378 find_ellipses(rh
, rv
, rot
, x1
, y1
, x2
, y2
,
379 &cx0
, &cy0
, &cx1
, &cy1
);
382 if (type
== VG_SCWARC_TO
||
383 type
== VG_LCCWARC_TO
) {
391 debug_printf("Centers are : (%f, %f) , (%f, %f). Real (%f, %f)\n",
392 cx0
, cy0
, cx1
, cy1
, cx
, cy
);
397 arc
->is_valid
= find_angles(arc
);
399 check_endpoints(arc
);
401 /* remap a few points. find_angles requires
402 * rot in angles, the rest of the code
403 * will need them in radians. and find_angles
404 * modifies the center to match an identity
405 * circle so lets reset it */
406 arc
->theta
= DEGREES_TO_RADIANS(rot
);
407 arc
->cos_theta
= cos(arc
->theta
);
408 arc
->sin_theta
= sin(arc
->theta
);
415 static INLINE
double rational_function(double x
, const double *c
)
417 return (x
* (x
* c
[0] + c
[1]) + c
[2]) / (x
+ c
[3]);
420 static double estimate_error(struct arc
*arc
,
421 double etaA
, double etaB
)
423 double eta
= 0.5 * (etaA
+ etaB
);
425 double x
= arc
->b
/ arc
->a
;
426 double dEta
= etaB
- etaA
;
427 double cos2
= cos(2 * eta
);
428 double cos4
= cos(4 * eta
);
429 double cos6
= cos(6 * eta
);
432 /* select the right coeficients set according to degree and b/a */
433 const double (*coeffs
)[4][4];
434 const double *safety
;
435 coeffs
= (x
< 0.25) ? coeffs3Low
: coeffs3High
;
438 c0
= rational_function(x
, coeffs
[0][0])
439 + cos2
* rational_function(x
, coeffs
[0][1])
440 + cos4
* rational_function(x
, coeffs
[0][2])
441 + cos6
* rational_function(x
, coeffs
[0][3]);
443 c1
= rational_function(x
, coeffs
[1][0])
444 + cos2
* rational_function(x
, coeffs
[1][1])
445 + cos4
* rational_function(x
, coeffs
[1][2])
446 + cos6
* rational_function(x
, coeffs
[1][3]);
448 return rational_function(x
, safety
) * arc
->a
* exp(c0
+ c1
* dEta
);
452 void (*move
)(struct arc_cb
*cb
, VGfloat x
, VGfloat y
);
453 void (*point
)(struct arc_cb
*cb
, VGfloat x
, VGfloat y
);
454 void (*bezier
)(struct arc_cb
*cb
, struct bezier
*bezier
);
459 static void cb_null_move(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
463 static void polygon_point(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
465 struct polygon
*poly
= (struct polygon
*)cb
->user_data
;
466 polygon_vertex_append(poly
, x
, y
);
469 static void polygon_bezier(struct arc_cb
*cb
, struct bezier
*bezier
)
471 struct polygon
*poly
= (struct polygon
*)cb
->user_data
;
472 bezier_add_to_polygon(bezier
, poly
);
475 static void stroke_point(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
477 struct stroker
*stroker
= (struct stroker
*)cb
->user_data
;
478 stroker_line_to(stroker
, x
, y
);
481 static void stroke_curve(struct arc_cb
*cb
, struct bezier
*bezier
)
483 struct stroker
*stroker
= (struct stroker
*)cb
->user_data
;
484 stroker_curve_to(stroker
,
485 bezier
->x2
, bezier
->y2
,
486 bezier
->x3
, bezier
->y3
,
487 bezier
->x4
, bezier
->y4
);
490 static void stroke_emit_point(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
492 struct stroker
*stroker
= (struct stroker
*)cb
->user_data
;
493 stroker_emit_line_to(stroker
, x
, y
);
496 static void stroke_emit_curve(struct arc_cb
*cb
, struct bezier
*bezier
)
498 struct stroker
*stroker
= (struct stroker
*)cb
->user_data
;
499 stroker_emit_curve_to(stroker
,
500 bezier
->x2
, bezier
->y2
,
501 bezier
->x3
, bezier
->y3
,
502 bezier
->x4
, bezier
->y4
);
505 static void arc_path_move(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
507 struct path
*path
= (struct path
*)cb
->user_data
;
508 path_move_to(path
, x
, y
);
511 static void arc_path_point(struct arc_cb
*cb
, VGfloat x
, VGfloat y
)
513 struct path
*path
= (struct path
*)cb
->user_data
;
514 path_line_to(path
, x
, y
);
517 static void arc_path_bezier(struct arc_cb
*cb
, struct bezier
*bezier
)
519 struct path
*path
= (struct path
*)cb
->user_data
;
521 bezier
->x2
, bezier
->y2
,
522 bezier
->x3
, bezier
->y3
,
523 bezier
->x4
, bezier
->y4
);
526 static INLINE
int num_beziers_needed(struct arc
*arc
)
528 double threshold
= 0.05;
529 VGboolean found
= VG_FALSE
;
531 double min_eta
, max_eta
;
533 min_eta
= MIN2(arc
->eta1
, arc
->eta2
);
534 max_eta
= MAX2(arc
->eta1
, arc
->eta2
);
536 while ((! found
) && (n
< 1024)) {
537 double d_eta
= (max_eta
- min_eta
) / n
;
538 if (d_eta
<= 0.5 * M_PI
) {
539 double eta_b
= min_eta
;
542 for (i
= 0; found
&& (i
< n
); ++i
) {
545 found
= (estimate_error(arc
, etaA
, eta_b
) <= threshold
);
554 static void arc_to_beziers(struct arc
*arc
,
556 struct matrix
*matrix
)
560 double d_eta
, eta_b
, cos_eta_b
,
561 sin_eta_b
, a_cos_eta_b
, b_sin_eta_b
, a_sin_eta_b
,
562 b_cos_eta_b
, x_b
, y_b
, x_b_dot
, y_b_dot
, lx
, ly
;
565 { /* always move to the start of the arc */
568 matrix_map_point(matrix
, x
, y
, &x
, &y
);
572 if (!arc
->is_valid
) {
575 matrix_map_point(matrix
, x
, y
, &x
, &y
);
580 /* find the number of Bézier curves needed */
581 n
= num_beziers_needed(arc
);
583 d_eta
= (arc
->eta2
- arc
->eta1
) / n
;
586 cos_eta_b
= cos(eta_b
);
587 sin_eta_b
= sin(eta_b
);
588 a_cos_eta_b
= arc
->a
* cos_eta_b
;
589 b_sin_eta_b
= arc
->b
* sin_eta_b
;
590 a_sin_eta_b
= arc
->a
* sin_eta_b
;
591 b_cos_eta_b
= arc
->b
* cos_eta_b
;
592 x_b
= arc
->cx
+ a_cos_eta_b
* arc
->cos_theta
-
593 b_sin_eta_b
* arc
->sin_theta
;
594 y_b
= arc
->cy
+ a_cos_eta_b
* arc
->sin_theta
+
595 b_sin_eta_b
* arc
->cos_theta
;
596 x_b_dot
= -a_sin_eta_b
* arc
->cos_theta
-
597 b_cos_eta_b
* arc
->sin_theta
;
598 y_b_dot
= -a_sin_eta_b
* arc
->sin_theta
+
599 b_cos_eta_b
* arc
->cos_theta
;
602 VGfloat x
= x_b
, y
= y_b
;
603 matrix_map_point(matrix
, x
, y
, &x
, &y
);
609 t
= tan(0.5 * d_eta
);
610 alpha
= sin(d_eta
) * (sqrt(4 + 3 * t
* t
) - 1) / 3;
612 for (i
= 0; i
< n
; ++i
) {
613 struct bezier bezier
;
616 double xADot
= x_b_dot
;
617 double yADot
= y_b_dot
;
620 cos_eta_b
= cos(eta_b
);
621 sin_eta_b
= sin(eta_b
);
622 a_cos_eta_b
= arc
->a
* cos_eta_b
;
623 b_sin_eta_b
= arc
->b
* sin_eta_b
;
624 a_sin_eta_b
= arc
->a
* sin_eta_b
;
625 b_cos_eta_b
= arc
->b
* cos_eta_b
;
626 x_b
= arc
->cx
+ a_cos_eta_b
* arc
->cos_theta
-
627 b_sin_eta_b
* arc
->sin_theta
;
628 y_b
= arc
->cy
+ a_cos_eta_b
* arc
->sin_theta
+
629 b_sin_eta_b
* arc
->cos_theta
;
630 x_b_dot
= -a_sin_eta_b
* arc
->cos_theta
-
631 b_cos_eta_b
* arc
->sin_theta
;
632 y_b_dot
= -a_sin_eta_b
* arc
->sin_theta
+
633 b_cos_eta_b
* arc
->cos_theta
;
637 (float) (xA
+ alpha
* xADot
), (float) (yA
+ alpha
* yADot
),
638 (float) (x_b
- alpha
* x_b_dot
), (float) (y_b
- alpha
* y_b_dot
),
639 (float) x_b
, (float) y_b
);
641 debug_printf("%d) Bezier (%f, %f), (%f, %f), (%f, %f), (%f, %f)\n",
643 bezier
.x1
, bezier
.y1
,
644 bezier
.x2
, bezier
.y2
,
645 bezier
.x3
, bezier
.y3
,
646 bezier
.x4
, bezier
.y4
);
648 bezier_transform(&bezier
, matrix
);
649 cb
.bezier(&cb
, &bezier
);
656 void arc_add_to_polygon(struct arc
*arc
,
657 struct polygon
*poly
,
658 struct matrix
*matrix
)
662 cb
.move
= cb_null_move
;
663 cb
.point
= polygon_point
;
664 cb
.bezier
= polygon_bezier
;
667 arc_to_beziers(arc
, cb
, matrix
);
670 void arc_stroke_cb(struct arc
*arc
,
671 struct stroker
*stroke
,
672 struct matrix
*matrix
)
676 cb
.move
= cb_null_move
;
677 cb
.point
= stroke_point
;
678 cb
.bezier
= stroke_curve
;
679 cb
.user_data
= stroke
;
681 arc_to_beziers(arc
, cb
, matrix
);
684 void arc_stroker_emit(struct arc
*arc
,
685 struct stroker
*stroker
,
686 struct matrix
*matrix
)
690 cb
.move
= cb_null_move
;
691 cb
.point
= stroke_emit_point
;
692 cb
.bezier
= stroke_emit_curve
;
693 cb
.user_data
= stroker
;
695 arc_to_beziers(arc
, cb
, matrix
);
698 void arc_to_path(struct arc
*arc
,
700 struct matrix
*matrix
)
704 cb
.move
= arc_path_move
;
705 cb
.point
= arc_path_point
;
706 cb
.bezier
= arc_path_bezier
;
709 arc_to_beziers(arc
, cb
, matrix
);