2009-09-12 Chris Toshok <toshok@ximian.com>
[moon.git] / src / moon-path.c
blobed0dff71b101e45f91ce4bae661eb07012ff0150
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * moon-path.c: Path-based API, similar to cairo but without requiring a cairo_context_t
5 * Author:
6 * Sebastien Pouliot <sebastien@ximian.com>
8 * Copyright 2007, 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include "moon-path.h"
17 /**
18 * moon_path_new:
19 * @size: the number of items to hold
21 * The number of items varies for each operation (MOVE_TO, LINE_TO,
22 * CURVE_TO and CLOSE_PATH). The caller has the responsability to
23 * calculate the required number of items.
25 * Return value: the allocated #moon_path
26 **/
27 moon_path*
28 moon_path_new (int size)
30 moon_path* path = g_new0 (moon_path, 1);
31 path->allocated = size;
32 path->cairo.status = CAIRO_STATUS_SUCCESS;
33 path->cairo.data = g_new0 (cairo_path_data_t, size);
34 path->cairo.num_data = 0;
35 return path;
38 /**
39 * moon_path_renew:
40 * @path: an existing #moon_path or NULL
41 * @size: the number of items to hold
43 * The number of items varies for each operation (MOVE_TO, LINE_TO,
44 * CURVE_TO and CLOSE_PATH). The caller has the responsability to
45 * calculate the required number of items.
47 * Return value: the existing #moon_path (if large enough) or a new one
48 **/
49 moon_path*
50 moon_path_renew (moon_path* path, int size)
52 if (!path)
53 return moon_path_new (size);
55 if (path->allocated < size) {
56 /* not enough space, destroy and recreate */
57 moon_path_destroy (path);
58 return moon_path_new (size);
61 /* we can reuse the already allocated structure */
62 moon_path_clear (path);
63 return path;
66 /**
67 * moon_path_clear:
68 * @path: an existing #moon_path
70 * Clear the #moon_path structure so it can be reused for a path
71 * of the same size.
72 **/
73 void
74 moon_path_clear (moon_path* path)
76 g_return_if_fail (path != NULL);
78 path->cairo.status = CAIRO_STATUS_SUCCESS;
79 memset (path->cairo.data, 0, path->allocated * sizeof (cairo_path_data_t));
80 path->cairo.num_data = 0;
83 /**
84 * moon_path_destroy:
85 * @path: a #moon_path
87 * Free the specified #moon_path
88 **/
89 void
90 moon_path_destroy (moon_path* path)
92 g_return_if_fail (path != NULL);
94 if (path->allocated > 0)
95 g_free (path->cairo.data);
96 g_free (path);
99 /**
100 * moon_get_current_point:
101 * @path: a #moon_path
102 * @x: pointer to a double (x coordinate)
103 * @y: pointer to a double (y coordinate)
105 * Get the current point (x,y) on the moon_path. By default (empty path)
106 * this is (0,0)
108 void
109 moon_get_current_point (moon_path *path, double *x, double *y)
111 if (!path || !x || !y) {
112 g_warning ("moon_get_current_point(%p,%p,%p)", path, x, y);
113 return;
116 int pos = path->cairo.num_data - 1;
117 if (pos > 0) {
118 cairo_path_data_t *data = path->cairo.data;
119 *x = data[pos].point.x;
120 *y = data[pos].point.y;
121 } else {
122 *x = 0.0;
123 *y = 0.0;
127 /* this is a total soptimization, but I don't care ;-) */
128 static inline guint32
129 nearest_pow2 (guint32 num)
131 guint32 n;
133 if (num == 0)
134 return 0;
136 n = num - 1;
137 #if defined (__GNUC__) && defined (__i386__)
138 __asm__("bsrl %1,%0\n\t"
139 "jnz 1f\n\t"
140 "movl $-1,%0\n"
141 "1:" : "=r" (n) : "rm" (n));
142 n = (1 << (n + 1));
143 #else
144 n |= n >> 1;
145 n |= n >> 2;
146 n |= n >> 4;
147 n |= n >> 8;
148 n |= n >> 16;
149 n++;
150 #endif
152 return n;
155 static inline gboolean
156 moon_path_ensure_space (moon_path *path, int need)
158 void *data;
159 guint32 n;
161 if (path->cairo.num_data + need <= path->allocated)
162 return TRUE;
164 n = nearest_pow2 (path->cairo.num_data + need);
165 if (!(data = g_try_realloc (path->cairo.data, sizeof (cairo_path_data_t) * n)))
166 return FALSE;
168 path->cairo.data = (cairo_path_data_t *) data;
169 path->allocated = n;
171 return TRUE;
175 * moon_move_to:
176 * @path: a #moon_path
177 * @x: a double with the x coordinate
178 * @y: a double with the y coordinate
180 * Record a move operation to x,y in the #moon_path.
182 void
183 moon_move_to (moon_path *path, double x, double y)
185 g_return_if_fail (path != NULL);
187 if (!moon_path_ensure_space (path, MOON_PATH_MOVE_TO_LENGTH))
188 return;
190 cairo_path_data_t *data = path->cairo.data;
191 int pos = path->cairo.num_data;
193 data[pos].header.type = CAIRO_PATH_MOVE_TO;
194 data[pos].header.length = MOON_PATH_MOVE_TO_LENGTH;
195 pos++;
196 data[pos].point.x = x;
197 data[pos].point.y = y;
198 path->cairo.num_data += MOON_PATH_MOVE_TO_LENGTH;
202 * moon_line_to:
203 * @path: a #moon_path
204 * @x: a double with the x coordinate
205 * @y: a double with the y coordinate
207 * Record a line operation to x,y in the #moon_path.
209 void
210 moon_line_to (moon_path *path, double x, double y)
212 g_return_if_fail (path != NULL);
214 if (!moon_path_ensure_space (path, MOON_PATH_LINE_TO_LENGTH))
215 return;
217 cairo_path_data_t *data = path->cairo.data;
218 int pos = path->cairo.num_data;
220 data[pos].header.type = CAIRO_PATH_LINE_TO;
221 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
222 pos++;
223 data[pos].point.x = x;
224 data[pos].point.y = y;
225 path->cairo.num_data += MOON_PATH_LINE_TO_LENGTH;
229 * moon_curve_to:
230 * @path: a #moon_path
231 * @x1: a double with the x coordinate of the first point
232 * @y1: a double with the y coordinate of the first point
233 * @x2: a double with the x coordinate of the second point
234 * @y2: a double with the y coordinate of the second point
235 * @x3: a double with the x coordinate of the third point
236 * @y3: a double with the y coordinate of the third point
238 * Record a cubic bezier curve operation (x1,y1 x2,y2 x3,y3)
239 * in the #moon_path.
241 void
242 moon_curve_to (moon_path *path, double x1, double y1, double x2, double y2, double x3, double y3)
244 g_return_if_fail (path != NULL);
246 if (!moon_path_ensure_space (path, MOON_PATH_CURVE_TO_LENGTH))
247 return;
249 cairo_path_data_t *data = path->cairo.data;
250 int pos = path->cairo.num_data;
252 data[pos].header.type = CAIRO_PATH_CURVE_TO;
253 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
254 pos++;
255 data[pos].point.x = x1;
256 data[pos].point.y = y1;
257 pos++;
258 data[pos].point.x = x2;
259 data[pos].point.y = y2;
260 pos++;
261 data[pos].point.x = x3;
262 data[pos].point.y = y3;
263 path->cairo.num_data += MOON_PATH_CURVE_TO_LENGTH;
267 * moon_quad_curve_to:
268 * @path: a #moon_path
269 * @x1: a double with the x coordinate of the first point
270 * @y1: a double with the y coordinate of the first point
271 * @x2: a double with the x coordinate of the second point
272 * @y2: a double with the y coordinate of the second point
274 * Record the quadratic bezier curve operation (x1,y1 x2,y2)
275 * as a (transformed into) cubic bezier curve in the #moon_path.
277 * quadratic to cubic bezier, the original control point and the end control point are the same
278 * http://web.archive.org/web/20020209100930/http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/node2.html
280 void
281 moon_quad_curve_to (moon_path* path, double x1, double y1, double x2, double y2)
283 g_return_if_fail (path != NULL);
285 double x0, y0;
286 double x3 = x2;
287 double y3 = y2;
289 moon_get_current_point (path, &x0, &y0);
291 x2 = x1 + (x2 - x1) / 3;
292 y2 = y1 + (y2 - y1) / 3;
293 x1 = x0 + 2 * (x1 - x0) / 3;
294 y1 = y0 + 2 * (y1 - y0) / 3;
296 moon_curve_to (path, x1, y1, x2, y2, x3, y3);
300 * moon_arc_to:
301 * @path: a #moon_path
302 * @width: a double with the horizontal size of the arc
303 * @height: a double with the vertical size of the arc
304 * @large: a boolean to indicate if this is a large arc
305 * @sweep: a boolean to indicate the sweep direction
306 * @ex: a double with the x coordinate of the end point
307 * @ey: a double with the y coordinate of the end point
309 * Record the arc as multiple cubic curves operation
310 * in the #moon_path.
312 void
313 moon_arc_to (moon_path *path, double width, double height, double angle, gboolean large, gboolean sweep, double ex, double ey)
315 g_return_if_fail (path != NULL);
317 // from tests it seems that Silverlight closely follows SVG arc
318 // behavior (which is very different from the model used with GDI+)
319 // some helpful stuff is available here:
320 // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
322 // get start point from the existing path
323 double sx, sy;
324 moon_get_current_point (path, &sx, &sy);
326 // if start and end points are identical, then no arc is drawn
327 // FIXME: what's the logic (if any) to compare points
328 // e.g. 60 and 60.000002 are drawn while 80 and 80.000003 aren't
329 if (IS_ZERO (ex - sx) && IS_ZERO (ey - sy))
330 return;
332 // Correction of out-of-range radii, see F6.6 (step 1)
333 if (IS_ZERO (width) || IS_ZERO (height)) {
334 // treat this as a straight line (to end point)
335 moon_line_to (path, ex, ey);
336 return;
339 // Silverlight "too small to be useful"
340 if (IS_TOO_SMALL (width) || IS_TOO_SMALL (height)) {
341 // yes it does mean there's a hole between "normal" FP values and "zero" FP values
342 // and SL doesn't render anything in this twilight sonze
343 return;
346 // Correction of out-of-range radii, see F6.6.1 (step 2)
347 double rx = fabs (width);
348 double ry = fabs (height);
350 // convert angle into radians
351 angle = angle * M_PI / 180.0;
353 // variables required for F6.3.1
354 double cos_phi = cos (angle);
355 double sin_phi = sin (angle);
356 double dx2 = (sx - ex) / 2.0;
357 double dy2 = (sy - ey) / 2.0;
358 double x1p = cos_phi * dx2 + sin_phi * dy2;
359 double y1p = cos_phi * dy2 - sin_phi * dx2;
360 double x1p2 = x1p * x1p;
361 double y1p2 = y1p * y1p;
362 double rx2 = rx * rx;
363 double ry2 = ry * ry;
365 // Correction of out-of-range radii, see F6.6.2 (step 4)
366 double lambda = (x1p2 / rx2) + (y1p2 / ry2);
367 if (lambda > 1.0) {
368 // see F6.6.3
369 double lambda_root = sqrt (lambda);
370 rx *= lambda_root;
371 ry *= lambda_root;
372 // update rx2 and ry2
373 rx2 = rx * rx;
374 ry2 = ry * ry;
377 double cxp, cyp, cx, cy;
378 double c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
380 // check if there is no possible solution (i.e. we can't do a square root of a negative value)
381 if (c < 0.0) {
382 // scale uniformly until we have a single solution (see F6.2) i.e. when c == 0.0
383 double scale = sqrt (1.0 - c / (rx2 * ry2));
384 rx *= scale;
385 ry *= scale;
386 // update rx2 and ry2
387 rx2 = rx * rx;
388 ry2 = ry * ry;
390 // step 2 (F6.5.2) - simplified since c == 0.0
391 cxp = 0.0;
392 cyp = 0.0;
394 // step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
395 cx = 0.0;
396 cy = 0.0;
397 } else {
398 // complete c calculation
399 c = sqrt (c / ((rx2 * y1p2) + (ry2 * x1p2)));
401 // inverse sign if Fa == Fs
402 if (large == sweep)
403 c = -c;
405 // step 2 (F6.5.2)
406 cxp = c * ( rx * y1p / ry);
407 cyp = c * (-ry * x1p / rx);
409 // step 3 (F6.5.3 first part)
410 cx = cos_phi * cxp - sin_phi * cyp;
411 cy = sin_phi * cxp + cos_phi * cyp;
414 // step 3 (F6.5.3 second part) we now have the center point of the ellipse
415 cx += (sx + ex) / 2.0;
416 cy += (sy + ey) / 2.0;
418 // step 4 (F6.5.4)
419 // we dont' use arccos (as per w3c doc), see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
420 // note: atan2 (0.0, 1.0) == 0.0
421 double at = atan2 (((y1p - cyp) / ry), ((x1p - cxp) / rx));
422 double theta1 = (at < 0.0) ? 2.0 * M_PI + at : at;
424 double nat = atan2 (((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
425 double delta_theta = (nat < at) ? 2.0 * M_PI - at + nat : nat - at;
427 if (sweep) {
428 // ensure delta theta < 0 or else add 360 degrees
429 if (delta_theta < 0.0)
430 delta_theta += 2.0 * M_PI;
431 } else {
432 // ensure delta theta > 0 or else substract 360 degrees
433 if (delta_theta > 0.0)
434 delta_theta -= 2.0 * M_PI;
437 // add several cubic bezier to approximate the arc (smaller than 90 degrees)
438 // we add one extra segment because we want something smaller than 90deg (i.e. not 90 itself)
439 int segments = (int) (fabs (delta_theta / M_PI_2)) + 1;
440 double delta = delta_theta / segments;
442 // http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
443 double bcp = 4.0 / 3 * (1 - cos (delta / 2)) / sin (delta / 2);
445 double cos_phi_rx = cos_phi * rx;
446 double cos_phi_ry = cos_phi * ry;
447 double sin_phi_rx = sin_phi * rx;
448 double sin_phi_ry = sin_phi * ry;
450 double cos_theta1 = cos (theta1);
451 double sin_theta1 = sin (theta1);
453 if (!moon_path_ensure_space (path, segments * MOON_PATH_CURVE_TO_LENGTH))
454 return;
456 int i;
457 for (i = 0; i < segments; ++i) {
458 // end angle (for this segment) = current + delta
459 double theta2 = theta1 + delta;
460 double cos_theta2 = cos (theta2);
461 double sin_theta2 = sin (theta2);
463 // first control point (based on start point sx,sy)
464 double c1x = sx - bcp * (cos_phi_rx * sin_theta1 + sin_phi_ry * cos_theta1);
465 double c1y = sy + bcp * (cos_phi_ry * cos_theta1 - sin_phi_rx * sin_theta1);
467 // end point (for this segment)
468 double ex = cx + (cos_phi_rx * cos_theta2 - sin_phi_ry * sin_theta2);
469 double ey = cy + (sin_phi_rx * cos_theta2 + cos_phi_ry * sin_theta2);
471 // second control point (based on end point ex,ey)
472 double c2x = ex + bcp * (cos_phi_rx * sin_theta2 + sin_phi_ry * cos_theta2);
473 double c2y = ey + bcp * (sin_phi_rx * sin_theta2 - cos_phi_ry * cos_theta2);
475 moon_curve_to (path, c1x, c1y, c2x, c2y, ex, ey);
477 // next start point is the current end point (same for angle)
478 sx = ex;
479 sy = ey;
480 theta1 = theta2;
481 // avoid recomputations
482 cos_theta1 = cos_theta2;
483 sin_theta1 = sin_theta2;
488 * moon_ellipse:
489 * @path: a #moon_path
490 * @x: a double with the left-most coordinate of the ellipse
491 * @y: a double with the top-most coordinate of the ellipse
492 * @w: a double with the width of the ellipse
493 * @h: a double with the height of the ellipse
495 * Record a series of basic operations that correspond to an ellipse in
496 * the #moon_path. Note that the x,y aren't the center of the ellipse.
498 void
499 moon_ellipse (moon_path *path, double x, double y, double w, double h)
501 g_return_if_fail (path != NULL);
503 double rx = w / 2.0;
504 double ry = h / 2.0;
505 double cx = x + rx;
506 double cy = y + ry;
507 double brx = ARC_TO_BEZIER * rx;
508 double bry = ARC_TO_BEZIER * ry;
510 if (!moon_path_ensure_space (path, MOON_PATH_ELLIPSE_LENGTH))
511 return;
513 cairo_path_data_t *data = path->cairo.data;
514 int pos = path->cairo.num_data;
516 data[pos].header.type = CAIRO_PATH_MOVE_TO;
517 data[pos].header.length = MOON_PATH_MOVE_TO_LENGTH;
518 pos++;
519 data[pos].point.x = cx + rx;
520 data[pos].point.y = cy;
521 pos++;
522 data[pos].header.type = CAIRO_PATH_CURVE_TO;
523 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
524 pos++;
525 data[pos].point.x = cx + rx;
526 data[pos].point.y = cy + bry;
527 pos++;
528 data[pos].point.x = cx + brx;
529 data[pos].point.y = cy + ry;
530 pos++;
531 data[pos].point.x = cx;
532 data[pos].point.y = cy + ry;
533 pos++;
534 data[pos].header.type = CAIRO_PATH_CURVE_TO;
535 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
536 pos++;
537 data[pos].point.x = cx - brx;
538 data[pos].point.y = cy + ry;
539 pos++;
540 data[pos].point.x = cx - rx;
541 data[pos].point.y = cy + bry;
542 pos++;
543 data[pos].point.x = cx - rx;
544 data[pos].point.y = cy;
545 pos++;
546 data[pos].header.type = CAIRO_PATH_CURVE_TO;
547 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
548 pos++;
549 data[pos].point.x = cx - rx;
550 data[pos].point.y = cy - bry;
551 pos++;
552 data[pos].point.x = cx - brx;
553 data[pos].point.y = cy - ry;
554 pos++;
555 data[pos].point.x = cx;
556 data[pos].point.y = cy - ry;
557 pos++;
558 data[pos].header.type = CAIRO_PATH_CURVE_TO;
559 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
560 pos++;
561 data[pos].point.x = cx + brx;
562 data[pos].point.y = cy - ry;
563 pos++;
564 data[pos].point.x = cx + rx;
565 data[pos].point.y = cy - bry;
566 pos++;
567 data[pos].point.x = cx + rx;
568 data[pos].point.y = cy;
569 path->cairo.num_data += MOON_PATH_ELLIPSE_LENGTH;
573 * moon_rectangle:
574 * @path: a #moon_path
575 * @x: a double with the left-most coordinate of the rectangle
576 * @y: a double with the top-most coordinate of the rectangle
577 * @w: a double with the width of the rectangle
578 * @h: a double with the height of the rectangle
580 * Record a series of basic operations that correspond to a rectangle
581 * in the #moon_path.
583 void
584 moon_rectangle (moon_path *path, double x, double y, double w, double h)
586 g_return_if_fail (path != NULL);
588 if (!moon_path_ensure_space (path, MOON_PATH_RECTANGLE_LENGTH))
589 return;
591 cairo_path_data_t *data = path->cairo.data;
592 int pos = path->cairo.num_data;
594 data[pos].header.type = CAIRO_PATH_MOVE_TO;
595 data[pos].header.length = MOON_PATH_MOVE_TO_LENGTH;
596 pos++;
597 data[pos].point.x = x;
598 data[pos].point.y = y;
599 pos++;
600 data[pos].header.type = CAIRO_PATH_LINE_TO;
601 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
602 pos++;
603 data[pos].point.x = x + w;
604 data[pos].point.y = y;
605 pos++;
606 data[pos].header.type = CAIRO_PATH_LINE_TO;
607 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
608 pos++;
609 data[pos].point.x = x + w;
610 data[pos].point.y = y + h;
611 pos++;
612 data[pos].header.type = CAIRO_PATH_LINE_TO;
613 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
614 pos++;
615 data[pos].point.x = x;
616 data[pos].point.y = y + h;
617 pos++;
618 data[pos].header.type = CAIRO_PATH_CLOSE_PATH;
619 data[pos].header.length = MOON_PATH_CLOSE_PATH_LENGTH;
620 path->cairo.num_data += MOON_PATH_RECTANGLE_LENGTH;
624 * moon_rounded_rectangle:
625 * @path: a #moon_path
626 * @x: a double with the left-most coordinate of the rectangle
627 * @y: a double with the top-most coordinate of the rectangle
628 * @w: a double with the width of the rectangle
629 * @h: a double with the height of the rectangle
630 * @radius_x: a double with the x radius of the rounded corner
631 * @radius_y: a double with the y radius of the rounded corner
633 * Record a series of basic operations that correspond to a rectangle
634 * with rounded corners in the #moon_path.
636 void
637 moon_rounded_rectangle (moon_path *path, double x, double y, double w, double h, double radius_x, double radius_y)
639 g_return_if_fail (path != NULL);
641 if (!moon_path_ensure_space (path, MOON_PATH_ROUNDED_RECTANGLE_LENGTH))
642 return;
644 if (radius_x < 0.0)
645 radius_x = -radius_x;
646 if (radius_y < 0.0)
647 radius_y = -radius_y;
649 // test limits (without using multiplications)
650 if (radius_x > w - radius_x)
651 radius_x = w / 2;
652 if (radius_y > h - radius_y)
653 radius_y = h / 2;
655 // approximate (quite close) the arc using a bezier curve
656 double c1 = ARC_TO_BEZIER * radius_x;
657 double c2 = ARC_TO_BEZIER * radius_y;
659 cairo_path_data_t *data = path->cairo.data;
660 int pos = path->cairo.num_data;
662 data[pos].header.type = CAIRO_PATH_MOVE_TO;
663 data[pos].header.length = MOON_PATH_MOVE_TO_LENGTH;
664 pos++;
665 data[pos].point.x = x + radius_x;
666 data[pos].point.y = y;
667 pos++;
668 data[pos].header.type = CAIRO_PATH_LINE_TO;
669 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
670 pos++;
671 data[pos].point.x = x + w - radius_x;
672 data[pos].point.y = y;
673 pos++;
674 data[pos].header.type = CAIRO_PATH_CURVE_TO;
675 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
676 pos++;
677 data[pos].point.x = x + w - radius_x + c1;
678 data[pos].point.y = y;
679 pos++;
680 data[pos].point.x = x + w;
681 data[pos].point.y = y + c2;
682 pos++;
683 data[pos].point.x = x + w;
684 data[pos].point.y = y + radius_y;
685 pos++;
686 data[pos].header.type = CAIRO_PATH_LINE_TO;
687 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
688 pos++;
689 data[pos].point.x = x + w;
690 data[pos].point.y = y + h - radius_y;
691 pos++;
692 data[pos].header.type = CAIRO_PATH_CURVE_TO;
693 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
694 pos++;
695 data[pos].point.x = x + w;
696 data[pos].point.y = y + h - radius_y + c2;
697 pos++;
698 data[pos].point.x = x + w + c1 - radius_x;
699 data[pos].point.y = y + h;
700 pos++;
701 data[pos].point.x = x + w - radius_x;
702 data[pos].point.y = y + h;
703 pos++;
704 data[pos].header.type = CAIRO_PATH_LINE_TO;
705 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
706 pos++;
707 data[pos].point.x = x + radius_x;
708 data[pos].point.y = y + h;
709 pos++;
710 data[pos].header.type = CAIRO_PATH_CURVE_TO;
711 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
712 pos++;
713 data[pos].point.x = x + radius_x - c1;
714 data[pos].point.y = y + h;
715 pos++;
716 data[pos].point.x = x;
717 data[pos].point.y = y + h - c2;
718 pos++;
719 data[pos].point.x = x;
720 data[pos].point.y = y + h - radius_y;
721 pos++;
722 data[pos].header.type = CAIRO_PATH_LINE_TO;
723 data[pos].header.length = MOON_PATH_LINE_TO_LENGTH;
724 pos++;
725 data[pos].point.x = x;
726 data[pos].point.y = y + radius_y;
727 pos++;
728 data[pos].header.type = CAIRO_PATH_CURVE_TO;
729 data[pos].header.length = MOON_PATH_CURVE_TO_LENGTH;
730 pos++;
731 data[pos].point.x = x;
732 data[pos].point.y = y + radius_y - c2;
733 pos++;
734 data[pos].point.x = x + radius_x - c1;
735 data[pos].point.y = y;
736 pos++;
737 data[pos].point.x = x + radius_x;
738 data[pos].point.y = y;
739 pos++;
740 data[pos].header.type = CAIRO_PATH_CLOSE_PATH;
741 data[pos].header.length = MOON_PATH_CLOSE_PATH_LENGTH;
743 path->cairo.num_data += MOON_PATH_ROUNDED_RECTANGLE_LENGTH;
747 * moon_close_path:
748 * @path: a #moon_path
750 * Record a close operation in the #moon_path.
752 void
753 moon_close_path (moon_path *path)
755 g_return_if_fail (path != NULL);
757 if (!moon_path_ensure_space (path, MOON_PATH_CLOSE_PATH_LENGTH))
758 return;
760 cairo_path_data_t *data = path->cairo.data;
761 int pos = path->cairo.num_data;
763 data[pos].header.type = CAIRO_PATH_CLOSE_PATH;
764 data[pos].header.length = MOON_PATH_CLOSE_PATH_LENGTH;
765 path->cairo.num_data += MOON_PATH_CLOSE_PATH_LENGTH;
768 #if FALSE
770 * moon_get_origin
771 * @path: a #moon_path
772 * @ox: a pointer to the double for the minimal X value of the path
773 * @oy: a pointer to the double for the minimal Y value of the path
775 * Get the origin point of the path.
777 void
778 moon_get_origin (moon_path *path, double *ox, double *oy)
780 g_return_if_fail (path != NULL);
782 int i = 0;
783 double x = 0.0, y = 0.0;
784 cairo_path_t *c_path;
786 c_path = &path->cairo;
787 for (; i < c_path->num_data; i+= c_path->data[i].header.length) {
788 cairo_path_data_t *data = &c_path->data[i];
789 switch (data->header.type) {
790 case CAIRO_PATH_CURVE_TO:
791 if (i == 0) {
792 x = data[1].point.x;
793 y = data[1].point.y;
794 } else {
795 x = MIN (x, data[1].point.x);
796 y = MIN (y, data[1].point.y);
798 x = MIN (x, data[2].point.x);
799 y = MIN (y, data[2].point.y);
800 x = MIN (x, data[3].point.x);
801 y = MIN (y, data[3].point.y);
802 break;
803 case CAIRO_PATH_LINE_TO:
804 case CAIRO_PATH_MOVE_TO:
805 if (i == 0) {
806 x = data[1].point.x;
807 y = data[1].point.y;
808 } else {
809 x = MIN (x, data[1].point.x);
810 y = MIN (y, data[1].point.y);
812 break;
813 default:
814 break;
818 if (ox) *ox = x;
819 if (oy) *oy = y;
821 #endif
824 * moon_merge:
825 * @path: a #moon_path
826 * @subpath: the #moon_path to merge into path
828 * Merge 'subpath' into 'path'.
830 void
831 moon_merge (moon_path *path, moon_path *subpath)
833 g_return_if_fail (path != NULL);
834 g_return_if_fail (subpath != NULL);
836 if (!moon_path_ensure_space (path, subpath->cairo.num_data))
837 return;
839 cairo_path_data_t *data = path->cairo.data;
840 int pos = path->cairo.num_data;
842 memcpy (&data [pos], subpath->cairo.data, subpath->cairo.num_data * sizeof (cairo_path_data_t));
843 path->cairo.num_data += subpath->cairo.num_data;
847 * cairo_path_display:
848 * @path: a #cairo_path_t
850 * Display the content of the #cairo_path_t on the console.
851 * For debugging purpose only.
853 void
854 cairo_path_display (cairo_path_t *path)
856 #if FALSE
857 g_return_if_fail (path != NULL);
859 int i = 0;
860 g_warning ("path %p status %d, num_data %d", path, path->status, path->num_data);
861 for (; i < path->num_data; i+= path->data[i].header.length) {
862 cairo_path_data_t *data = &path->data[i];
863 switch (data->header.type) {
864 case CAIRO_PATH_CURVE_TO:
865 g_warning ("\tCAIRO_PATH_CURVE_TO (size %d) (%g, %g) (%g, %g) (%g, %g)", data->header.length,
866 data[1].point.x, data[1].point.y, data[2].point.x, data[2].point.y, data[3].point.x, data[3].point.y);
867 break;
868 case CAIRO_PATH_LINE_TO:
869 g_warning ("\tCAIRO_PATH_LINE_TO (size %d) (%g, %g)", data->header.length, data[1].point.x, data[1].point.y);
870 break;
871 case CAIRO_PATH_MOVE_TO:
872 g_warning ("\tCAIRO_PATH_MOVE_TO (size %d) (%g, %g)", data->header.length, data[1].point.x, data[1].point.y);
873 break;
874 case CAIRO_PATH_CLOSE_PATH:
875 g_warning ("\tCAIRO_PATH_CLOSE_PATH (size %d)", data->header.length);
876 break;
879 #endif
883 * moon_path_display:
884 * @path: a #moon_path
886 * Display the content of the #moon_path on the console.
887 * For debugging purpose only.
889 void
890 moon_path_display (moon_path *path)
892 cairo_path_display (&path->cairo);