1 /*
2 * go-path.c
3 *
4 * Copyright © 2002 University of Southern California
5 * Copyright © 2005 Red Hat, Inc.
6 * Copyright © 2006 Emmanuel Pacaud (emmanuel.pacaud@lapp.in2p3.fr)
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 *
23 * This code is an adaptation of the path code which can be found in
24 * the cairo library (http://cairographics.org).
25 *
26 * Contributor(s):
27 * Carl D. Worth <cworth@cworth.org>
28 * Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr>
29 */
30
31 #include <goffice/math/go-math.h>
32 #include <goffice/utils/go-path.h>
33 #include <goffice/utils/go-cairo.h>
34 #include <string.h>
35
36 /* the five following does not belong there, but they don't in any other .c file */
37
38 /**
39 * GOPoint:
40 * @x: horizontal position.
41 * @y: vertical position.
42 **/
43
44 /**
45 * GORect:
46 * @top: top.
47 * @left: left.
48 * @bottom: bottom.
49 * @right: right.
50 **/
51
52 /**
53 * GOAnchorType:
54 * @GO_ANCHOR_CENTER: anchor center.
55 * @GO_ANCHOR_NORTH: anchor top.
56 * @GO_ANCHOR_NORTH_WEST: anchor top left.
57 * @GO_ANCHOR_NORTH_EAST: anchor top left.
58 * @GO_ANCHOR_SOUTH: anchor bottom.
59 * @GO_ANCHOR_SOUTH_WEST: anchor bottom left.
60 * @GO_ANCHOR_SOUTH_EAST: anchor bottom left.
61 * @GO_ANCHOR_WEST: anchor left.
62 * @GO_ANCHOR_EAST: anchor right.
63 * @GO_ANCHOR_N: anchor top.
64 * @GO_ANCHOR_NW: anchor top left.
65 * @GO_ANCHOR_NE: anchor top left.
66 * @GO_ANCHOR_S: anchor bottom.
67 * @GO_ANCHOR_SW: anchor bottom left.
68 * @GO_ANCHOR_SE: anchor bottom left.
69 * @GO_ANCHOR_W: anchor left.
70 * @GO_ANCHOR_E: anchor right.
71 * @GO_ANCHOR_BASELINE_CENTER: anchor centered on baseline
72 * @GO_ANCHOR_BASELINE_WEST: anchor left on baseline
73 * @GO_ANCHOR_BASELINE_EAST: anchor right on baseline
74 * @GO_ANCHOR_B: synonym for GO_ANCHOR_BASELINE_CENTER
75 * @GO_ANCHOR_BW: synonym for GO_ANCHOR_BASELINE_WEST
76 * @GO_ANCHOR_BE: synonym for GO_ANCHOR_BASELINE_EAST
77 **/
78
79 /**
80 * GODrawingAnchor:
81 * @pos_pts: position in points.
82 * @direction: #GODrawingAnchorDir
83 **/
84
85 /**
86 * GODrawingAnchorDir:
87 * @GOD_ANCHOR_DIR_UNKNOWN: unknown.
88 * @GOD_ANCHOR_DIR_UP_LEFT: up left.
89 * @GOD_ANCHOR_DIR_UP_RIGHT: up right.
90 * @GOD_ANCHOR_DIR_DOWN_LEFT: down left.
91 * @GOD_ANCHOR_DIR_DOWN_RIGHT: down right.
92 * @GOD_ANCHOR_DIR_NONE_MASK: mask for none.
93 * @GOD_ANCHOR_DIR_H_MASK: horizontal mask.
94 * @GOD_ANCHOR_DIR_RIGHT: right
95 * @GOD_ANCHOR_DIR_V_MASK: vertical mask.
96 * @GOD_ANCHOR_DIR_DOWN: down
97 **/
98
99 /************************************/
100
101 /**
102 * GOPathOptions:
103 * @GO_PATH_OPTIONS_SNAP_COORDINATES: round coordinates to avoid aliasing in pixbufs.
104 * @GO_PATH_OPTIONS_SNAP_WIDTH: round width so that it correpond to a round pixels number.
105 * @GO_PATH_OPTIONS_SHARP: use raw coordinates.
106 **/
107
108 /**
109 * GOPathPoint:
110 * @x: horizontal position.
111 * @y: vertical dimension.
112 **/
113
114 /**
115 * GOPathDirection:
116 * @GO_PATH_DIRECTION_FORWARD: go through the pass from start to end.
117 * @GO_PATH_DIRECTION_BACKWARD: go through the pass from end to start.
118 **/
119
120 #define GO_PATH_DEFAULT_BUFFER_SIZE 64
121
122 typedef struct _GOPathData GOPathData;
123 typedef struct _GOPathDataBuffer GOPathDataBuffer;
124
125 typedef enum _GOPathAction {
126 GO_PATH_ACTION_MOVE_TO = 0,
127 GO_PATH_ACTION_LINE_TO = 1,
128 GO_PATH_ACTION_CURVE_TO = 2,
129 GO_PATH_ACTION_CLOSE_PATH = 3,
130 GO_PATH_ACTION_CHANGE_STATE
131 } GOPathAction;
132
133 static int action_n_args[4] = { 1, 1, 3, 0};
134
135 struct _GOPathDataBuffer {
136 int n_points;
137 int n_actions;
138
139 GOPathAction *actions;
140 GOPathPoint *points;
141
142 struct _GOPathDataBuffer *next;
143 struct _GOPathDataBuffer *previous;
144 };
145
146 struct _GOPath {
147 GOPathDataBuffer *data_buffer_head;
148 GOPathDataBuffer *data_buffer_tail;
149
150 GOPathOptions options;
151 unsigned refs;
152 };
153
154 static void
go_path_data_buffer_free(GOPathDataBuffer * buffer)155 go_path_data_buffer_free (GOPathDataBuffer *buffer)
156 {
157 g_free (buffer->points);
158 g_free (buffer->actions);
159 g_free (buffer);
160 }
161
162 static void
go_path_data_buffer_clear(GOPathDataBuffer * buffer)163 go_path_data_buffer_clear (GOPathDataBuffer *buffer)
164 {
165 buffer->n_points = 0;
166 buffer->n_actions = 0;
167 }
168
169 static GOPathDataBuffer *
go_path_data_buffer_new(void)170 go_path_data_buffer_new (void)
171 {
172 GOPathDataBuffer *buffer;
173
174 buffer = g_new (GOPathDataBuffer, 1);
175 if (buffer == NULL) {
176 g_warning ("[GOPathDataBuffer::new] can't create data buffer");
177 return NULL;
178 }
179
180 buffer->points = g_new (GOPathPoint, GO_PATH_DEFAULT_BUFFER_SIZE);
181 if (buffer->points == NULL) {
182 g_warning ("[GOPathDataBuffer::new] can't create data buffer");
183 g_free (buffer);
184 return NULL;
185 }
186
187 buffer->actions = g_new (GOPathAction, GO_PATH_DEFAULT_BUFFER_SIZE);
188 if (buffer->actions == NULL) {
189 g_warning ("[GOPathDataBuffer::new] can't create data buffer");
190 g_free (buffer->points);
191 g_free (buffer);
192 return NULL;
193 }
194
195 buffer->n_points = 0;
196 buffer->n_actions =0;
197 buffer->next = NULL;
198 buffer->previous = NULL;
199
200 return buffer;
201 }
202
203 static GOPathDataBuffer *
go_path_add_data_buffer(GOPath * path)204 go_path_add_data_buffer (GOPath *path)
205 {
206 GOPathDataBuffer *buffer;
207
208 buffer = go_path_data_buffer_new ();
209 if (buffer == NULL)
210 return NULL;
211
212 if (path->data_buffer_head == NULL) {
213 path->data_buffer_head = buffer;
214 path->data_buffer_tail = buffer;
215 return buffer;
216 }
217 buffer->previous = path->data_buffer_tail;
218 path->data_buffer_tail->next = buffer;
219 path->data_buffer_tail = buffer;
220
221 return buffer;
222 }
223
224 GOPath *
go_path_new(void)225 go_path_new (void)
226 {
227 GOPath *path;
228
229 path = g_new (GOPath, 1);
230 if (path == NULL) {
231 g_warning ("[GOPath::new] can't create path");
232 return NULL;
233 }
234 path->data_buffer_tail = NULL;
235 path->data_buffer_head = NULL;
236 path->options = 0;
237
238 if (go_path_add_data_buffer (path) == NULL) {
239 g_free (path);
240 return NULL;
241 }
242
243 path->refs = 1;
244
245 return path;
246 }
247
248 void
go_path_clear(GOPath * path)249 go_path_clear (GOPath *path)
250 {
251 GOPathDataBuffer *buffer;
252
253 g_return_if_fail (GO_IS_PATH (path));
254
255 if (path->data_buffer_head == NULL)
256 return;
257
258 while (path->data_buffer_head->next != NULL) {
259 buffer = path->data_buffer_head->next->next;
260 go_path_data_buffer_free (path->data_buffer_head->next);
261 path->data_buffer_head->next = buffer;
262 }
263 go_path_data_buffer_clear (path->data_buffer_head);
264 path->data_buffer_tail = path->data_buffer_head;
265 }
266
267 /**
268 * go_path_free:
269 * @path: a #GOPath
270 *
271 * Decrements references count and frees all memory allocated for @path if
272 * references count reaches 0.
273 **/
274
275 void
go_path_free(GOPath * path)276 go_path_free (GOPath *path)
277 {
278 GOPathDataBuffer *buffer;
279
280 g_return_if_fail (path != NULL);
281
282 path->refs--;
283 if (path->refs > 0)
284 return;
285
286 while (path->data_buffer_head != NULL) {
287 buffer = path->data_buffer_head->next;
288 go_path_data_buffer_free (path->data_buffer_head);
289 path->data_buffer_head = buffer;
290 }
291 g_free (path);
292 }
293
294 /**
295 * go_path_ref:
296 * @path: a #GOPath
297 *
298 * Increments references count to @path.
299 * Returns: the path with an incremented references count.
300 **/
301
302 GOPath *
go_path_ref(GOPath * path)303 go_path_ref (GOPath *path)
304 {
305 g_return_val_if_fail (path != NULL, NULL);
306
307 path->refs++;
308 return path;
309 }
310
311 GType
go_path_get_type(void)312 go_path_get_type (void)
313 {
314 static GType type_path = 0;
315
316 if (!type_path)
317 type_path = g_boxed_type_register_static
318 ("GOPath",
319 (GBoxedCopyFunc) go_path_ref,
320 (GBoxedFreeFunc) go_path_free);
321
322 return type_path;
323 }
324
325 /**
326 * go_path_set_options:
327 * @path: a #GOPath
328 * @options: #GOPathOptions
329 *
330 * Change the rendering options for @path using
331 * %GO_PATH_OPTIONS_SNAP_COORDINATES
332 * %GO_PATH_OPTIONS_SNAP_WIDTH
333 * %GO_PATH_OPTIONS_SHARP
334 **/
335 void
go_path_set_options(GOPath * path,GOPathOptions options)336 go_path_set_options (GOPath *path, GOPathOptions options)
337 {
338 g_return_if_fail (GO_IS_PATH (path));
339
340 path->options = options;
341 }
342
343 GOPathOptions
go_path_get_options(GOPath const * path)344 go_path_get_options (GOPath const *path)
345 {
346 g_return_val_if_fail (GO_IS_PATH (path), 0);
347
348 return path->options;
349 }
350
351 static void
go_path_add_points(GOPath * path,GOPathAction action,GOPathPoint * points,int n_points)352 go_path_add_points (GOPath *path, GOPathAction action,
353 GOPathPoint *points, int n_points)
354 {
355 GOPathDataBuffer *buffer;
356 int i;
357
358 g_return_if_fail (GO_IS_PATH (path));
359
360 buffer = path->data_buffer_tail;
361 if (buffer->n_actions + 1 > GO_PATH_DEFAULT_BUFFER_SIZE
362 || buffer->n_points + n_points > GO_PATH_DEFAULT_BUFFER_SIZE)
363 buffer = go_path_add_data_buffer (path);
364
365 buffer->actions[buffer->n_actions++] = action;
366
367 for (i = 0; i < n_points; i++)
368 buffer->points[buffer->n_points++] = points[i];
369 }
370
371 void
go_path_move_to(GOPath * path,double x,double y)372 go_path_move_to (GOPath *path, double x, double y)
373 {
374 GOPathPoint point;
375
376 point.x = GO_CAIRO_CLAMP (x);
377 point.y = GO_CAIRO_CLAMP (y);
378 go_path_add_points (path, GO_PATH_ACTION_MOVE_TO, &point, 1);
379 }
380
381 void
go_path_line_to(GOPath * path,double x,double y)382 go_path_line_to (GOPath *path, double x, double y)
383 {
384 GOPathPoint point;
385
386 point.x = GO_CAIRO_CLAMP (x);
387 point.y = GO_CAIRO_CLAMP (y);
388 go_path_add_points (path, GO_PATH_ACTION_LINE_TO, &point, 1);
389 }
390
391 void
go_path_curve_to(GOPath * path,double x0,double y0,double x1,double y1,double x2,double y2)392 go_path_curve_to (GOPath *path,
393 double x0, double y0,
394 double x1, double y1,
395 double x2, double y2)
396 {
397 GOPathPoint points[3];
398
399 points[0].x = GO_CAIRO_CLAMP (x0);
400 points[0].y = GO_CAIRO_CLAMP (y0);
401 points[1].x = GO_CAIRO_CLAMP (x1);
402 points[1].y = GO_CAIRO_CLAMP (y1);
403 points[2].x = GO_CAIRO_CLAMP (x2);
404 points[2].y = GO_CAIRO_CLAMP (y2);
405 go_path_add_points (path, GO_PATH_ACTION_CURVE_TO, points, 3);
406 }
407
408 void
go_path_close(GOPath * path)409 go_path_close (GOPath *path)
410 {
411 go_path_add_points (path, GO_PATH_ACTION_CLOSE_PATH, NULL, 0);
412 }
413
414 static void
_ring_wedge(GOPath * path,double cx,double cy,double rx_out,double ry_out,double rx_in,double ry_in,double th0,double th1,gboolean arc_to)415 _ring_wedge (GOPath *path,
416 double cx, double cy,
417 double rx_out, double ry_out,
418 double rx_in, double ry_in,
419 double th0, double th1,
420 gboolean arc_to)
421 {
422 double th_arc, th_out, th_in, th_delta, t, r;
423 double x, y;
424 int i, n_segs;
425 gboolean fill;
426 gboolean draw_in, ellipse = FALSE;
427
428 g_return_if_fail (GO_IS_PATH (path));
429
430 if (rx_out < rx_in) {
431 r = rx_out;
432 rx_out = rx_in;
433 rx_in = r;
434 }
435 if (ry_out < ry_in) {
436 r = ry_out;
437 ry_out = ry_in;
438 ry_in = r;
439 }
440 /* Here we tolerate slightly negative values for inside radius
441 * when deciding to fill. We use outside radius for comparison. */
442 fill = rx_in >= -(rx_out * 1e-6) && ry_in >= -(ry_out * 1e-6);
443
444 /* Here also we use outside radius for comparison. If inside radius
445 * is high enough, we'll emit an arc, otherwise we just use lines to
446 * wedge center. */
447 draw_in = fill && (rx_in > rx_out * 1e-6) && (ry_in > ry_out * 1e-6);
448
449 if (go_add_epsilon (th1 - th0) >= 2 * M_PI) {
450 ellipse = TRUE;
451 th1 = th0 + 2 * M_PI;
452 } else if (go_add_epsilon (th0 - th1) >= 2 * M_PI) {
453 ellipse = TRUE;
454 th0 = th1 + 2 * M_PI;
455 }
456
457 th_arc = th1 - th0;
458 n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
459
460 if (arc_to && !fill)
461 go_path_line_to (path, cx + rx_out * cos (th0), cy + ry_out * sin (th0));
462 else
463 go_path_move_to (path, cx + rx_out * cos (th0), cy + ry_out * sin (th0));
464
465 th_delta = th_arc / n_segs;
466 t = (8.0 / 3.0) * sin (th_delta * 0.25) * sin (th_delta * 0.25) / sin (th_delta * 0.5);
467
468 th_out = th0;
469 for (i = 0; i < n_segs; i++) {
470 x = cx + rx_out * cos (th_out + th_delta);
471 y = cy + ry_out * sin (th_out + th_delta);
472 go_path_curve_to (path,
473 cx + rx_out * (cos (th_out) - t * sin (th_out)),
474 cy + ry_out * (sin (th_out) + t * cos (th_out)),
475 x + rx_out * t * sin (th_out + th_delta),
476 y - ry_out * t * cos (th_out + th_delta),
477 x, y);
478 th_out += th_delta;
479 }
480
481 if (!fill)
482 return;
483
484 if (ellipse) {
485 go_path_close (path);
486 if (!draw_in)
487 return;
488 }
489
490 if (!ellipse) {
491 if (draw_in)
492 go_path_line_to (path,
493 cx + rx_in * cos (th1),
494 cy + ry_in * sin (th1));
495 else
496 go_path_line_to (path, cx, cy);
497 }
498
499 if (draw_in) {
500 th_in = th1;
501 for (i = 0; i < n_segs; i++) {
502 x = cx + rx_in * cos (th_in - th_delta);
503 y = cy + ry_in * sin (th_in - th_delta);
504 go_path_curve_to (path,
505 cx + rx_in * (cos (th_in) + t * sin (th_in)),
506 cy + ry_in * (sin (th_in) - t * cos (th_in)),
507 x - rx_in * t * sin (th_in - th_delta),
508 y + ry_in * t * cos (th_in - th_delta),
509 x, y);
510 th_in -= th_delta;
511 }
512 }
513
514 go_path_close (path);
515 }
516
517 void
go_path_ring_wedge(GOPath * path,double cx,double cy,double rx_out,double ry_out,double rx_in,double ry_in,double th0,double th1)518 go_path_ring_wedge (GOPath *path,
519 double cx, double cy,
520 double rx_out, double ry_out,
521 double rx_in, double ry_in,
522 double th0, double th1)
523 {
524 _ring_wedge (path, cx, cy, rx_out, ry_out, rx_in, ry_in, th0, th1, FALSE);
525 }
526
527 void
go_path_pie_wedge(GOPath * path,double cx,double cy,double rx,double ry,double th0,double th1)528 go_path_pie_wedge (GOPath *path,
529 double cx, double cy,
530 double rx, double ry,
531 double th0, double th1)
532 {
533 _ring_wedge (path, cx, cy, rx, ry, 0.0, 0.0, th0, th1, FALSE);
534 }
535
536 void
go_path_arc(GOPath * path,double cx,double cy,double rx,double ry,double th0,double th1)537 go_path_arc (GOPath *path,
538 double cx, double cy,
539 double rx, double ry,
540 double th0, double th1)
541 {
542 _ring_wedge (path, cx, cy, rx, ry, -1.0, -1.0, th0, th1, FALSE);
543 }
544
545 void
go_path_arc_to(GOPath * path,double cx,double cy,double rx,double ry,double th0,double th1)546 go_path_arc_to (GOPath *path,
547 double cx, double cy,
548 double rx, double ry,
549 double th0, double th1)
550 {
551 _ring_wedge (path, cx, cy, rx, ry, -1.0, -1.0, th0, th1, TRUE);
552 }
553
554 void
go_path_rectangle(GOPath * path,double x,double y,double width,double height)555 go_path_rectangle (GOPath *path,
556 double x, double y,
557 double width, double height)
558 {
559 go_path_move_to (path, x, y);
560 go_path_line_to (path, x + width, y);
561 go_path_line_to (path, x + width, y + height);
562 go_path_line_to (path, x, y + height);
563 go_path_close (path);
564 }
565
566 /**
567 * go_path_interpret:
568 * @path: #GOPath
569 * @direction: #GOPathDirection
570 * @move_to: (scope call): the callback for move to.
571 * @line_to: (scope call): the callback for drawing a line.
572 * @curve_to: (scope call): the callback for drawing a bezier cubic spline.
573 * @close_path: (scope call): the callback for closing the path.
574 * @closure: data to pass as first argument to the callbacks.
575 *
576 * This function can be used to draw a path or for other purposes.
577 * To draw using cairo, the closure argument should be a valid #cairo_t.
578 **/
579 void
go_path_interpret(GOPath const * path,GOPathDirection direction,GOPathMoveToFunc move_to,GOPathLineToFunc line_to,GOPathCurveToFunc curve_to,GOPathClosePathFunc close_path,void * closure)580 go_path_interpret (GOPath const *path,
581 GOPathDirection direction,
582 GOPathMoveToFunc move_to,
583 GOPathLineToFunc line_to,
584 GOPathCurveToFunc curve_to,
585 GOPathClosePathFunc close_path,
586 void *closure)
587 {
588 GOPathDataBuffer *buffer;
589 GOPathAction action, next_action;
590 GOPathPoint *points;
591 GOPathPoint *prev_control_points = NULL;
592 gboolean forward = (direction == GO_PATH_DIRECTION_FORWARD);
593 int index;
594
595 if (path == NULL)
596 return;
597
598 if (forward) {
599 for (buffer = path->data_buffer_head; buffer != NULL; buffer = buffer->next) {
600 int i;
601 points = buffer->points;
602
603 for (i = 0; i != buffer->n_actions; i++) {
604
605 action = buffer->actions[i];
606
607 switch (action) {
608 case GO_PATH_ACTION_MOVE_TO:
609 move_to (closure, &points[0]);
610 break;
611 case GO_PATH_ACTION_LINE_TO:
612 line_to (closure, &points[0]);
613 break;
614 case GO_PATH_ACTION_CURVE_TO:
615 curve_to (closure, &points[0], &points[1], &points[2]);
616 break;
617 case GO_PATH_ACTION_CLOSE_PATH:
618 default:
619 close_path (closure);
620 break;
621 }
622 points += action_n_args[action];
623 }
624 }
625 return;
626 }
627
628 next_action = GO_PATH_ACTION_MOVE_TO;
629
630 for (buffer = path->data_buffer_tail; buffer != NULL; buffer = buffer->previous) {
631 int i;
632
633 points = buffer->points + buffer->n_points;
634
635 for (i = buffer->n_actions - 1; i != -1; i--) {
636 action = next_action;
637 next_action = buffer->actions[i];
638
639 points -= action_n_args[next_action];
640
641 index = next_action == GO_PATH_ACTION_CURVE_TO ? 2 : 0;
642
643 switch (action) {
644 case GO_PATH_ACTION_MOVE_TO:
645 (*move_to) (closure, &points[index]);
646 break;
647 case GO_PATH_ACTION_LINE_TO:
648 (*line_to) (closure, &points[index]);
649 break;
650 case GO_PATH_ACTION_CURVE_TO:
651 (*curve_to) (closure,
652 &prev_control_points[1],
653 &prev_control_points[0],
654 &points[index]);
655 break;
656 case GO_PATH_ACTION_CLOSE_PATH:
657 default:
658 (*close_path) (closure);
659 break;
660 }
661
662 prev_control_points = &points[0];
663 }
664 }
665 }
666
667 /**
668 * go_path_interpret_full:
669 * @path: #GOPath
670 * @start: index of the first action to interpret
671 * @end: index of the last action to interpret
672 * @direction: #GOPathDirection
673 * @move_to: (scope call): the callback for move to.
674 * @line_to: (scope call): the callback for drawing a line.
675 * @curve_to: (scope call): the callback for drawing a bezier cubic spline.
676 * @close_path: (scope call): the callback for closing the path.
677 * @closure: data to pass as first argument to the callbacks.
678 *
679 * This function can be used to draw a portion path or for other purposes.
680 * Only actions between start and end will be executed. If start or end is
681 * negative, it is not taken into account.
682 * Since: 0.10.5
683 **/
684 void
go_path_interpret_full(GOPath const * path,gssize start,gssize end,GOPathDirection direction,GOPathMoveToFunc move_to,GOPathLineToFunc line_to,GOPathCurveToFunc curve_to,GOPathClosePathFunc close_path,void * closure)685 go_path_interpret_full (GOPath const *path,
686 gssize start,
687 gssize end,
688 GOPathDirection direction,
689 GOPathMoveToFunc move_to,
690 GOPathLineToFunc line_to,
691 GOPathCurveToFunc curve_to,
692 GOPathClosePathFunc close_path,
693 void *closure)
694 {
695 GOPathDataBuffer *buffer;
696 GOPathAction action, next_action;
697 GOPathPoint *points;
698 GOPathPoint *prev_control_points = NULL;
699 gboolean forward = (direction == GO_PATH_DIRECTION_FORWARD);
700 int index;
701 gssize cur;
702
703 if (path == NULL || start >= end)
704 return;
705
706 if (forward) {
707 cur = 0;
708 for (buffer = path->data_buffer_head; buffer != NULL; buffer = buffer->next) {
709 int i;
710 points = buffer->points;
711
712 for (i = 0; i != buffer->n_actions; i++) {
713
714 action = buffer->actions[i];
715
716 if (end > 0 && cur > end)
717 return;
718 else if (cur == start)
719 switch (action) {
720 case GO_PATH_ACTION_MOVE_TO:
721 case GO_PATH_ACTION_LINE_TO:
722 move_to (closure, &points[0]);
723 break;
724 case GO_PATH_ACTION_CURVE_TO:
725 move_to (closure, &points[2]);
726 break;
727 case GO_PATH_ACTION_CLOSE_PATH:
728 default:
729 break;
730 }
731 else if (cur > start)
732 switch (action) {
733 case GO_PATH_ACTION_MOVE_TO:
734 move_to (closure, &points[0]);
735 break;
736 case GO_PATH_ACTION_LINE_TO:
737 line_to (closure, &points[0]);
738 break;
739 case GO_PATH_ACTION_CURVE_TO:
740 curve_to (closure, &points[0], &points[1], &points[2]);
741 break;
742 case GO_PATH_ACTION_CLOSE_PATH:
743 default:
744 close_path (closure);
745 break;
746 }
747 points += action_n_args[action];
748 cur++;
749 }
750 }
751 return;
752 }
753
754 next_action = GO_PATH_ACTION_MOVE_TO;
755 cur = 0;
756 for (buffer = path->data_buffer_head; buffer != NULL; buffer = buffer->next)
757 cur += buffer->n_actions;
758
759 for (buffer = path->data_buffer_tail; buffer != NULL; buffer = buffer->previous) {
760 int i;
761
762 points = buffer->points + buffer->n_points;
763
764 for (i = buffer->n_actions - 1; i != -1; i--) {
765 cur--;
766 action = next_action;
767 next_action = buffer->actions[i];
768
769 points -= action_n_args[next_action];
770
771 index = next_action == GO_PATH_ACTION_CURVE_TO ? 2 : 0;
772
773 if (end > 0 && cur > end)
774 continue;
775 else if (cur == end)
776 switch (action) {
777 case GO_PATH_ACTION_MOVE_TO:
778 case GO_PATH_ACTION_LINE_TO:
779 case GO_PATH_ACTION_CURVE_TO:
780 (*move_to) (closure, &points[index]);
781 break;
782 case GO_PATH_ACTION_CLOSE_PATH:
783 default:
784 break;
785 }
786 else {
787 switch (action) {
788 case GO_PATH_ACTION_MOVE_TO:
789 (*move_to) (closure, &points[index]);
790 break;
791 case GO_PATH_ACTION_LINE_TO:
792 (*line_to) (closure, &points[index]);
793 break;
794 case GO_PATH_ACTION_CURVE_TO:
795 (*curve_to) (closure,
796 &prev_control_points[1],
797 &prev_control_points[0],
798 &points[index]);
799 break;
800 case GO_PATH_ACTION_CLOSE_PATH:
801 default:
802 (*close_path) (closure);
803 break;
804 }
805 if (cur < start)
806 return;
807 }
808
809 prev_control_points = &points[0];
810 }
811 }
812 }
813
814 static void
go_path_cairo_move_to(cairo_t * cr,GOPathPoint const * point)815 go_path_cairo_move_to (cairo_t *cr, GOPathPoint const *point)
816 {
817 cairo_move_to (cr, point->x, point->y);
818 }
819
820 static void
go_path_cairo_line_to(cairo_t * cr,GOPathPoint const * point)821 go_path_cairo_line_to (cairo_t *cr, GOPathPoint const *point)
822 {
823 cairo_line_to (cr, point->x, point->y);
824 }
825
826 static void
go_path_cairo_curve_to(cairo_t * cr,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)827 go_path_cairo_curve_to (cairo_t *cr, GOPathPoint const *point0,
828 GOPathPoint const *point1, GOPathPoint const *point2)
829 {
830 cairo_curve_to (cr, point0->x, point0->y, point1->x, point1->y, point2->x, point2->y);
831 }
832
833 /**
834 * go_path_to_cairo:
835 * @path: #GOPath
836 * @direction: #GOPathDirection
837 * @cr: #cairo_t
838 *
839 * Renders the path to the cairo context using its current settings.
840 **/
841
842 void
go_path_to_cairo(GOPath const * path,GOPathDirection direction,cairo_t * cr)843 go_path_to_cairo (GOPath const *path, GOPathDirection direction, cairo_t *cr)
844 {
845 go_path_interpret (path, direction,
846 (GOPathMoveToFunc) go_path_cairo_move_to,
847 (GOPathLineToFunc) go_path_cairo_line_to,
848 (GOPathCurveToFunc) go_path_cairo_curve_to,
849 (GOPathClosePathFunc) cairo_close_path, cr);
850 }
851
852 static void
go_path_append_move_to(GOPath * path,GOPathPoint const * point)853 go_path_append_move_to (GOPath *path, GOPathPoint const *point)
854 {
855 go_path_move_to (path, point->x, point->y);
856 }
857
858 static void
go_path_append_line_to(GOPath * path,GOPathPoint const * point)859 go_path_append_line_to (GOPath *path, GOPathPoint const *point)
860 {
861 go_path_line_to (path, point->x, point->y);
862 }
863
864 static void
go_path_append_curve_to(GOPath * path,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)865 go_path_append_curve_to (GOPath *path, GOPathPoint const *point0,
866 GOPathPoint const *point1, GOPathPoint const *point2)
867 {
868 go_path_curve_to (path, point0->x, point0->y, point1->x, point1->y, point2->x, point2->y);
869 }
870
871 static void
go_path_append_close(GOPath * path)872 go_path_append_close (GOPath *path)
873 {
874 go_path_close (path);
875 }
876
877 /**
878 * go_path_copy:
879 * @path: #GOPath
880 *
881 * Returns: a new #GOPath identical to @path.
882 **/
883
go_path_copy(GOPath const * path)884 GOPath *go_path_copy (GOPath const *path)
885 {
886 GOPath *new_path;
887
888 if (path == NULL)
889 return NULL;
890 new_path = go_path_new ();
891 new_path->options = path->options;
892 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
893 (GOPathMoveToFunc) go_path_append_move_to,
894 (GOPathLineToFunc) go_path_append_line_to,
895 (GOPathCurveToFunc) go_path_append_curve_to,
896 (GOPathClosePathFunc) go_path_append_close, new_path);
897 return new_path;
898 }
899
900
901 /**
902 * go_path_copy_restricted:
903 * @path: #GOPath
904 * @start: the first action to copy
905 * @end: the second action to copy
906 *
907 * Copies actions between start and end will be copied inside a new #GOPath.
908 * Returns: a new #GOPath. If start or end is
909 * negative, it is not taken into account.
910 * Since: 0.10.5
911 **/
912
go_path_copy_restricted(GOPath const * path,gssize start,gssize end)913 GOPath *go_path_copy_restricted (GOPath const *path, gssize start, gssize end)
914 {
915 GOPath *new_path;
916 if (path == NULL)
917 return NULL;
918 new_path = go_path_new ();
919 new_path->options = path->options;
920 go_path_interpret_full (path, start, end, GO_PATH_DIRECTION_FORWARD,
921 (GOPathMoveToFunc) go_path_append_move_to,
922 (GOPathLineToFunc) go_path_append_line_to,
923 (GOPathCurveToFunc) go_path_append_curve_to,
924 (GOPathClosePathFunc) go_path_append_close, new_path);
925 return new_path;
926 }
927 /**
928 * go_path_append:
929 * @path1: #GOPath
930 * @path2: #GOPath
931 *
932 * Appends @path2 at the end of @path1.
933 * Returns: @path1
934 */
go_path_append(GOPath * path1,GOPath const * path2)935 GOPath *go_path_append (GOPath *path1, GOPath const *path2)
936 {
937 if (path2 == NULL)
938 return path1;
939 if (path1 == NULL)
940 return go_path_copy (path2);
941 go_path_interpret (path2, GO_PATH_DIRECTION_FORWARD,
942 (GOPathMoveToFunc) go_path_append_move_to,
943 (GOPathLineToFunc) go_path_append_line_to,
944 (GOPathCurveToFunc) go_path_append_curve_to,
945 (GOPathClosePathFunc) go_path_append_close, path1);
946 return path1;
947 }
948
949 /******************************************************************************/
950 struct PathScaleClosure {
951 GOPath *path;
952 double scale_x, scale_y;
953 };
954
955 static void
go_path_scale_move_to(struct PathScaleClosure * closure,GOPathPoint const * point)956 go_path_scale_move_to (struct PathScaleClosure *closure,
957 GOPathPoint const *point)
958 {
959 go_path_move_to (closure->path,
960 point->x * closure->scale_x,
961 point->y * closure->scale_y);
962 }
963
964 static void
go_path_scale_line_to(struct PathScaleClosure * closure,GOPathPoint const * point)965 go_path_scale_line_to (struct PathScaleClosure *closure,
966 GOPathPoint const *point)
967 {
968 go_path_line_to (closure->path,
969 point->x * closure->scale_x,
970 point->y * closure->scale_y);
971 }
972
973 static void
go_path_scale_curve_to(struct PathScaleClosure * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)974 go_path_scale_curve_to (struct PathScaleClosure *closure,
975 GOPathPoint const *point0,
976 GOPathPoint const *point1,
977 GOPathPoint const *point2)
978 {
979 go_path_curve_to (closure->path,
980 point0->x * closure->scale_x,
981 point0->y * closure->scale_y,
982 point1->x * closure->scale_x,
983 point1->y * closure->scale_y,
984 point2->x * closure->scale_x,
985 point2->y * closure->scale_y);
986 }
987
988 static void
go_path_scale_close(struct PathScaleClosure * closure)989 go_path_scale_close (struct PathScaleClosure *closure)
990 {
991 go_path_close (closure->path);
992 }
993
994 /**
995 * go_path_scale:
996 * @path: #GOPath
997 * @scale_x: horizontal scale.
998 * @scale_y: vertical scale.
999 *
1000 * Builds a scaled.
1001 * Returns: (transfer full): the scaled path.
1002 **/
1003 GOPath *
go_path_scale(GOPath * path,double scale_x,double scale_y)1004 go_path_scale (GOPath *path, double scale_x, double scale_y)
1005 {
1006 struct PathScaleClosure closure;
1007 closure.path = go_path_new ();
1008 closure.scale_x = scale_x;
1009 closure.scale_y = scale_y;
1010 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
1011 (GOPathMoveToFunc) go_path_scale_move_to,
1012 (GOPathLineToFunc) go_path_scale_line_to,
1013 (GOPathCurveToFunc) go_path_scale_curve_to,
1014 (GOPathClosePathFunc) go_path_scale_close, &closure);
1015 return closure.path;
1016 }
1017
1018 /******************************************************************************/
1019
1020 struct PathSvgClosure {
1021 GString *str;
1022 char last_op;
1023 };
1024
1025 static void
go_path_svg_move_to(struct PathSvgClosure * closure,GOPathPoint const * point)1026 go_path_svg_move_to (struct PathSvgClosure *closure,
1027 GOPathPoint const *point)
1028 {
1029 if (closure->last_op != 'M') {
1030 g_string_append (closure->str, " M");
1031 closure->last_op = 'M';
1032 }
1033 g_string_append_printf (closure->str, " %g %g", point->x, point->y);
1034 }
1035
1036 static void
go_path_svg_line_to(struct PathSvgClosure * closure,GOPathPoint const * point)1037 go_path_svg_line_to (struct PathSvgClosure *closure,
1038 GOPathPoint const *point)
1039 {
1040 if (closure->last_op != 'L') {
1041 g_string_append (closure->str, " L");
1042 closure->last_op = 'L';
1043 }
1044 g_string_append_printf (closure->str, " %g %g", point->x, point->y);
1045 }
1046
1047 static void
go_path_svg_curve_to(struct PathSvgClosure * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)1048 go_path_svg_curve_to (struct PathSvgClosure *closure,
1049 GOPathPoint const *point0,
1050 GOPathPoint const *point1,
1051 GOPathPoint const *point2)
1052 {
1053 if (closure->last_op != 'C') {
1054 g_string_append (closure->str, " C");
1055 closure->last_op = 'C';
1056 }
1057 g_string_append_printf (closure->str, " %g %g %g %g %g %g",
1058 point0->x, point0->y,
1059 point1->x, point1->y,
1060 point2->x, point2->y);
1061 }
1062
1063 static void
go_path_svg_close(struct PathSvgClosure * closure)1064 go_path_svg_close (struct PathSvgClosure *closure)
1065 {
1066 g_string_append (closure->str, " Z");
1067 closure->last_op = 'Z';
1068 }
1069
1070 /**
1071 * go_path_to_svg:
1072 * @path: a #GOPath
1073 *
1074 * Builds an svg path from @path.
1075 * Returns: (transfer full): the svg:d string.
1076 **/
1077 char *
go_path_to_svg(GOPath * path)1078 go_path_to_svg (GOPath *path)
1079 {
1080 struct PathSvgClosure closure;
1081 closure.str = g_string_new ("");
1082 closure.last_op = 0;
1083 go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
1084 (GOPathMoveToFunc) go_path_svg_move_to,
1085 (GOPathLineToFunc) go_path_svg_line_to,
1086 (GOPathCurveToFunc) go_path_svg_curve_to,
1087 (GOPathClosePathFunc) go_path_svg_close, &closure);
1088
1089 return g_string_free (closure.str, FALSE);
1090 }
1091
1092 /*******************************************************************************
1093 * Paths from string
1094 ******************************************************************************/
1095
1096 typedef struct {
1097 char const *src;
1098 GOPath *path;
1099 GHashTable const *variables;
1100 double lastx, lasty;
1101 gboolean relative, clockwise, line_to, horiz;
1102 } PathParseState;
1103
1104 static void
skip_spaces(PathParseState * state)1105 skip_spaces (PathParseState *state)
1106 {
1107 while (*state->src == ' ')
1108 (state->src)++;
1109 }
1110
1111 static void
skip_comma_and_spaces(PathParseState * state)1112 skip_comma_and_spaces (PathParseState *state)
1113 {
1114 while (*state->src == ' ' || *state->src == ',')
1115 (state->src)++;
1116 }
1117
1118 static gboolean
parse_value(PathParseState * state,double * x)1119 parse_value (PathParseState *state, double *x)
1120 {
1121 char const *end, *c;
1122 gboolean integer_part = FALSE;
1123 gboolean fractional_part = FALSE;
1124 gboolean exponent_part = FALSE;
1125 double mantissa = 0.0;
1126 double exponent =0.0;
1127 double divisor;
1128 gboolean mantissa_sign = 1.0;
1129 gboolean exponent_sign = 1.0;
1130
1131 c = state->src;
1132
1133 if (*c == '?' || *c == '$') {
1134 char *var;
1135 double *val;
1136 state->src++;
1137 while (*state->src != 0 && *state->src != ' ' && *state->src != ',')
1138 state->src++;
1139 var = g_strndup (c, state->src - c);
1140 if (state->variables == NULL || ((val = g_hash_table_lookup ((GHashTable *) state->variables, var)) == NULL)) {
1141 g_free (var);
1142 return FALSE;
1143 }
1144 *x = *val;
1145 g_free (var);
1146 return TRUE;
1147 }
1148
1149 if (*c == '-') {
1150 mantissa_sign = -1.0;
1151 c++;
1152 } else if (*c == '+')
1153 c++;
1154
1155 if (*c >= '0' && *c <= '9') {
1156 integer_part = TRUE;
1157 mantissa = *c - '0';
1158 c++;
1159
1160 while (*c >= '0' && *c <= '9') {
1161 mantissa = mantissa * 10.0 + *c - '0';
1162 c++;
1163 }
1164 }
1165
1166
1167 if (*c == '.')
1168 c++;
1169 else if (!integer_part)
1170 return FALSE;
1171
1172 if (*c >= '0' && *c <= '9') {
1173 fractional_part = TRUE;
1174 mantissa += (*c - '0') * 0.1;
1175 divisor = 0.01;
1176 c++;
1177
1178 while (*c >= '0' && *c <= '9') {
1179 mantissa += (*c - '0') * divisor;
1180 divisor *= 0.1;
1181 c++;
1182 }
1183 }
1184
1185 if (!fractional_part && !integer_part)
1186 return FALSE;
1187
1188 end = c;
1189
1190 if (*c == 'E' || *c == 'e') {
1191 c++;
1192
1193 if (*c == '-') {
1194 exponent_sign = -1.0;
1195 c++;
1196 } else if (*c == '+')
1197 c++;
1198
1199 if (*c >= '0' && *c <= '9') {
1200 exponent_part = TRUE;
1201 exponent = *c - '0';
1202 c++;
1203
1204 while (*c >= '0' && *c <= '9') {
1205 exponent = exponent * 10.0 + *c - '0';
1206 c++;
1207 }
1208 }
1209
1210 }
1211
1212 if (exponent_part) {
1213 end = c;
1214 *x = mantissa_sign * mantissa * pow (10.0, exponent_sign * exponent);
1215 } else
1216 *x = mantissa_sign * mantissa;
1217
1218 state->src = end;
1219
1220 return TRUE;
1221 }
1222
1223 static gboolean
parse_values(PathParseState * state,unsigned int n_values,double * values)1224 parse_values (PathParseState *state, unsigned int n_values, double *values)
1225 {
1226 char const *ptr = state->src;
1227 unsigned int i;
1228
1229 skip_comma_and_spaces (state);
1230
1231 for (i = 0; i < n_values; i++) {
1232 if (!parse_value (state, &values[i])) {
1233 state->src = ptr;
1234 return FALSE;
1235 }
1236 skip_comma_and_spaces (state);
1237 }
1238
1239 return TRUE;
1240 }
1241
1242 static void
emit_function_2(PathParseState * state,void (* path_func)(GOPath *,double,double))1243 emit_function_2 (PathParseState *state,
1244 void (*path_func) (GOPath *, double, double))
1245 {
1246 double values[2];
1247
1248 skip_spaces (state);
1249
1250 while (parse_values (state, 2, values)) {
1251 if (state->relative) {
1252 state->lastx += values[0];
1253 state->lasty += values[1];
1254 } else {
1255 state->lastx = values[0];
1256 state->lasty = values[1];
1257 }
1258 path_func (state->path, state->lastx, state->lasty);
1259 }
1260 }
1261
1262 static void
emit_function_6(PathParseState * state,void (* path_func)(GOPath *,double,double,double,double,double,double))1263 emit_function_6 (PathParseState *state,
1264 void (*path_func) (GOPath *, double, double, double ,double, double, double))
1265 {
1266 double values[6];
1267
1268 skip_spaces (state);
1269
1270 while (parse_values (state, 6, values)) {
1271 if (state->relative) {
1272 values[0] += state->lastx;
1273 values[1] += state->lasty;
1274 values[2] += state->lastx;
1275 values[3] += state->lasty;
1276 state->lastx += values[4];
1277 state->lasty += values[5];
1278 } else {
1279 state->lastx = values[4];
1280 state->lasty = values[5];
1281 }
1282 path_func (state->path, values[0], values[1], values[2], values[3], state->lastx, state->lasty);
1283 }
1284 }
1285
1286 static void
emit_function_8(PathParseState * state,void (* path_func)(PathParseState *,double,double,double,double,double,double,double,double))1287 emit_function_8 (PathParseState *state,
1288 void (*path_func) (PathParseState *, double, double, double ,double, double, double, double ,double))
1289 {
1290 double values[8];
1291
1292 skip_spaces (state);
1293
1294 while (parse_values (state, 8, values)) {
1295 if (state->relative) {
1296 values[0] += state->lastx;
1297 values[1] += state->lasty;
1298 values[2] += state->lastx;
1299 values[3] += state->lasty;
1300 values[4] += state->lastx;
1301 values[5] += state->lasty;
1302 state->lastx += values[6];
1303 state->lasty += values[7];
1304 } else {
1305 state->lastx = values[6];
1306 state->lasty = values[7];
1307 }
1308 path_func (state, values[0], values[1], values[2], values[3], values[4], values[5], state->lastx, state->lasty);
1309 }
1310 }
1311
1312 static void
emit_quadratic(PathParseState * state)1313 emit_quadratic (PathParseState *state)
1314 {
1315 double values[4];
1316
1317 skip_spaces (state);
1318
1319 while (parse_values (state, 4, values)) {
1320 if (state->relative) {
1321 values[0] += state->lastx;
1322 values[1] += state->lasty;
1323 values[2] += state->lastx;
1324 values[3] += state->lasty;
1325 }
1326 go_path_curve_to (state->path,
1327 (state->lastx + 2 * values[0]) / 3.,
1328 (state->lasty + 2 * values[1]) / 3.,
1329 (2 * values[0] + values[2]) / 3.,
1330 (2 * values[1] + values[3]) / 3.,
1331 values[2], values[3]);
1332 state->lastx += values[2];
1333 state->lasty += values[3];
1334 }
1335 }
1336
1337 static void
_path_arc(PathParseState * state,double cx,double cy,double rx,double ry,double th0,double th1)1338 _path_arc (PathParseState *state,
1339 double cx, double cy,
1340 double rx, double ry,
1341 double th0, double th1)
1342 {
1343 double th_arc, th, th_delta, t;
1344 double x, y;
1345 int i, n_segs;
1346
1347 if (state->line_to)
1348 go_path_line_to (state->path, cx + rx * cos (th0), cy + ry * sin (th0));
1349 else
1350 go_path_move_to (state->path, cx + rx * cos (th0), cy + ry * sin (th0));
1351 if (state->clockwise) {
1352 while (th1 < th0)
1353 th1 += 2 * M_PI;
1354 } else {
1355 while (th1 > th0)
1356 th0 += 2 * M_PI;
1357 }
1358 th_arc = th1 - th0;
1359 n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
1360 th_delta = th_arc / n_segs;
1361 t = (8.0 / 3.0) * sin (th_delta * 0.25) * sin (th_delta * 0.25) / sin (th_delta * 0.5);
1362 th = th0;
1363 for (i = 0; i < n_segs; i++) {
1364 x = cx + rx * cos (th + th_delta);
1365 y = cy + ry * sin (th + th_delta);
1366 go_path_curve_to (state->path,
1367 cx + rx * (cos (th) - t * sin (th)),
1368 cy + ry * (sin (th) + t * cos (th)),
1369 x + rx * t * sin (th + th_delta),
1370 y - ry * t * cos (th + th_delta),
1371 x, y);
1372 th += th_delta;
1373 }
1374 state->lastx = cx + rx * sin (th1);
1375 state->lasty = cy + ry * sin (th1);
1376 }
1377
1378 static void
go_path_arc_degrees(PathParseState * state)1379 go_path_arc_degrees (PathParseState *state)
1380 {
1381 double values[6];
1382
1383 skip_spaces (state);
1384
1385 while (parse_values (state, 6, values)) {
1386 if (state->relative) {
1387 values[0] += state->lastx;
1388 values[1] += state->lasty;
1389 }
1390 _path_arc (state, values[0], values[1], values[2], values[3],
1391 values[4] * M_PI / 180., values[5] * M_PI / 180.);
1392 }
1393 }
1394
1395 static void
go_path_arc_full(PathParseState * state,double x1,double y1,double x2,double y2,double x3,double y3,double x4,double y4)1396 go_path_arc_full (PathParseState *state, double x1, double y1, double x2, double y2,
1397 double x3, double y3, double x4, double y4)
1398 {
1399 double cx, cy, rx, ry, th0, th1;
1400 cx = (x1 + x2) / 2.;
1401 cy = (y1 + y2) / 2;
1402 rx = MAX (x1, x2) - cx;
1403 ry = MAX (y1, y2) - cy;
1404 th0 = atan2 ((y3 - cy) / ry, (x3 - cx) / rx);
1405 th1 = atan2 ((y4 - cy) / ry, (x4 - cx) / rx);
1406 if (state->clockwise) {
1407 /* we need th1 > th0 */
1408 while (th1 < th0)
1409 th1 += 2 * M_PI;
1410 } else {
1411 /* we need th1 > th0 */
1412 while (th1 > th0)
1413 th0 += 2 * M_PI;
1414 }
1415 _path_arc (state, cx, cy, rx, ry, th0, th1);
1416 }
1417
1418 static void
go_path_quadrant(PathParseState * state)1419 go_path_quadrant (PathParseState *state)
1420 {
1421 double values[2], cx, cy, rx, ry, th0, th1;
1422
1423 skip_spaces (state);
1424
1425 while (parse_values (state, 2, values)) {
1426 if (state->relative) {
1427 values[0] += state->lastx;
1428 values[1] += state->lasty;
1429 }
1430 /* evaluating center and radius */
1431 if (state->horiz) {
1432 cx = state->lastx;
1433 cy = values[1];
1434 rx = fabs (values[0] - cx);
1435 ry = fabs (state->lasty - cy);
1436 } else {
1437 cx = values[0];
1438 cy = state->lasty;
1439 rx = fabs (state->lastx - cx);
1440 ry = fabs (values[1] - cy);
1441 }
1442 /* now the angles */
1443 th0 = atan2 (state->lasty - cy, state->lastx - cx);
1444 th1 = atan2 (values[1] - cy, values[0] - cx);
1445 /* and finally the direction */
1446 state->clockwise = (values[1] - cy) * (state->lastx - cx) - (state->lasty - cy) * (values[0] - cx) > 0.;
1447 _path_arc (state, cx, cy, rx, ry, th0, th1);
1448
1449 state->lastx = values[0];
1450 state->lasty = values[1];
1451 state->horiz = !state->horiz;
1452 }
1453 }
1454
1455 /**
1456 * go_path_new_from_svg:
1457 * @src: an SVG path.
1458 *
1459 * Returns: (transfer full): the newly allocated #GOPath.
1460 **/
1461 GOPath *
go_path_new_from_svg(char const * src)1462 go_path_new_from_svg (char const *src)
1463 {
1464 PathParseState state;
1465
1466 if (src == NULL)
1467 return NULL;
1468
1469 state.path = go_path_new ();
1470 state.src = (char *) src;
1471 state.lastx = state.lasty = 0.;
1472 state.variables = NULL;
1473
1474 skip_spaces (&state);
1475
1476 while (*state.src != '\0') {
1477 switch (*state.src) {
1478 case 'A':
1479 state.src++;
1480 state.relative = FALSE;
1481 break;
1482 case 'a':
1483 state.src++;
1484 state.relative = TRUE;
1485 break;
1486 case 'M':
1487 state.src++;
1488 state.relative = FALSE;
1489 emit_function_2 (&state, go_path_move_to);
1490 break;
1491 case 'm':
1492 state.src++;
1493 state.relative = TRUE;
1494 emit_function_2 (&state, go_path_move_to);
1495 break;
1496 case 'H':
1497 state.src++;
1498 state.relative = FALSE;
1499 break;
1500 case 'h':
1501 state.src++;
1502 state.relative = TRUE;
1503 break;
1504 case 'L':
1505 state.src++;
1506 state.relative = FALSE;
1507 emit_function_2 (&state, go_path_line_to);
1508 break;
1509 case 'l':
1510 state.src++;
1511 state.relative = TRUE;
1512 emit_function_2 (&state, go_path_line_to);
1513 break;
1514 case 'C':
1515 state.src++;
1516 state.relative = FALSE;
1517 emit_function_6 (&state, go_path_curve_to);
1518 break;
1519 case 'c':
1520 state.src++;
1521 state.relative = TRUE;
1522 emit_function_6 (&state, go_path_curve_to);
1523 break;
1524 case 'Q':
1525 state.src++;
1526 state.relative = FALSE;
1527 emit_quadratic (&state);
1528 break;
1529 case 'q':
1530 state.src++;
1531 state.relative = TRUE;
1532 emit_quadratic (&state);
1533 break;
1534 case 'S':
1535 state.src++;
1536 state.relative = FALSE;
1537 break;
1538 case 's':
1539 state.src++;
1540 state.relative = TRUE;
1541 break;
1542 case 'T':
1543 state.src++;
1544 state.relative = FALSE;
1545 break;
1546 case 't':
1547 state.src++;
1548 state.relative = TRUE;
1549 break;
1550 case 'V':
1551 state.src++;
1552 state.relative = FALSE;
1553 break;
1554 case 'v':
1555 state.src++;
1556 state.relative = TRUE;
1557 break;
1558 case 'Z':
1559 case 'z':
1560 state.src++;
1561 go_path_close (state.path);
1562 break;
1563 default:
1564 go_path_free (state.path);
1565 return NULL;
1566 }
1567 skip_spaces (&state);
1568 }
1569 return state.path;
1570 }
1571
1572 /**
1573 * go_path_new_from_odf_enhanced_path:
1574 * @src: an ODF enhanced path.
1575 * @variables: environment
1576 *
1577 * Returns: (transfer full): the newly allocated #GOPath or %NULL on error.
1578 **/
1579 GOPath *
go_path_new_from_odf_enhanced_path(char const * src,GHashTable const * variables)1580 go_path_new_from_odf_enhanced_path (char const *src, GHashTable const *variables)
1581 {
1582 PathParseState state;
1583
1584 if (src == NULL)
1585 return NULL;
1586
1587 state.path = go_path_new ();
1588 state.src = (char *) src;
1589 state.lastx = state.lasty = 0.;
1590 state.variables = variables;
1591 state.relative = FALSE;
1592
1593 skip_spaces (&state);
1594
1595 while (*state.src != '\0') {
1596 switch (*state.src) {
1597 case 'A':
1598 state.src++;
1599 state.clockwise = FALSE;
1600 state.line_to = TRUE;
1601 emit_function_8 (&state, go_path_arc_full);
1602 break;
1603 case 'B':
1604 state.src++;
1605 state.clockwise = FALSE;
1606 state.line_to = FALSE;
1607 emit_function_8 (&state, go_path_arc_full);
1608 break;
1609 case 'C':
1610 state.src++;
1611 emit_function_6 (&state, go_path_curve_to);
1612 break;
1613 case 'F':
1614 state.src++; /* ignore */
1615 break;
1616 case 'L':
1617 state.src++;
1618 emit_function_2 (&state, go_path_line_to);
1619 break;
1620 case 'M':
1621 state.src++;
1622 emit_function_2 (&state, go_path_move_to);
1623 break;
1624 case 'N': /* new sub path, just ignore */
1625 state.src++;
1626 break;
1627 case 'Q':
1628 state.src++;
1629 emit_quadratic (&state);
1630 break;
1631 case 'S':
1632 state.src++; /* ignore */
1633 break;
1634 case 'T':
1635 state.src++;
1636 state.clockwise = TRUE;
1637 state.line_to = TRUE;
1638 go_path_arc_degrees (&state);
1639 break;
1640 case 'U':
1641 state.src++;
1642 state.clockwise = TRUE;
1643 state.line_to = FALSE;
1644 go_path_arc_degrees (&state);
1645 break;
1646 case 'V':
1647 state.src++;
1648 state.clockwise = TRUE;
1649 state.line_to = FALSE;
1650 emit_function_8 (&state, go_path_arc_full);
1651 break;
1652 case 'W':
1653 state.src++;
1654 state.clockwise = TRUE;
1655 state.line_to = TRUE;
1656 emit_function_8 (&state, go_path_arc_full);
1657 break;
1658 case 'X':
1659 /* assuming that the horizontal/vertical alternance only applies
1660 * when the additional letters are omitted */
1661 state.src++;
1662 state.horiz = TRUE;
1663 go_path_quadrant (&state);
1664 break;
1665 case 'Y':
1666 state.src++;
1667 state.horiz = FALSE;
1668 go_path_quadrant (&state);
1669 break;
1670 case 'Z':
1671 state.src++;
1672 go_path_close (state.path);
1673 break;
1674 default:
1675 go_path_free (state.path);
1676 return NULL;
1677 }
1678 skip_spaces (&state);
1679 }
1680 if (state.path->data_buffer_head->n_actions == 0) {
1681 go_path_free (state.path);
1682 return NULL;
1683 }
1684 return state.path;
1685 }
1686