1 /* CPML - Cairo Path Manipulation Library
2 * Copyright (C) 2008, 2009 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 * @title: Circular arcs
23 * @short_description: Functions for manipulating circular arcs
25 * The following functions manipulate %CAIRO_PATH_ARC_TO #CpmlPrimitive.
26 * No check is made on the primitive struct, so be sure
27 * <structname>CpmlPrimitive</structname> is effectively an arc
28 * before calling these APIs.
30 * An arc is not a native cairo primitive and should be treated specially.
32 * The arc primitive is defined by 3 points: the first one is the usual
33 * implicit point got from the previous primitive, the second point is
34 * an arbitrary intermediate point laying on the arc and the third point
35 * is the end of the arc. These points identify univocally an arc:
36 * furthermore, the intermediate point also gives the "direction" of
39 * As a special case, when the first point is coincident with the end
40 * point, the primitive is considered a circle with diameter defined
41 * as the segment between the first point and the intermediate point.
45 #include "cpml-pair.h"
51 /* Hardcoded max angle of the arc to be approximated by a Bézier curve:
52 * this influence the arc quality (the default value is got from cairo) */
53 #define ARC_MAX_ANGLE (M_PI / 2.)
56 static cairo_bool_t
get_center (const CpmlPair
*p
,
58 static void get_angles (const CpmlPair
*p
,
59 const CpmlPair
*center
,
64 * cpml_arc_type_get_npoints:
66 * Returns the number of point needed to properly specify an arc primitive.
71 cpml_arc_type_get_npoints(void)
78 * @arc: the #CpmlPrimitive arc data
79 * @center: where to store the center coordinates (can be %NULL)
80 * @r: where to store the radius (can be %NULL)
81 * @start: where to store the starting angle (can be %NULL)
82 * @end: where to store the ending angle (can be %NULL)
84 * Given an @arc, this function calculates and returns its basic data.
85 * Any pointer can be %NULL, in which case the requested info is not
86 * returned. This function can fail (when the three points lay on a
87 * straight line, for example) in which case 0 is returned and no
88 * data can be considered valid.
90 * The radius @r can be 0 when the three points are coincidents: a
91 * circle with radius 0 is considered a valid path.
93 * When the start and end angle are returned, together with their
94 * values these angles implicitely gives another important information:
97 * If @start < @end the arc must be rendered with increasing angle
98 * value (clockwise direction using the ordinary cairo coordinate
99 * system) while if @start > @end the arc must be rendered in reverse
100 * order (that is counterclockwise in the cairo world). This is the
101 * reason the angle values are returned in the range
102 * { -M_PI < value < 3*M_PI } inclusive instead of the usual
103 * { -M_PI < value < M_PI } range.
105 * Return value: 1 if the function worked succesfully, 0 on errors
108 cpml_arc_info(const CpmlPrimitive
*arc
, CpmlPair
*center
,
109 double *r
, double *start
, double *end
)
111 CpmlPair p
[3], l_center
;
113 cpml_pair_from_cairo(&p
[0], arc
->org
);
114 cpml_pair_from_cairo(&p
[1], &arc
->data
[1]);
115 cpml_pair_from_cairo(&p
[2], &arc
->data
[2]);
117 if (!get_center(p
, &l_center
))
124 *r
= cpml_pair_distance(&p
[0], &l_center
);
126 if (start
!= NULL
|| end
!= NULL
) {
129 get_angles(p
, &l_center
, &angles
);
142 * @arc: the #CpmlPrimitive arc data
143 * @cr: the destination cairo context
145 * Appends the path of @primitive to @cr using cairo_append_path().
146 * As a special case, if the primitive is a #CAIRO_PATH_CLOSE_PATH,
147 * an equivalent line is rendered, because a close path left alone
149 * @pair: the destination #CpmlPair
151 * Given an @arc, this function renders it to the @cr cairo context
152 * approximating it using only Bézier curves.
155 cpml_arc_to_cairo(const CpmlPrimitive
*arc
, cairo_t
*cr
)
161 * @arc: the #CpmlPrimitive arc data
162 * @pair: the destination #CpmlPair
163 * @pos: the position value
165 * Given an @arc, finds the coordinates at position @pos (where 0 is
166 * the start and 1 is the end) and stores the result in @pair.
169 * <title>TODO</title>
171 * <listitem>To be implemented...</listitem>
176 cpml_arc_pair_at(const CpmlPrimitive
*arc
, CpmlPair
*pair
, double pos
)
181 * cpml_arc_vector_at:
182 * @arc: the #CpmlPrimitive arc data
183 * @vector: the destination vector
184 * @pos: the position value
186 * Given an @arc, finds the slope at position @pos (where 0 is
187 * the start and 1 is the end) and stores the result in @vector.
190 * <title>TODO</title>
192 * <listitem>To be implemented...</listitem>
197 cpml_arc_vector_at(const CpmlPrimitive
*arc
, CpmlVector
*vector
, double pos
)
202 * cpml_arc_intersection:
203 * @arc: the first arc
204 * @arc2: the second arc
205 * @dest: a vector of at least 2 #CpmlPair
207 * Given two arcs (@arc and @arc2), gets their intersection points
208 * and store the result in @dest. Because two arcs can have
209 * 2 intersections, @dest MUST be at least an array of 2 #CpmlPair.
212 * <title>TODO</title>
214 * <listitem>To be implemented...</listitem>
218 * Return value: the number of intersections (max 2)
221 cpml_arc_intersection(const CpmlPrimitive
*arc
,
222 const CpmlPrimitive
*arc2
, CpmlPair
*dest
)
228 * cpml_arc_intersection_with_line:
231 * @dest: a vector of at least 2 #CpmlPair
233 * Given an @arc and a @line, gets their intersection points
234 * and store the result in @dest. Because an arc and a line
235 * can have up to 2 intersections, @dest MUST be at least an
236 * array of 2 #CpmlPair.
239 * <title>TODO</title>
241 * <listitem>To be implemented...</listitem>
245 * Return value: the number of intersections (max 2)
248 cpml_arc_intersection_with_line(const CpmlPrimitive
*arc
,
249 const CpmlPrimitive
*line
, CpmlPair
*dest
)
256 * @arc: the #CpmlPrimitive arc data
257 * @offset: distance for the computed parallel arc
259 * Given an @arc, this function computes the parallel arc at
260 * distance @offset. The three points needed to build the
261 * new arc are returned in the @arc data (substituting the
265 cpml_arc_offset(CpmlPrimitive
*arc
, double offset
)
267 CpmlPair p
[3], center
;
270 cpml_pair_from_cairo(&p
[0], arc
->org
);
271 cpml_pair_from_cairo(&p
[1], &arc
->data
[1]);
272 cpml_pair_from_cairo(&p
[2], &arc
->data
[2]);
274 if (!get_center(p
, ¢er
))
277 r
= cpml_pair_distance(&p
[0], ¢er
) + offset
;
279 /* Offset the three points by calculating their vector from the center,
280 * setting the new radius as length and readding the center */
281 cpml_pair_sub(&p
[0], ¢er
);
282 cpml_pair_sub(&p
[1], ¢er
);
283 cpml_pair_sub(&p
[2], ¢er
);
285 cpml_vector_set_length(&p
[0], r
);
286 cpml_vector_set_length(&p
[1], r
);
287 cpml_vector_set_length(&p
[2], r
);
289 cpml_pair_add(&p
[0], ¢er
);
290 cpml_pair_add(&p
[1], ¢er
);
291 cpml_pair_add(&p
[2], ¢er
);
293 cpml_pair_to_cairo(&p
[0], arc
->org
);
294 cpml_pair_to_cairo(&p
[1], &arc
->data
[1]);
295 cpml_pair_to_cairo(&p
[2], &arc
->data
[2]);
300 get_center(const CpmlPair
*p
, CpmlPair
*dest
)
305 /* When p[0] == p[2], p[0]..p[1] is considered the diameter of a circle */
306 if (p
[0].x
== p
[2].x
&& p
[0].y
== p
[2].y
) {
307 dest
->x
= (p
[0].x
+ p
[1].x
) / 2;
308 dest
->y
= (p
[0].y
+ p
[1].y
) / 2;
312 div
.x
= (p
[0].x
*(p
[2].y
-p
[1].y
) +
313 p
[1].x
*(p
[0].y
-p
[2].y
) +
314 p
[1].x
*(p
[1].y
-p
[0].y
)) * 2;
315 div
.y
= (p
[0].y
*(p
[2].x
-p
[1].x
) +
316 p
[1].y
*(p
[0].x
-p
[2].x
) +
317 p
[2].y
*(p
[1].x
-p
[0].x
)) * 2;
319 /* Check for division by 0, that is the case where the 3 given points
320 * are laying on a straight line) */
321 if (div
.x
== 0. || div
.y
== 0.)
324 p2
[0] = p
[0].x
*p
[0].x
+ p
[0].y
*p
[0].y
;
325 p2
[1] = p
[1].x
*p
[1].x
+ p
[1].y
*p
[1].y
;
326 p2
[2] = p
[2].x
*p
[2].x
+ p
[2].y
*p
[2].y
;
328 /* Compute the center of the arc */
329 dest
->x
= (p2
[0] * (p
[2].y
- p
[1].y
) +
330 p2
[1] * (p
[0].y
- p
[2].y
) +
331 p2
[2] * (p
[1].y
- p
[0].y
)) / div
.x
;
332 dest
->y
= (p2
[0] * (p
[2].x
- p
[1].x
) +
333 p2
[1] * (p
[0].x
- p
[2].x
) +
334 p2
[2] * (p
[1].x
- p
[0].x
)) / div
.y
;
340 get_angles(const CpmlPair
*p
, const CpmlPair
*center
, CpmlPair
*angles
)
343 double start
, mid
, end
;
345 /* Calculate the starting angle */
346 cpml_pair_sub(cpml_pair_copy(&vector
, &p
[0]), center
);
347 start
= cpml_vector_angle(&vector
);
349 if (p
[0].x
== p
[2].x
&& p
[0].y
== p
[2].y
) {
350 /* When p[0] and p[2] are cohincidents, p[0]..p[1] is the diameter
351 * of a circle: return by convention start=start end=start+2PI */
352 end
= start
+ M_PI
*2;
354 /* Calculate the mid and end angle */
355 cpml_pair_sub(cpml_pair_copy(&vector
, &p
[1]), center
);
356 mid
= cpml_vector_angle(&vector
);
357 cpml_pair_sub(cpml_pair_copy(&vector
, &p
[2]), center
);
358 end
= cpml_vector_angle(&vector
);
361 if (mid
> end
|| mid
< start
)
364 if (mid
< end
|| mid
> start
)