beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-arc.c
blob390397bae10426d2be99da45ae1717c7832207c4
1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2002 University of Southern California
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.
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
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/
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.
28 * The Original Code is the cairo graphics library.
30 * The Initial Developer of the Original Code is University of Southern
31 * California.
33 * Contributor(s):
34 * Carl D. Worth <cworth@cworth.org>
37 #include "cairoint.h"
39 #include "cairo-arc-private.h"
41 #define MAX_FULL_CIRCLES 65536
43 /* Spline deviation from the circle in radius would be given by:
45 error = sqrt (x**2 + y**2) - 1
47 A simpler error function to work with is:
49 e = x**2 + y**2 - 1
51 From "Good approximation of circles by curvature-continuous Bezier
52 curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric
53 Design 8 (1990) 22-41, we learn:
55 abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4)
57 and
58 abs (error) =~ 1/2 * e
60 Of course, this error value applies only for the particular spline
61 approximation that is used in _cairo_gstate_arc_segment.
63 static double
64 _arc_error_normalized (double angle)
66 return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
69 static double
70 _arc_max_angle_for_tolerance_normalized (double tolerance)
72 double angle, error;
73 int i;
75 /* Use table lookup to reduce search time in most cases. */
76 struct {
77 double angle;
78 double error;
79 } table[] = {
80 { M_PI / 1.0, 0.0185185185185185036127 },
81 { M_PI / 2.0, 0.000272567143730179811158 },
82 { M_PI / 3.0, 2.38647043651461047433e-05 },
83 { M_PI / 4.0, 4.2455377443222443279e-06 },
84 { M_PI / 5.0, 1.11281001494389081528e-06 },
85 { M_PI / 6.0, 3.72662000942734705475e-07 },
86 { M_PI / 7.0, 1.47783685574284411325e-07 },
87 { M_PI / 8.0, 6.63240432022601149057e-08 },
88 { M_PI / 9.0, 3.2715520137536980553e-08 },
89 { M_PI / 10.0, 1.73863223499021216974e-08 },
90 { M_PI / 11.0, 9.81410988043554039085e-09 },
92 int table_size = ARRAY_LENGTH (table);
94 for (i = 0; i < table_size; i++)
95 if (table[i].error < tolerance)
96 return table[i].angle;
98 ++i;
99 do {
100 angle = M_PI / i++;
101 error = _arc_error_normalized (angle);
102 } while (error > tolerance);
104 return angle;
107 static int
108 _arc_segments_needed (double angle,
109 double radius,
110 cairo_matrix_t *ctm,
111 double tolerance)
113 double major_axis, max_angle;
115 /* the error is amplified by at most the length of the
116 * major axis of the circle; see cairo-pen.c for a more detailed analysis
117 * of this. */
118 major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius);
119 max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis);
121 return ceil (fabs (angle) / max_angle);
124 /* We want to draw a single spline approximating a circular arc radius
125 R from angle A to angle B. Since we want a symmetric spline that
126 matches the endpoints of the arc in position and slope, we know
127 that the spline control points must be:
129 (R * cos(A), R * sin(A))
130 (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A))
131 (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B))
132 (R * cos(B), R * sin(B))
134 for some value of h.
136 "Approximation of circular arcs by cubic polynomials", Michael
137 Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
138 various values of h along with error analysis for each.
140 From that paper, a very practical value of h is:
142 h = 4/3 * R * tan(angle/4)
144 This value does not give the spline with minimal error, but it does
145 provide a very good approximation, (6th-order convergence), and the
146 error expression is quite simple, (see the comment for
147 _arc_error_normalized).
149 static void
150 _cairo_arc_segment (cairo_t *cr,
151 double xc,
152 double yc,
153 double radius,
154 double angle_A,
155 double angle_B)
157 double r_sin_A, r_cos_A;
158 double r_sin_B, r_cos_B;
159 double h;
161 r_sin_A = radius * sin (angle_A);
162 r_cos_A = radius * cos (angle_A);
163 r_sin_B = radius * sin (angle_B);
164 r_cos_B = radius * cos (angle_B);
166 h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
168 cairo_curve_to (cr,
169 xc + r_cos_A - h * r_sin_A,
170 yc + r_sin_A + h * r_cos_A,
171 xc + r_cos_B + h * r_sin_B,
172 yc + r_sin_B - h * r_cos_B,
173 xc + r_cos_B,
174 yc + r_sin_B);
177 static void
178 _cairo_arc_in_direction (cairo_t *cr,
179 double xc,
180 double yc,
181 double radius,
182 double angle_min,
183 double angle_max,
184 cairo_direction_t dir)
186 if (cairo_status (cr))
187 return;
189 assert (angle_max >= angle_min);
191 if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) {
192 angle_max = fmod (angle_max - angle_min, 2 * M_PI);
193 angle_min = fmod (angle_min, 2 * M_PI);
194 angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES;
197 /* Recurse if drawing arc larger than pi */
198 if (angle_max - angle_min > M_PI) {
199 double angle_mid = angle_min + (angle_max - angle_min) / 2.0;
200 if (dir == CAIRO_DIRECTION_FORWARD) {
201 _cairo_arc_in_direction (cr, xc, yc, radius,
202 angle_min, angle_mid,
203 dir);
205 _cairo_arc_in_direction (cr, xc, yc, radius,
206 angle_mid, angle_max,
207 dir);
208 } else {
209 _cairo_arc_in_direction (cr, xc, yc, radius,
210 angle_mid, angle_max,
211 dir);
213 _cairo_arc_in_direction (cr, xc, yc, radius,
214 angle_min, angle_mid,
215 dir);
217 } else if (angle_max != angle_min) {
218 cairo_matrix_t ctm;
219 int i, segments;
220 double step;
222 cairo_get_matrix (cr, &ctm);
223 segments = _arc_segments_needed (angle_max - angle_min,
224 radius, &ctm,
225 cairo_get_tolerance (cr));
226 step = (angle_max - angle_min) / segments;
227 segments -= 1;
229 if (dir == CAIRO_DIRECTION_REVERSE) {
230 double t;
232 t = angle_min;
233 angle_min = angle_max;
234 angle_max = t;
236 step = -step;
239 cairo_line_to (cr,
240 xc + radius * cos (angle_min),
241 yc + radius * sin (angle_min));
243 for (i = 0; i < segments; i++, angle_min += step) {
244 _cairo_arc_segment (cr, xc, yc, radius,
245 angle_min, angle_min + step);
248 _cairo_arc_segment (cr, xc, yc, radius,
249 angle_min, angle_max);
250 } else {
251 cairo_line_to (cr,
252 xc + radius * cos (angle_min),
253 yc + radius * sin (angle_min));
258 * _cairo_arc_path:
259 * @cr: a cairo context
260 * @xc: X position of the center of the arc
261 * @yc: Y position of the center of the arc
262 * @radius: the radius of the arc
263 * @angle1: the start angle, in radians
264 * @angle2: the end angle, in radians
266 * Compute a path for the given arc and append it onto the current
267 * path within @cr. The arc will be accurate within the current
268 * tolerance and given the current transformation.
270 void
271 _cairo_arc_path (cairo_t *cr,
272 double xc,
273 double yc,
274 double radius,
275 double angle1,
276 double angle2)
278 _cairo_arc_in_direction (cr, xc, yc,
279 radius,
280 angle1, angle2,
281 CAIRO_DIRECTION_FORWARD);
285 * _cairo_arc_path_negative:
286 * @xc: X position of the center of the arc
287 * @yc: Y position of the center of the arc
288 * @radius: the radius of the arc
289 * @angle1: the start angle, in radians
290 * @angle2: the end angle, in radians
291 * @ctm: the current transformation matrix
292 * @tolerance: the current tolerance value
293 * @path: the path onto which the arc will be appended
295 * Compute a path for the given arc (defined in the negative
296 * direction) and append it onto the current path within @cr. The arc
297 * will be accurate within the current tolerance and given the current
298 * transformation.
300 void
301 _cairo_arc_path_negative (cairo_t *cr,
302 double xc,
303 double yc,
304 double radius,
305 double angle1,
306 double angle2)
308 _cairo_arc_in_direction (cr, xc, yc,
309 radius,
310 angle2, angle1,
311 CAIRO_DIRECTION_REVERSE);