merge gnomecanvas into ardour tree, so that we can fix our own bugs and not wait...
[ardour.git] / libs / gnomecanvas / libgnomecanvas / gnome-canvas-path-def.c
1 #define GNOME_CANVAS_PATH_DEF_C
2
3 /*
4  * GnomeCanvasPathDef
5  *
6  * (C) 1999-2000 Lauris Kaplinski <lauris@ximian.com>
7  * Released under LGPL
8  *
9  * Authors: Lauris Kaplinski <lauris@ximian.com>
10  *          Rusty Conover <rconover@bangtail.net>
11  *
12  * Copyright 1999-2001 Ximian Inc. and authors.
13  */
14
15 #include <string.h>
16 #include <libart_lgpl/art_misc.h>
17 #include "gnome-canvas-path-def.h"
18
19 /* The number of points to allocate at once */
20 #define GNOME_CANVAS_PATH_DEF_LENSTEP 32
21
22 struct _GnomeCanvasPathDef {
23         gint refcount;
24         ArtBpath * bpath;
25         gint end;               /* ART_END position */
26         gint length;            /* Num allocated Bpaths */
27         gint substart;          /* subpath start */
28         gdouble x, y;           /* previous moveto position */
29         guint sbpath : 1;       /* Bpath is static */
30         guint hascpt : 1;       /* Currentpoint is defined */
31         guint posset : 1;       /* Previous was moveto */
32         guint moving : 1;       /* Bpath end is moving */
33         guint allclosed : 1;    /* All subpaths are closed */
34         guint allopen : 1;      /* All subpaths are open */
35 };
36
37 static gboolean sp_bpath_good (ArtBpath * bpath);
38 static ArtBpath * sp_bpath_check_subpath (ArtBpath * bpath);
39 static gint sp_bpath_length (const ArtBpath * bpath);
40 static gboolean sp_bpath_all_closed (const ArtBpath * bpath);
41 static gboolean sp_bpath_all_open (const ArtBpath * bpath);
42
43 /* Boxed Type Support */
44
45 static GnomeCanvasPathDef *
46 path_def_boxed_copy (GnomeCanvasPathDef * path_def)
47 {
48         if (path_def)
49                 gnome_canvas_path_def_ref (path_def);
50         return path_def;
51 }
52
53 GType
54 gnome_canvas_path_def_get_type (void)
55 {
56         static GType t = 0;
57         if (t == 0)
58                 t = g_boxed_type_register_static
59                                 ("GnomeCanvasPathDef",
60                                  (GBoxedCopyFunc)path_def_boxed_copy,
61                                  (GBoxedFreeFunc)gnome_canvas_path_def_unref);
62         return t;
63 }
64
65 /* Constructors */
66
67 /**
68  * gnome_canvas_path_def_new:
69  * 
70  * This function creates a new empty #gnome_canvas_path_def.
71  *
72  * Returns: the new canvas path definition. 
73  */
74 GnomeCanvasPathDef *
75 gnome_canvas_path_def_new (void)
76 {
77         GnomeCanvasPathDef * path;
78
79         path = gnome_canvas_path_def_new_sized (GNOME_CANVAS_PATH_DEF_LENSTEP);
80
81         return path;
82 }
83
84 /**
85  * gnome_canvas_path_def_new_sized:
86  * @length: number of points to allocate for the path
87  *
88  * This function creates a new #gnome_canvas_path_def with @length
89  * number of points allocated. It is useful, if you know the exact
90  * number of points in path, so you can avoid automatic point
91  * array reallocation.
92  *
93  * Returns: the new canvas path definition
94  */
95 GnomeCanvasPathDef *
96 gnome_canvas_path_def_new_sized (gint length)
97 {
98         GnomeCanvasPathDef * path;
99
100         g_return_val_if_fail (length > 0, NULL);
101
102         path = g_new (GnomeCanvasPathDef, 1);
103
104         path->refcount = 1;
105         path->bpath = art_new (ArtBpath, length);
106         path->end = 0;
107         path->bpath[path->end].code = ART_END;
108         path->length = length;
109         path->sbpath = FALSE;
110         path->hascpt = FALSE;
111         path->posset = FALSE;
112         path->moving = FALSE;
113         path->allclosed = TRUE;
114         path->allopen = TRUE;
115
116         return path;
117 }
118
119 /**
120  * gnome_canvas_path_def_new_from_bpath:
121  * @bpath: libart bezier path
122  *
123  * This function constructs a new #gnome_canvas_path_def and uses the
124  * passed @bpath as the contents.  The passed bpath should not be
125  * static as the path definition is editable when constructed with
126  * this function. Also, passed bpath will be freed with art_free, if
127  * path is destroyed, so use it with caution.
128  * For constructing a #gnome_canvas_path_def
129  * from (non-modifiable) bpath use
130  * #gnome_canvas_path_def_new_from_static_bpath.
131  *
132  * Returns: the new canvas path definition that is populated with the
133  * passed bezier path, if the @bpath is bad NULL is returned.
134  */
135 GnomeCanvasPathDef *
136 gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath)
137 {
138         GnomeCanvasPathDef * path;
139
140         g_return_val_if_fail (sp_bpath_good (bpath), NULL);
141
142         path = g_new (GnomeCanvasPathDef, 1);
143
144         path->refcount = 1;
145         path->bpath = bpath;
146         path->length = sp_bpath_length (bpath);
147         path->end = path->length - 1;
148         path->sbpath = FALSE;
149         path->hascpt = FALSE;
150         path->posset = FALSE;
151         path->moving = FALSE;
152         path->allclosed = sp_bpath_all_closed (bpath);
153         path->allopen = sp_bpath_all_open (bpath);
154
155         return path;
156 }
157
158 /**
159  * gnome_canvas_path_def_new_from_static_bpath:
160  * @bpath: libart bezier path
161  *
162  * This function constructs a new #gnome_canvas_path_def and
163  * references the passed @bpath as its contents.  The
164  * #gnome_canvas_path_def returned from this function is to be
165  * considered static and non-editable (meaning you cannot change the
166  * path from what you passed in @bpath). The bpath will not be freed,
167  * if path will be destroyed, so use it with caution.
168  *
169  * Returns: the new canvas path definition that references the passed
170  * @bpath, or if the path is bad NULL is returned.
171  */
172 GnomeCanvasPathDef *
173 gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath)
174 {
175         GnomeCanvasPathDef * path;
176
177         g_return_val_if_fail (sp_bpath_good (bpath), NULL);
178
179         path = g_new (GnomeCanvasPathDef, 1);
180
181         path->refcount = 1;
182         path->bpath = bpath;
183         path->length = sp_bpath_length (bpath);
184         path->end = path->length - 1;
185         path->sbpath = TRUE;
186         path->hascpt = FALSE;
187         path->posset = FALSE;
188         path->moving = FALSE;
189         path->allclosed = sp_bpath_all_closed (bpath);
190         path->allopen = sp_bpath_all_open (bpath);
191
192         return path;
193 }
194
195 /**
196  * gnome_canvas_path_def_new_from_foreign_bpath:
197  * @bpath: libart bezier path
198  *
199  * This function constructs a new #gnome_canvas_path_def and
200  * duplicates the contents of the passed @bpath in the definition.
201  *
202  * Returns: the newly created #gnome_canvas_path_def or NULL if the
203  * path is invalid.
204  */
205 GnomeCanvasPathDef *
206 gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath)
207 {
208         GnomeCanvasPathDef * path;
209         gint length;
210
211         g_return_val_if_fail (sp_bpath_good (bpath), NULL);
212
213         length = sp_bpath_length (bpath);
214
215         path = gnome_canvas_path_def_new_sized (length);
216         memcpy (path->bpath, bpath, sizeof (ArtBpath) * length);
217         path->end = length - 1;
218         path->allclosed = sp_bpath_all_closed (bpath);
219         path->allopen = sp_bpath_all_open (bpath);
220
221         return path;
222 }
223
224 /**
225  * gnome_canvas_path_def_ref:
226  * @path: a GnomeCanvasPathDef
227  *
228  * Increment the reference count of the GnomeCanvasPathDef.
229  */
230 void
231 gnome_canvas_path_def_ref (GnomeCanvasPathDef * path)
232 {
233         g_return_if_fail (path != NULL);
234
235         path->refcount++;
236 }
237
238 /**
239  * gnome_canvas_path_def_finish:
240  * @path: a GnomeCanvasPathDef
241  *
242  * Trims dynamic point array to exact length of path.
243  */
244 void
245 gnome_canvas_path_def_finish (GnomeCanvasPathDef * path)
246 {
247         g_return_if_fail (path != NULL);
248         g_return_if_fail (path->sbpath);
249
250         if ((path->end + 1) < path->length) {
251                 path->bpath = art_renew (path->bpath, ArtBpath, path->end + 1);
252                 path->length = path->end + 1;
253         }
254
255         path->hascpt = FALSE;
256         path->posset = FALSE;
257         path->moving = FALSE;
258 }
259 /**
260  * gnome_canvas_path_def_ensure_space:
261  * @path: a GnomeCanvasPathDef
262  * @space: number of points to guarantee are allocated at the end of
263  * the path.
264  *
265  * This function ensures that enough space for @space points is
266  * allocated at the end of the path.
267  */
268 void
269 gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space)
270 {
271         g_return_if_fail (path != NULL);
272         g_return_if_fail (space > 0);
273
274         if (path->end + space < path->length) return;
275
276         if (space < GNOME_CANVAS_PATH_DEF_LENSTEP) space = GNOME_CANVAS_PATH_DEF_LENSTEP;
277
278         path->bpath = art_renew (path->bpath, ArtBpath, path->length + space);
279
280         path->length += space;
281 }
282
283 /**
284  * gnome_canvas_path_def_copy:
285  * @dst: a GnomeCanvasPathDef where the contents of @src will be stored.
286  * @src: a GnomeCanvasPathDef whose contents will be copied it @src.
287  *
288  * This function copies the contents @src to @dest. The old @dest path
289  * array is freed and @dest is marked as non-static (editable),
290  * regardless of the status of @src.
291  */
292 void 
293 gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src)
294 {
295         g_return_if_fail (dst != NULL);
296         g_return_if_fail (src != NULL);
297
298         if (!dst->sbpath) g_free (dst->bpath);
299
300         memcpy (dst, src, sizeof (GnomeCanvasPathDef));
301
302         dst->bpath = g_new (ArtBpath, src->end + 1);
303         memcpy (dst->bpath, src->bpath, (src->end + 1) * sizeof (ArtBpath));
304
305         dst->sbpath = FALSE;
306 }
307
308
309 /**
310  * gnome_canvas_path_def_duplicate:
311  * @path: a GnomeCanvasPathDef to duplicate
312  *
313  * This function duplicates the passed @path. The new path is
314  * marked as non-static regardless of the state of original.
315  *
316  * Returns: a GnomeCanvasPathDef which is a duplicate of @path.
317  */
318 GnomeCanvasPathDef *
319 gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path)
320 {
321         GnomeCanvasPathDef * new;
322
323         g_return_val_if_fail (path != NULL, NULL);
324
325         new = gnome_canvas_path_def_new_from_foreign_bpath (path->bpath);
326         new->x = path->x;
327         new->y = path->y;
328         new->hascpt = path->hascpt;
329         new->posset = path->posset;
330         new->moving = path->moving;
331         new->allclosed = path->allclosed;
332         new->allopen = path->allopen;
333
334         return new;
335 }
336
337 /**
338  * gnome_canvas_path_def_concat:
339  * @list: a GSList of GnomeCanvasPathDefs to concatenate into one new
340  * path.
341  *
342  * This function concatenates a list of GnomeCanvasPathDefs into one
343  * newly created GnomeCanvasPathDef.
344  *
345  * Returns: a new GnomeCanvasPathDef
346  */
347 GnomeCanvasPathDef *
348 gnome_canvas_path_def_concat (const GSList * list)
349 {
350         GnomeCanvasPathDef * c, * new;
351         ArtBpath * bp;
352         const GSList * l;
353         gint length;
354
355         g_return_val_if_fail (list != NULL, NULL);
356
357         length = 1;
358
359         for (l = list; l != NULL; l = l->next) {
360                 c = (GnomeCanvasPathDef *) l->data;
361                 length += c->end;
362         }
363
364         new = gnome_canvas_path_def_new_sized (length);
365
366         bp = new->bpath;
367
368         for (l = list; l != NULL; l = l->next) {
369                 c = (GnomeCanvasPathDef *) l->data;
370                 memcpy (bp, c->bpath, c->end * sizeof (ArtBpath));
371                 bp += c->end;
372         }
373
374         bp->code = ART_END;
375
376         new->end = length - 1;
377
378         new->allclosed = sp_bpath_all_closed (new->bpath);
379         new->allopen = sp_bpath_all_open (new->bpath);
380
381         return new;
382 }
383
384 /**
385  * gnome_canvas_path_def_split:
386  * @path: a GnomeCanvasPathDef
387  *
388  * This function splits the passed @path into a list of
389  * GnomeCanvasPathDefs which represent each segment of the origional
390  * path.  The path is split when ever an ART_MOVETO or ART_MOVETO_OPEN
391  * is encountered. The closedness of resulting paths is set accordingly
392  * to closedness of corresponding segment.
393  *
394  * Returns: a list of GnomeCanvasPathDef(s).
395  */
396 GSList *
397 gnome_canvas_path_def_split (const GnomeCanvasPathDef * path)
398 {
399         GnomeCanvasPathDef * new;
400         GSList * l;
401         gint p, i;
402
403         g_return_val_if_fail (path != NULL, NULL);
404
405         p = 0;
406         l = NULL;
407
408         while (p < path->end) {
409                 i = 1;
410                 while ((path->bpath[p + i].code == ART_LINETO) || (path->bpath[p + i].code == ART_CURVETO)) i++;
411                 new = gnome_canvas_path_def_new_sized (i + 1);
412                 memcpy (new->bpath, path->bpath + p, i * sizeof (ArtBpath));
413                 new->end = i;
414                 new->bpath[i].code = ART_END;
415                 new->allclosed = (new->bpath->code == ART_MOVETO);
416                 new->allopen = (new->bpath->code == ART_MOVETO_OPEN);
417                 l = g_slist_append (l, new);
418                 p += i;
419         }
420
421         return l;
422 }
423
424 /**
425  * gnome_canvas_path_def_open_parts:
426  * @path: a GnomeCanvasPathDef
427  * 
428  * This function creates a new GnomeCanvasPathDef that contains all of
429  * the open segments on the passed @path.
430  *
431  * Returns: a new GnomeCanvasPathDef that contains all of the open segemtns in @path.
432  */
433 GnomeCanvasPathDef *
434 gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path)
435 {
436         GnomeCanvasPathDef * new;
437         ArtBpath * p, * d;
438         gint len;
439         gboolean closed;
440
441         g_return_val_if_fail (path != NULL, NULL);
442
443         closed = TRUE;
444         len = 0;
445
446         for (p = path->bpath; p->code != ART_END; p++) {
447                 switch (p->code) {
448                 case ART_MOVETO_OPEN:
449                         closed = FALSE;
450                         len++;
451                         break;
452                 case ART_MOVETO:
453                         closed = TRUE;
454                         break;
455                 case ART_LINETO:
456                 case ART_CURVETO:
457                         if (!closed) len++;
458                         break;
459                 default:
460                         g_assert_not_reached ();
461                 }
462         }
463
464         new = gnome_canvas_path_def_new_sized (len + 1);
465
466         closed = TRUE;
467         d = new->bpath;
468
469         for (p = path->bpath; p->code != ART_END; p++) {
470                 switch (p->code) {
471                 case ART_MOVETO_OPEN:
472                         closed = FALSE;
473                         *d++ = *p;
474                         break;
475                 case ART_MOVETO:
476                         closed = TRUE;
477                         break;
478                 case ART_LINETO:
479                 case ART_CURVETO:
480                         if (!closed) *d++ = *p;
481                         break;
482                 default:
483                         g_assert_not_reached ();
484                 }
485         }
486
487         d->code = ART_END;
488
489         new->end = len;
490         new->allclosed = FALSE;
491         new->allopen = TRUE;
492
493         return new;
494 }
495
496 /**
497  * gnome_canvas_path_def_closed_parts:
498  * @path: a GnomeCanvasPathDef
499  * 
500  * This function returns a new GnomeCanvasPathDef that contains the
501  * all of close parts of passed @path.
502  *
503  * Returns: a new GnomeCanvasPathDef that contains all of the closed
504  * parts of passed @path.
505  */
506 GnomeCanvasPathDef *
507 gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path)
508 {
509         GnomeCanvasPathDef * new;
510         ArtBpath * p, * d;
511         gint len;
512         gboolean closed;
513
514         g_return_val_if_fail (path != NULL, NULL);
515
516         closed = FALSE;
517         len = 0;
518
519         for (p = path->bpath; p->code != ART_END; p++) {
520                 switch (p->code) {
521                 case ART_MOVETO_OPEN:
522                         closed = FALSE;
523                         break;
524                 case ART_MOVETO:
525                         closed = TRUE;
526                         len++;
527                         break;
528                 case ART_LINETO:
529                 case ART_CURVETO:
530                         if (closed) len++;
531                         break;
532                 default:
533                         g_assert_not_reached ();
534                 }
535         }
536
537         new = gnome_canvas_path_def_new_sized (len + 1);
538
539         closed = FALSE;
540         d = new->bpath;
541
542         for (p = path->bpath; p->code != ART_END; p++) {
543                 switch (p->code) {
544                 case ART_MOVETO_OPEN:
545                         closed = FALSE;
546                         break;
547                 case ART_MOVETO:
548                         closed = TRUE;
549                         *d++ = *p;
550                         break;
551                 case ART_LINETO:
552                 case ART_CURVETO:
553                         if (closed) *d++ = *p;
554                         break;
555                 default:
556                         g_assert_not_reached ();
557                 }
558         }
559
560         d->code = ART_END;
561
562         new->end = len;
563         new->allclosed = TRUE;
564         new->allopen = FALSE;
565
566         return new;
567 }
568
569 /**
570  * gnome_canvas_path_def_close_all:
571  * @path: a GnomeCanvasPathDef
572  *
573  * This function closes all of the open segments in the passed path
574  * and returns a new GnomeCanvasPathDef.
575  *
576  * Returns: a GnomeCanvasPathDef that contains the contents of @path
577  * but has modified the path is fully closed
578  */
579 GnomeCanvasPathDef *
580 gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path)
581 {
582         GnomeCanvasPathDef * new;
583         ArtBpath * p, * d, * start;
584         gint len;
585         gboolean closed;
586
587         g_return_val_if_fail (path != NULL, NULL);
588
589         if (path->allclosed) {
590                 new = gnome_canvas_path_def_duplicate (path);
591                 return new;
592         }
593
594         len = 1;
595
596         /* Count MOVETO_OPEN */
597
598         for (p = path->bpath; p->code != ART_END; p++) {
599                 len += 1;
600                 if (p->code == ART_MOVETO_OPEN) len += 2;
601         }
602
603         new = gnome_canvas_path_def_new_sized (len);
604
605         d = start = new->bpath;
606         closed = TRUE;
607
608         for (p = path->bpath; p->code != ART_END; p++) {
609                 switch (p->code) {
610                 case ART_MOVETO_OPEN:
611                         start = p;
612                         closed = FALSE;
613                 case ART_MOVETO:
614                         if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
615                                 d->code = ART_LINETO;
616                                 d->x3 = start->x3;
617                                 d->y3 = start->y3;
618                                 d++;
619                         }
620                         if (p->code == ART_MOVETO) closed = TRUE;
621                         d->code = ART_MOVETO;
622                         d->x3 = p->x3;
623                         d->y3 = p->y3;
624                         d++;
625                         break;
626                 case ART_LINETO:
627                 case ART_CURVETO:
628                         *d++ = *p;
629                         break;
630                 default:
631                         g_assert_not_reached ();
632                 }
633         }
634
635         if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
636                 d->code = ART_LINETO;
637                 d->x3 = start->x3;
638                 d->y3 = start->y3;
639                 d++;
640         }
641
642         d->code = ART_END;
643
644         new->end = d - new->bpath;
645         new->allclosed = TRUE;
646         new->allopen = FALSE;
647
648         return new;
649 }
650
651 /* Destructor */
652
653 /**
654  * gnome_canvas_path_def_unref:
655  * @path: a GnomeCanvasPathDef
656  *
657  * Decrease the reference count of the passed @path.  If the reference
658  * count is < 1 the path is deallocated.
659  */
660 void
661 gnome_canvas_path_def_unref (GnomeCanvasPathDef * path)
662 {
663         g_return_if_fail (path != NULL);
664
665         if (--path->refcount < 1) {
666                 if ((!path->sbpath) && (path->bpath)) art_free (path->bpath);
667                 g_free (path);
668         }
669 }
670
671
672 /* Methods */
673 /**
674  * gnome_canvas_path_def_reset:
675  * @path: a GnomeCanvasPathDef
676  *
677  * This function clears the contents of the passed @path.
678  */
679 void
680 gnome_canvas_path_def_reset (GnomeCanvasPathDef * path)
681 {
682         g_return_if_fail (path != NULL);
683         g_return_if_fail (!path->sbpath);
684
685         path->bpath->code = ART_END;
686         path->end = 0;
687         path->hascpt = FALSE;
688         path->posset = FALSE;
689         path->moving = FALSE;
690         path->allclosed = TRUE;
691         path->allopen = TRUE;
692 }
693
694 /* Several consequtive movetos are ALLOWED */
695
696 /**
697  * gnome_canvas_path_def_moveto:
698  * @path: a GnomeCanvasPathDef
699  * @x: x coordinate
700  * @y: y coordinate
701  *
702  * This function adds starts new subpath on @path, and sets its
703  * starting point to @x and @y. If current subpath is empty, it
704  * simply changes its starting coordinates to new values.
705  */
706 void
707 gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
708 {
709         g_return_if_fail (path != NULL);
710         g_return_if_fail (!path->sbpath);
711         g_return_if_fail (!path->moving);
712
713         path->substart = path->end;
714         path->hascpt = TRUE;
715         path->posset = TRUE;
716         path->x = x;
717         path->y = y;
718
719         path->allclosed = FALSE;
720 }
721
722 /**
723  * gnome_canvas_path_def_lineto:
724  * @path: a GnomeCanvasPathDef
725  * @x: x coordinate
726  * @y: y coordinate
727  *
728  * This function add a line segment to the passed @path with the
729  * specified @x and @y coordinates.
730  */
731 void
732 gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
733 {
734         ArtBpath * bp;
735
736         g_return_if_fail (path != NULL);
737         g_return_if_fail (!path->sbpath);
738         g_return_if_fail (path->hascpt);
739
740         if (path->moving) {
741                 /* simply fix endpoint */
742                 g_return_if_fail (!path->posset);
743                 g_return_if_fail (path->end > 1);
744                 bp = path->bpath + path->end - 1;
745                 g_return_if_fail (bp->code == ART_LINETO);
746                 bp->x3 = x;
747                 bp->y3 = y;
748                 path->moving = FALSE;
749                 return;
750         }
751
752         if (path->posset) {
753                 /* start a new segment */
754                 gnome_canvas_path_def_ensure_space (path, 2);
755                 bp = path->bpath + path->end;
756                 bp->code = ART_MOVETO_OPEN;
757                 bp->x3 = path->x;
758                 bp->y3 = path->y;
759                 bp++;
760                 bp->code = ART_LINETO;
761                 bp->x3 = x;
762                 bp->y3 = y;
763                 bp++;
764                 bp->code = ART_END;
765                 path->end += 2;
766                 path->posset = FALSE;
767                 path->allclosed = FALSE;
768                 return;
769         }
770
771         /* Simply add line */
772
773         g_return_if_fail (path->end > 1);
774         gnome_canvas_path_def_ensure_space (path, 1);
775         bp = path->bpath + path->end;
776         bp->code = ART_LINETO;
777         bp->x3 = x;
778         bp->y3 = y;
779         bp++;
780         bp->code = ART_END;
781         path->end++;
782 }
783
784
785 /**
786  * gnome_canvas_path_def_lineto_moving:
787  * @path: a GnomeCanvasPathDef
788  * @x: x coordinate
789  * @y: y coordinate
790  *
791  * This functions adds a new line segment with loose endpoint to the path, or
792  * if endpoint is already loose, changes its coordinates to @x, @y. You
793  * can change the coordinates of loose endpoint as many times as you want,
794  * the last ones set will be fixed, if you continue line. This is useful
795  * for handling drawing with mouse.
796  */ 
797 void
798 gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y)
799 {
800         ArtBpath * bp;
801
802         g_return_if_fail (path != NULL);
803         g_return_if_fail (!path->sbpath);
804         g_return_if_fail (path->hascpt);
805
806         if (path->moving) {
807                 /* simply change endpoint */
808                 g_return_if_fail (!path->posset);
809                 g_return_if_fail (path->end > 1);
810                 bp = path->bpath + path->end - 1;
811                 g_return_if_fail (bp->code == ART_LINETO);
812                 bp->x3 = x;
813                 bp->y3 = y;
814                 return;
815         }
816
817         if (path->posset) {
818                 /* start a new segment */
819                 gnome_canvas_path_def_ensure_space (path, 2);
820                 bp = path->bpath + path->end;
821                 bp->code = ART_MOVETO_OPEN;
822                 bp->x3 = path->x;
823                 bp->y3 = path->y;
824                 bp++;
825                 bp->code = ART_LINETO;
826                 bp->x3 = x;
827                 bp->y3 = y;
828                 bp++;
829                 bp->code = ART_END;
830                 path->end += 2;
831                 path->posset = FALSE;
832                 path->moving = TRUE;
833                 path->allclosed = FALSE;
834                 return;
835         }
836
837         /* Simply add line */
838
839         g_return_if_fail (path->end > 1);
840         gnome_canvas_path_def_ensure_space (path, 1);
841         bp = path->bpath + path->end;
842         bp->code = ART_LINETO;
843         bp->x3 = x;
844         bp->y3 = y;
845         bp++;
846         bp->code = ART_END;
847         path->end++;
848         path->moving = TRUE;
849 }
850
851 /**
852  * gnome_canvas_path_def_curveto:
853  * @path: a GnomeCanvasPathDef
854  * @x0: first control point x coordinate
855  * @y0: first control point y coordinate
856  * @x1: second control point x coordinate
857  * @y1: second control point y coordinate
858  * @x2: end of curve x coordinate
859  * @y2: end of curve y coordinate
860  *
861  * This function adds a bezier curve segment to the path definition.
862  */
863  
864 void
865 gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
866 {
867         ArtBpath * bp;
868
869         g_return_if_fail (path != NULL);
870         g_return_if_fail (!path->sbpath);
871         g_return_if_fail (path->hascpt);
872         g_return_if_fail (!path->moving);
873
874         if (path->posset) {
875                 /* start a new segment */
876                 gnome_canvas_path_def_ensure_space (path, 2);
877                 bp = path->bpath + path->end;
878                 bp->code = ART_MOVETO_OPEN;
879                 bp->x3 = path->x;
880                 bp->y3 = path->y;
881                 bp++;
882                 bp->code = ART_CURVETO;
883                 bp->x1 = x0;
884                 bp->y1 = y0;
885                 bp->x2 = x1;
886                 bp->y2 = y1;
887                 bp->x3 = x2;
888                 bp->y3 = y2;
889                 bp++;
890                 bp->code = ART_END;
891                 path->end += 2;
892                 path->posset = FALSE;
893                 path->allclosed = FALSE;
894                 return;
895         }
896
897         /* Simply add path */
898
899         g_return_if_fail (path->end > 1);
900         gnome_canvas_path_def_ensure_space (path, 1);
901         bp = path->bpath + path->end;
902         bp->code = ART_CURVETO;
903         bp->x1 = x0;
904         bp->y1 = y0;
905         bp->x2 = x1;
906         bp->y2 = y1;
907         bp->x3 = x2;
908         bp->y3 = y2;
909         bp++;
910         bp->code = ART_END;
911         path->end++;
912 }
913
914 /**
915  * gnome_canvas_path_def_closepath:
916  * @path: a GnomeCanvasPathDef
917  *
918  * This function closes the last subpath of @path, adding a ART_LINETO to
919  * subpath starting point, if needed and changing starting pathcode to
920  * ART_MOVETO
921  */
922 void
923 gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path)
924 {
925         ArtBpath * bs, * be;
926
927         g_return_if_fail (path != NULL);
928         g_return_if_fail (!path->sbpath);
929         g_return_if_fail (path->hascpt);
930         g_return_if_fail (!path->posset);
931         g_return_if_fail (!path->moving);
932         g_return_if_fail (!path->allclosed);
933         /* We need at last M + L + L + E */
934         g_return_if_fail (path->end - path->substart > 2);
935
936         bs = path->bpath + path->substart;
937         be = path->bpath + path->end - 1;
938
939         if ((bs->x3 != be->x3) || (bs->y3 != be->y3)) {
940                 gnome_canvas_path_def_lineto (path, bs->x3, bs->y3);
941         }
942
943         bs = path->bpath + path->substart; /* NB. def_lineto can
944                                               realloc bpath */
945         bs->code = ART_MOVETO;
946
947         path->allclosed = sp_bpath_all_closed (path->bpath);
948         path->allopen = sp_bpath_all_open (path->bpath);
949
950         path->hascpt = FALSE;
951 }
952
953 /**
954  * gnome_canvas_path_def_closepath_current:
955  * @path: a GnomeCanvasPathDef
956  *
957  * This function closes the last subpath by setting the coordinates of
958  * the endpoint of the last segment (line or curve) to starting point.
959  */
960 void
961 gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path)
962 {
963         ArtBpath * bs, * be;
964
965         g_return_if_fail (path != NULL);
966         g_return_if_fail (!path->sbpath);
967         g_return_if_fail (path->hascpt);
968         g_return_if_fail (!path->posset);
969         g_return_if_fail (!path->allclosed);
970         /* We need at last M + L + L + E */
971         g_return_if_fail (path->end - path->substart > 2);
972
973         bs = path->bpath + path->substart;
974         be = path->bpath + path->end - 1;
975
976         be->x3 = bs->x3;
977         be->y3 = bs->y3;
978
979         bs->code = ART_MOVETO;
980
981         path->allclosed = sp_bpath_all_closed (path->bpath);
982         path->allopen = sp_bpath_all_open (path->bpath);
983
984         path->hascpt = FALSE;
985         path->moving = FALSE;
986 }
987
988 /**
989  * gnome_canvas_path_def_bpath:
990  * @path: a GnomeCanvasPathDef
991  *
992  * This function returns a ArtBpath that consists of the path
993  * definition.
994  *
995  * Returns: ArtBpath
996  */
997 ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path)
998 {
999         g_return_val_if_fail (path != NULL, NULL);
1000
1001         return path->bpath;
1002 }
1003
1004 /**
1005  * gnome_canvas_path_def_length:
1006  * @path: a GnomeCanvasPathDef
1007  *
1008  * This function returns the length of the path definition.  Not
1009  * Euclidian length of the path but rather the number of points on the
1010  * path.
1011  *
1012  * Returns: integer, number of points on the path.
1013  */
1014 gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path)
1015 {
1016         g_return_val_if_fail (path != NULL, -1);
1017
1018         return path->end + 1;
1019 }
1020
1021 /**
1022  * gnome_canvas_path_def_is_empty:
1023  * @path: a GnomeCanvasPathDef
1024  *
1025  * This function is a boolean test to see if the path is empty,
1026  * meaning containing no line segments.
1027  *
1028  * Returns: boolean, indicating if the path is empty.
1029  */
1030 gboolean
1031 gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path)
1032 {
1033         g_return_val_if_fail (path != NULL, TRUE);
1034
1035         return (path->bpath->code == ART_END);
1036 }
1037
1038 /**
1039  * gnome_canvas_path_def_has_currentpoint:
1040  * @path: a GnomeCanvasPathdef
1041  *
1042  * This function is a boolean test checking to see if the path has a
1043  * current point defined. Current point will be set by line operators,
1044  * and cleared by closing subpath.
1045  *
1046  * Returns: boolean, indicating if the path has a current point defined.
1047  */
1048 gboolean
1049 gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path)
1050 {
1051         g_return_val_if_fail (path != NULL, FALSE);
1052
1053         return (path->hascpt);
1054 }
1055
1056 /**
1057  * gnome_canvas_path_def_currentpoint:
1058  * @path: a GnomeCanvasPathDef
1059  * @p: a ArtPoint where to store the current point
1060  *
1061  * Stores the current point of the path definition in the passed ArtPoint @p.
1062  */
1063 void
1064 gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p)
1065 {
1066         g_return_if_fail (path != NULL);
1067         g_return_if_fail (p != NULL);
1068         g_return_if_fail (path->hascpt);
1069
1070         if (path->posset) {
1071                 p->x = path->x;
1072                 p->y = path->y;
1073         } else {
1074                 p->x = (path->bpath + path->end - 1)->x3;
1075                 p->y = (path->bpath + path->end - 1)->y3;
1076         }       
1077 }
1078
1079 /**
1080  * gnome_canvas_path_def_last_bpath:
1081  * @path: a GnomeCanvasPathDef
1082  *
1083  * This function returns pointer to the last ArtBpath segment in the path
1084  * definition.
1085  *
1086  * Returns: ArtBpath, being the last segment in the path definition or
1087  * null if no line segments have been defined.
1088  */
1089 ArtBpath *
1090 gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path)
1091 {
1092         g_return_val_if_fail (path != NULL, NULL);
1093
1094         if (path->end == 0) return NULL;
1095
1096         return path->bpath + path->end - 1;
1097 }
1098
1099 /**
1100  * gnome_canvas_path_def_first_bpath:
1101  * @path: a GnomeCanvasPathDef
1102  *
1103  * This function returns the first ArtBpath point in the definition.
1104  *
1105  * Returns: ArtBpath being the first point in the path definition or
1106  * null if no points are defined
1107 */
1108 ArtBpath *
1109 gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path)
1110 {
1111         g_return_val_if_fail (path != NULL, NULL);
1112
1113         if (path->end == 0) return NULL;
1114
1115         return path->bpath;
1116 }
1117
1118 /**
1119  * gnome_canvas_path_def_any_open:
1120  * @path: a GnomeCanvasPathDef
1121  *
1122  * This function returns a boolean value indicating if the path has
1123  * any open segments.
1124  *
1125  * Returns: boolean, indicating if the path has any open segments.
1126  */
1127 gboolean
1128 gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path)
1129 {
1130         g_return_val_if_fail (path != NULL, FALSE);
1131
1132         return (!path->allclosed);
1133 }
1134
1135 /**
1136  * gnome_canvas_path_def_all_open:
1137  * @path: a GnomeCanvasPathDef
1138  *
1139  * This function returns a boolean value indicating if the path only
1140  * contains open segments.
1141  *
1142  * Returns: boolean, indicating if the path has all open segments.
1143  */
1144 gboolean
1145 gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path)
1146 {
1147         g_return_val_if_fail (path != NULL, FALSE);
1148
1149         return (path->allopen);
1150 }
1151
1152 /**
1153  * gnome_canvas_path_def_any_closed:
1154  * @path: a GnomeCanvasPathDef
1155  *
1156  * This function returns a boolean valid indicating if the path has
1157  * any closed segements.
1158  *
1159  * Returns: boolean, indicating if the path has any closed segments.
1160  */
1161 gboolean
1162 gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path)
1163 {
1164         g_return_val_if_fail (path != NULL, FALSE);
1165
1166         return (!path->allopen);
1167 }
1168
1169 /**
1170  * gnome_canvas_path_def_all_closed:
1171  * @path: a GnomeCanvasPathDef
1172  *
1173  * This function returns a boolean value indicating if the path only
1174  * contains closed segments.
1175  *
1176  * Returns: boolean, indicating if the path has all closed segments.
1177  */
1178 gboolean
1179 gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path)
1180 {
1181         g_return_val_if_fail (path != NULL, FALSE);
1182
1183         return (path->allclosed);
1184 }
1185
1186 /* Private methods */
1187
1188 static
1189 gboolean sp_bpath_good (ArtBpath * bpath)
1190 {
1191         ArtBpath * bp;
1192
1193         g_return_val_if_fail (bpath != NULL, FALSE);
1194
1195         if (bpath->code == ART_END)
1196                 return TRUE;
1197
1198         bp = bpath;
1199
1200         while (bp->code != ART_END) {
1201                 bp = sp_bpath_check_subpath (bp);
1202                 if (bp == NULL) return FALSE;
1203         }
1204
1205         return TRUE;
1206 }
1207
1208 static ArtBpath *
1209 sp_bpath_check_subpath (ArtBpath * bpath)
1210 {
1211         gint i, len;
1212         gboolean closed;
1213
1214         g_return_val_if_fail (bpath != NULL, NULL);
1215
1216         if (bpath->code == ART_MOVETO) {
1217                 closed = TRUE;
1218         } else if (bpath->code == ART_MOVETO_OPEN) {
1219                 closed = FALSE;
1220         } else {
1221                 return NULL;
1222         }
1223
1224         len = 0;
1225
1226         for (i = 1; (bpath[i].code != ART_END) && (bpath[i].code != ART_MOVETO) && (bpath[i].code != ART_MOVETO_OPEN); i++) {
1227                 switch (bpath[i].code) {
1228                         case ART_LINETO:
1229                         case ART_CURVETO:
1230                                 len++;
1231                                 break;
1232                         default:
1233                                 return NULL;
1234                 }
1235         }
1236
1237         if (closed) {
1238                 if (len < 2) return NULL;
1239                 if ((bpath->x3 != bpath[i-1].x3) || (bpath->y3 != bpath[i-1].y3)) return NULL;
1240         } else {
1241                 if (len < 1) return NULL;
1242         }
1243
1244         return bpath + i;
1245 }
1246
1247 static gint
1248 sp_bpath_length (const ArtBpath * bpath)
1249 {
1250         gint l;
1251
1252         g_return_val_if_fail (bpath != NULL, FALSE);
1253
1254         for (l = 0; bpath[l].code != ART_END; l++) ;
1255
1256         l++;
1257
1258         return l;
1259 }
1260
1261 static gboolean
1262 sp_bpath_all_closed (const ArtBpath * bpath)
1263 {
1264         const ArtBpath * bp;
1265
1266         g_return_val_if_fail (bpath != NULL, FALSE);
1267
1268         for (bp = bpath; bp->code != ART_END; bp++)
1269                 if (bp->code == ART_MOVETO_OPEN) return FALSE;
1270
1271         return TRUE;
1272 }
1273
1274 static gboolean
1275 sp_bpath_all_open (const ArtBpath * bpath)
1276 {
1277         const ArtBpath * bp;
1278
1279         g_return_val_if_fail (bpath != NULL, FALSE);
1280
1281         for (bp = bpath; bp->code != ART_END; bp++)
1282                 if (bp->code == ART_MOVETO) return FALSE;
1283
1284         return TRUE;
1285 }
1286
1287