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