Code

bulk whitespace removal patch #1198588 by gigaclon
[inkscape.git] / src / display / curve.cpp
1 #define __CURVE_C__
3 /** \file
4  * Routines for SPCurve and for NArtBpath arrays in general.
5  */
7 /*
8  * Author:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *
11  * Copyright (C) 2000 Lauris Kaplinski
12  * Copyright (C) 2000-2001 Ximian, Inc.
13  * Copyright (C) 2002 Lauris Kaplinski
14  *
15  * Released under GNU GPL
16  */
19 #include <display/curve.h>
20 #include <libnr/n-art-bpath.h>
21 #include <libnr/nr-point-matrix-ops.h>
22 #include <libnr/nr-translate-ops.h>
24 #define SP_CURVE_LENSTEP 32
26 static bool sp_bpath_good(NArtBpath const bpath[]);
27 static NArtBpath *sp_bpath_clean(NArtBpath const bpath[]);
28 static NArtBpath const *sp_bpath_check_subpath(NArtBpath const bpath[]);
29 static unsigned sp_bpath_length(NArtBpath const bpath[]);
30 static bool sp_bpath_closed(NArtBpath const bpath[]);
32 /* Constructors */
34 /**
35  * The returned curve's state is as if sp_curve_reset has just been called on it.
36  */
37 SPCurve *
38 sp_curve_new()
39 {
40     return sp_curve_new_sized(SP_CURVE_LENSTEP);
41 }
43 /**
44  * Like sp_curve_new, but overriding the default initial capacity.
45  *
46  * The returned curve's state is as if sp_curve_reset has just been called on it.
47  *
48  * \param length Initial number of NArtBpath elements allocated for bpath (including NR_END
49  *    element).
50  */
51 SPCurve *
52 sp_curve_new_sized(gint length)
53 {
54     g_return_val_if_fail(length > 0, NULL);
56     SPCurve *curve = g_new(SPCurve, 1);
58     curve->refcount = 1;
59     curve->bpath = nr_new(NArtBpath, length);
60     curve->bpath->code = NR_END;
61     curve->end = 0;
62     curve->length = length;
63     curve->substart = 0;
64     curve->sbpath = false;
65     curve->hascpt = false;
66     curve->posSet = false;
67     curve->moving = false;
68     curve->closed = false;
70     return curve;
71 }
73 /**
74  * Convert NArtBpath object to SPCurve object.
75  *
76  * \return new SPCurve, or NULL if the curve was not created for some reason.
77  */
78 SPCurve *
79 sp_curve_new_from_bpath(NArtBpath *bpath)
80 {
81     g_return_val_if_fail(bpath != NULL, NULL);
83     if (!sp_bpath_good(bpath)) {
84         NArtBpath *new_bpath = sp_bpath_clean(bpath);
85         if (new_bpath == NULL) {
86             return NULL;
87         }
88         nr_free(bpath);
89         bpath = new_bpath;
90     }
92     SPCurve *curve = g_new(SPCurve, 1);
94     curve->refcount = 1;
95     curve->bpath = bpath;
96     curve->length = sp_bpath_length(bpath);
97     curve->end = curve->length - 1;
98     gint i = curve->end;
99     for (; i > 0; i--)
100         if ((curve->bpath[i].code == NR_MOVETO) ||
101             (curve->bpath[i].code == NR_MOVETO_OPEN))
102             break;
103     curve->substart = i;
104     curve->sbpath = false;
105     curve->hascpt = false;
106     curve->posSet = false;
107     curve->moving = false;
108     curve->closed = sp_bpath_closed(bpath);
110     return curve;
113 /** 
114  * Construct an SPCurve from read-only, static storage.
115  *
116  *  We could treat read-onliness and staticness (i.e. can't call free on bpath) as orthogonal
117  *  attributes, but at the time of writing we have only one caller.
118  */
119 SPCurve *
120 sp_curve_new_from_static_bpath(NArtBpath const *bpath)
122     g_return_val_if_fail(bpath != NULL, NULL);
124     bool sbpath;
125     if (!sp_bpath_good(bpath)) {
126         NArtBpath *new_bpath = sp_bpath_clean(bpath);
127         g_return_val_if_fail(new_bpath != NULL, NULL);
128         sbpath = false;
129         bpath = new_bpath;
130     } else {
131         sbpath = true;
132     }
134     SPCurve *curve = g_new(SPCurve, 1);
136     curve->refcount = 1;
137     curve->bpath = const_cast<NArtBpath *>(bpath);
138     curve->length = sp_bpath_length(bpath);
139     curve->end = curve->length - 1;
140     gint i = curve->end;
141     for (; i > 0; i--)
142         if ((curve->bpath[i].code == NR_MOVETO) ||
143             (curve->bpath[i].code == NR_MOVETO_OPEN))
144             break;
145     curve->substart = i;
146     curve->sbpath = sbpath;
147     curve->hascpt = false;
148     curve->posSet = false;
149     curve->moving = false;
150     curve->closed = sp_bpath_closed(bpath);
152     return curve;
155 /**
156  * Convert const NArtBpath array to SPCurve.
157  *
158  * \return new SPCurve, or NULL if the curve was not created for some reason.
159  */
160 SPCurve *sp_curve_new_from_foreign_bpath(NArtBpath const bpath[])
162     g_return_val_if_fail(bpath != NULL, NULL);
164     NArtBpath *new_bpath;
165     if (!sp_bpath_good(bpath)) {
166         new_bpath = sp_bpath_clean(bpath);
167         g_return_val_if_fail(new_bpath != NULL, NULL);
168     } else {
169         unsigned const len = sp_bpath_length(bpath);
170         new_bpath = nr_new(NArtBpath, len);
171         memcpy(new_bpath, bpath, len * sizeof(NArtBpath));
172     }
174     SPCurve *curve = sp_curve_new_from_bpath(new_bpath);
176     if (!curve)
177         nr_free(new_bpath);
179     return curve;
182 /**
183  * Increase refcount of curve.
184  *
185  * \todo should this be shared with other refcounting code?
186  */
187 SPCurve *
188 sp_curve_ref(SPCurve *curve)
190     g_return_val_if_fail(curve != NULL, NULL);
192     curve->refcount += 1;
194     return curve;
197 /**
198  * Decrease refcount of curve, with possible destruction.
199  * 
200  * \todo should this be shared with other refcounting code?
201  */
202 SPCurve *
203 sp_curve_unref(SPCurve *curve)
205     g_return_val_if_fail(curve != NULL, NULL);
207     curve->refcount -= 1;
209     if (curve->refcount < 1) {
210         if ((!curve->sbpath) && (curve->bpath)) {
211             nr_free(curve->bpath);
212         }
213         g_free(curve);
214     }
216     return NULL;
219 /**
220  * Add space for more paths in curve.
221  */
222 static void
223 sp_curve_ensure_space(SPCurve *curve, gint space)
225     g_return_if_fail(curve != NULL);
226     g_return_if_fail(space > 0);
228     if (curve->end + space < curve->length)
229         return;
231     if (space < SP_CURVE_LENSTEP)
232         space = SP_CURVE_LENSTEP;
234     curve->bpath = nr_renew(curve->bpath, NArtBpath, curve->length + space);
236     curve->length += space;
239 /**
240  * Create new curve from its own bpath array.
241  */
242 SPCurve *
243 sp_curve_copy(SPCurve *curve)
245     g_return_val_if_fail(curve != NULL, NULL);
247     return sp_curve_new_from_foreign_bpath(curve->bpath);
250 /**
251  * Return new curve that is the concatenation of all curves in list.
252  */
253 SPCurve *
254 sp_curve_concat(GSList const *list)
256     g_return_val_if_fail(list != NULL, NULL);
258     gint length = 0;
260     for (GSList const *l = list; l != NULL; l = l->next) {
261         SPCurve *c = (SPCurve *) l->data;
262         length += c->end;
263     }
265     SPCurve *new_curve = sp_curve_new_sized(length + 1);
267     NArtBpath *bp = new_curve->bpath;
269     for (GSList const *l = list; l != NULL; l = l->next) {
270         SPCurve *c = (SPCurve *) l->data;
271         memcpy(bp, c->bpath, c->end * sizeof(NArtBpath));
272         bp += c->end;
273     }
275     bp->code = NR_END;
277     new_curve->end = length;
278     gint i;
279     for (i = new_curve->end; i > 0; i--) {
280         if ((new_curve->bpath[i].code == NR_MOVETO)     ||
281             (new_curve->bpath[i].code == NR_MOVETO_OPEN)  )
282             break;
283     }
285     new_curve->substart = i;
287     return new_curve;
290 /** 
291  * Returns a list of new curves corresponding to the subpaths in \a curve.
292  */
293 GSList *
294 sp_curve_split(SPCurve const *curve)
296     g_return_val_if_fail(curve != NULL, NULL);
298     gint p = 0;
299     GSList *l = NULL;
301     while (p < curve->end) {
302         gint i = 1;
303         while ((curve->bpath[p + i].code == NR_LINETO) ||
304                (curve->bpath[p + i].code == NR_CURVETO))
305             i++;
306         SPCurve *new_curve = sp_curve_new_sized(i + 1);
307         memcpy(new_curve->bpath, curve->bpath + p, i * sizeof(NArtBpath));
308         new_curve->end = i;
309         new_curve->bpath[i].code = NR_END;
310         new_curve->substart = 0;
311         new_curve->closed = (new_curve->bpath->code == NR_MOVETO);
312         new_curve->hascpt = (new_curve->bpath->code == NR_MOVETO_OPEN);
313         l = g_slist_append(l, new_curve);
314         /** \todo
315          * effic: Use g_slist_prepend instead.  Either work backwards from 
316          * the end of curve, or work forwards as at present but do
317          * g_slist_reverse before returning.
318          */
319         p += i;
320     }
322     return l;
325 /**
326  * Transform all paths in curve, template helper.
327  */
328 template<class M>
329 static void
330 tmpl_curve_transform(SPCurve *const curve, M const &m)
332     g_return_if_fail(curve != NULL);
333     g_return_if_fail(!curve->sbpath);
335     for (gint i = 0; i < curve->end; i++) {
336         NArtBpath *p = curve->bpath + i;
337         switch (p->code) {
338             case NR_MOVETO:
339             case NR_MOVETO_OPEN:
340             case NR_LINETO: {
341                 p->setC(3, p->c(3) * m);
342                 break;
343             }
344             case NR_CURVETO:
345                 for (unsigned i = 1; i <= 3; ++i) {
346                     p->setC(i, p->c(i) * m);
347                 }
348                 break;
349             default:
350                 g_warning("Illegal pathcode %d", p->code);
351                 break;
352         }
353     }
356 /**
357  * Transform all paths in curve using matrix.
358  */
359 void
360 sp_curve_transform(SPCurve *const curve, NR::Matrix const &m)
362     tmpl_curve_transform<NR::Matrix>(curve, m);
365 /**
366  * Transform all paths in curve using NR::translate.
367  */
368 void
369 sp_curve_transform(SPCurve *const curve, NR::translate const &m)
371     tmpl_curve_transform<NR::translate>(curve, m);
375 /* Methods */
377 /**
378  * Set curve to empty curve.
379  */
380 void
381 sp_curve_reset(SPCurve *curve)
383     g_return_if_fail(curve != NULL);
384     g_return_if_fail(!curve->sbpath);
386     curve->bpath->code = NR_END;
387     curve->end = 0;
388     curve->substart = 0;
389     curve->hascpt = false;
390     curve->posSet = false;
391     curve->moving = false;
392     curve->closed = false;
395 /* Several consecutive movetos are ALLOWED */
397 /**
398  * Calls sp_curve_moveto() with point made of given coordinates.
399  */
400 void
401 sp_curve_moveto(SPCurve *curve, gdouble x, gdouble y)
403     sp_curve_moveto(curve, NR::Point(x, y));
406 /**
407  * Perform a moveto to a point, thus starting a new subpath.
408  */
409 void
410 sp_curve_moveto(SPCurve *curve, NR::Point const &p)
412     g_return_if_fail(curve != NULL);
413     g_return_if_fail(!curve->sbpath);
414     g_return_if_fail(!curve->moving);
416     curve->substart = curve->end;
417     curve->hascpt = true;
418     curve->posSet = true;
419     curve->movePos = p;
422 /**
423  * Calls sp_curve_lineto() with a point's coordinates.
424  */
425 void
426 sp_curve_lineto(SPCurve *curve, NR::Point const &p)
428     sp_curve_lineto(curve, p[NR::X], p[NR::Y]);
431 /**
432  * Adds a line to the current subpath.
433  */
434 void
435 sp_curve_lineto(SPCurve *curve, gdouble x, gdouble y)
437     g_return_if_fail(curve != NULL);
438     g_return_if_fail(!curve->sbpath);
439     g_return_if_fail(curve->hascpt);
441     if (curve->moving) {
442         /* fix endpoint */
443         g_return_if_fail(!curve->posSet);
444         g_return_if_fail(curve->end > 1);
445         NArtBpath *bp = curve->bpath + curve->end - 1;
446         g_return_if_fail(bp->code == NR_LINETO);
447         bp->x3 = x;
448         bp->y3 = y;
449         curve->moving = false;
450         return;
451     }
453     if (curve->posSet) {
454         /* start a new segment */
455         sp_curve_ensure_space(curve, 2);
456         NArtBpath *bp = curve->bpath + curve->end;
457         bp->code = NR_MOVETO_OPEN;
458         bp->setC(3, curve->movePos);
459         bp++;
460         bp->code = NR_LINETO;
461         bp->x3 = x;
462         bp->y3 = y;
463         bp++;
464         bp->code = NR_END;
465         curve->end += 2;
466         curve->posSet = false;
467         curve->closed = false;
468         return;
469     }
471     /* add line */
473     g_return_if_fail(curve->end > 1);
474     sp_curve_ensure_space(curve, 1);
475     NArtBpath *bp = curve->bpath + curve->end;
476     bp->code = NR_LINETO;
477     bp->x3 = x;
478     bp->y3 = y;
479     bp++;
480     bp->code = NR_END;
481     curve->end++;
484 /// Unused
485 void
486 sp_curve_lineto_moving(SPCurve *curve, gdouble x, gdouble y)
488     g_return_if_fail(curve != NULL);
489     g_return_if_fail(!curve->sbpath);
490     g_return_if_fail(curve->hascpt);
492     if (curve->moving) {
493         /* change endpoint */
494         g_return_if_fail(!curve->posSet);
495         g_return_if_fail(curve->end > 1);
496         NArtBpath *bp = curve->bpath + curve->end - 1;
497         g_return_if_fail(bp->code == NR_LINETO);
498         bp->x3 = x;
499         bp->y3 = y;
500         return;
501     }
503     if (curve->posSet) {
504         /* start a new segment */
505         sp_curve_ensure_space(curve, 2);
506         NArtBpath *bp = curve->bpath + curve->end;
507         bp->code = NR_MOVETO_OPEN;
508         bp->setC(3, curve->movePos);
509         bp++;
510         bp->code = NR_LINETO;
511         bp->x3 = x;
512         bp->y3 = y;
513         bp++;
514         bp->code = NR_END;
515         curve->end += 2;
516         curve->posSet = false;
517         curve->moving = true;
518         curve->closed = false;
519         return;
520     }
522     /* add line */
524     g_return_if_fail(curve->end > 1);
525     sp_curve_ensure_space(curve, 1);
526     NArtBpath *bp = curve->bpath + curve->end;
527     bp->code = NR_LINETO;
528     bp->x3 = x;
529     bp->y3 = y;
530     bp++;
531     bp->code = NR_END;
532     curve->end++;
533     curve->moving = true;
536 /**
537  * Calls sp_curve_curveto() with coordinates of three points.
538  */
539 void
540 sp_curve_curveto(SPCurve *curve, NR::Point const &p0, NR::Point const &p1, NR::Point const &p2)
542     using NR::X;
543     using NR::Y;
544     sp_curve_curveto(curve,
545                      p0[X], p0[Y],
546                      p1[X], p1[Y],
547                      p2[X], p2[Y]);
550 /**
551  * Adds a bezier segment to the current subpath.
552  */
553 void
554 sp_curve_curveto(SPCurve *curve, gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
556     g_return_if_fail(curve != NULL);
557     g_return_if_fail(!curve->sbpath);
558     g_return_if_fail(curve->hascpt);
559     g_return_if_fail(!curve->moving);
561     if (curve->posSet) {
562         /* start a new segment */
563         sp_curve_ensure_space(curve, 2);
564         NArtBpath *bp = curve->bpath + curve->end;
565         bp->code = NR_MOVETO_OPEN;
566         bp->setC(3, curve->movePos);
567         bp++;
568         bp->code = NR_CURVETO;
569         bp->x1 = x0;
570         bp->y1 = y0;
571         bp->x2 = x1;
572         bp->y2 = y1;
573         bp->x3 = x2;
574         bp->y3 = y2;
575         bp++;
576         bp->code = NR_END;
577         curve->end += 2;
578         curve->posSet = false;
579         curve->closed = false;
580         return;
581     }
583     /* add curve */
585     g_return_if_fail(curve->end > 1);
586     sp_curve_ensure_space(curve, 1);
587     NArtBpath *bp = curve->bpath + curve->end;
588     bp->code = NR_CURVETO;
589     bp->x1 = x0;
590     bp->y1 = y0;
591     bp->x2 = x1;
592     bp->y2 = y1;
593     bp->x3 = x2;
594     bp->y3 = y2;
595     bp++;
596     bp->code = NR_END;
597     curve->end++;
600 /**
601  * Close current subpath by possibly adding a line between start and end.
602  */
603 void
604 sp_curve_closepath(SPCurve *curve)
606     g_return_if_fail(curve != NULL);
607     g_return_if_fail(!curve->sbpath);
608     g_return_if_fail(curve->hascpt);
609     g_return_if_fail(!curve->posSet);
610     g_return_if_fail(!curve->moving);
611     g_return_if_fail(!curve->closed);
612     /* We need at least moveto, curveto, end. */
613     g_return_if_fail(curve->end - curve->substart > 1);
615     {
616         NArtBpath *bs = curve->bpath + curve->substart;
617         NArtBpath *be = curve->bpath + curve->end - 1;
619         if (bs->c(3) != be->c(3)) {
620             sp_curve_lineto(curve, bs->c(3));
621             bs = curve->bpath + curve->substart;
622         }
624         bs->code = NR_MOVETO;
625     }
626     curve->closed = true;
628     for (NArtBpath const *bp = curve->bpath; bp->code != NR_END; bp++) {
629         /** \todo 
630          * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of 
631          * the closed boolean).
632          */
633         if (bp->code == NR_MOVETO_OPEN) {
634             curve->closed = false;
635             break;
636         }
637     }
639     curve->hascpt = false;
642 /** Like sp_curve_closepath() but sets the end point of the current
643     command to the subpath start point instead of adding a new lineto.
645     Used for freehand drawing when the user draws back to the start point.
646 **/
647 void
648 sp_curve_closepath_current(SPCurve *curve)
650     g_return_if_fail(curve != NULL);
651     g_return_if_fail(!curve->sbpath);
652     g_return_if_fail(curve->hascpt);
653     g_return_if_fail(!curve->posSet);
654     g_return_if_fail(!curve->closed);
655     /* We need at least moveto, curveto, end. */
656     g_return_if_fail(curve->end - curve->substart > 1);
658     {
659         NArtBpath *bs = curve->bpath + curve->substart;
660         NArtBpath *be = curve->bpath + curve->end - 1;
662         be->x3 = bs->x3;
663         be->y3 = bs->y3;
665         bs->code = NR_MOVETO;
666     }
667     curve->closed = true;
669     for (NArtBpath const *bp = curve->bpath; bp->code != NR_END; bp++) {
670         /** \todo 
671          * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of 
672          * the closed boolean).
673          */
674         if (bp->code == NR_MOVETO_OPEN) {
675             curve->closed = false;
676             break;
677         }
678     }
680     curve->hascpt = false;
681     curve->moving = false;
684 /**
685  * True if no paths are in curve.
686  */
687 bool
688 sp_curve_empty(SPCurve *curve)
690     g_return_val_if_fail(curve != NULL, TRUE);
692     return (curve->bpath->code == NR_END);
695 /**
696  * Return last subpath or NULL.
697  */
698 NArtBpath *
699 sp_curve_last_bpath(SPCurve const *curve)
701     g_return_val_if_fail(curve != NULL, NULL);
703     if (curve->end == 0) {
704         return NULL;
705     }
707     return curve->bpath + curve->end - 1;
710 /**
711  * Return first subpath or NULL.
712  */
713 NArtBpath *
714 sp_curve_first_bpath(SPCurve const *curve)
716     g_return_val_if_fail(curve != NULL, NULL);
718     if (curve->end == 0) {
719         return NULL;
720     }
722     return curve->bpath;
725 /**
726  * Return first point of first subpath or (0,0).
727  */
728 NR::Point
729 sp_curve_first_point(SPCurve const *const curve)
731     NArtBpath *const bpath = sp_curve_first_bpath(curve);
732     g_return_val_if_fail(bpath != NULL, NR::Point(0, 0));
733     return bpath->c(3);
736 /**
737  * Return the second point of first subpath or curve->movePos if curve too short.
738  */
739 NR::Point
740 sp_curve_second_point(SPCurve const *const curve)
742     g_return_val_if_fail(curve != NULL, NR::Point(0, 0));
744     if (curve->end < 1) {
745         return curve->movePos;
746     }
748     NArtBpath *bpath = NULL;
749     if (curve->end < 2) {
750         bpath = curve->bpath;
751     } else {
752         bpath = curve->bpath + 1;
753     }
754     g_return_val_if_fail(bpath != NULL, NR::Point(0, 0));
755     return bpath->c(3);
758 /**
759  * Return the second-last point of last subpath or curve->movePos if curve too short.
760  */
761 NR::Point
762 sp_curve_penultimate_point(SPCurve const *const curve)
764     g_return_val_if_fail(curve != NULL, NR::Point(0, 0));
766     if (curve->end < 2) {
767         return curve->movePos;
768     }
770     NArtBpath *const bpath = curve->bpath + curve->end - 2;
771     g_return_val_if_fail(bpath != NULL, NR::Point(0, 0));
772     return bpath->c(3);
775 /**
776  * Return last point of last subpath or (0,0).
777  */
778 NR::Point
779 sp_curve_last_point(SPCurve const *const curve)
781     NArtBpath *const bpath = sp_curve_last_bpath(curve);
782     g_return_val_if_fail(bpath != NULL, NR::Point(0, 0));
783     return bpath->c(3);
786 inline static bool
787 is_moveto(NRPathcode const c)
789     return c == NR_MOVETO || c == NR_MOVETO_OPEN;
792 /** 
793  * Returns \a curve but drawn in the opposite direction.  
794  * Should result in the same shape, but
795  * with all its markers drawn facing the other direction.
796  **/
797 SPCurve *
798 sp_curve_reverse(SPCurve const *curve)
800     /* We need at least moveto, curveto, end. */
801     g_return_val_if_fail(curve->end - curve->substart > 1, NULL);
803     NArtBpath const *be = curve->bpath + curve->end - 1;
805     g_assert(is_moveto(curve->bpath[curve->substart].code));
806     g_assert(is_moveto(curve->bpath[0].code));
807     g_assert((be+1)->code == NR_END);
809     SPCurve  *new_curve = sp_curve_new_sized(curve->length);
810     sp_curve_moveto(new_curve, be->c(3));
812     for (NArtBpath const *bp = be; ; --bp) {
813         switch (bp->code) {
814             case NR_MOVETO:
815                 g_assert(new_curve->bpath[new_curve->substart].code == NR_MOVETO_OPEN);
816                 new_curve->bpath[new_curve->substart].code = NR_MOVETO;
817                 /* FALL-THROUGH */
818             case NR_MOVETO_OPEN:
819                 if (bp == curve->bpath) {
820                     return new_curve;
821                 }
822                 sp_curve_moveto(new_curve, (bp-1)->c(3));
823                 break;
825             case NR_LINETO:
826                 sp_curve_lineto(new_curve, (bp-1)->c(3));
827                 break;
829             case NR_CURVETO:
830                 sp_curve_curveto(new_curve, bp->c(2), bp->c(1), (bp-1)->c(3));
831                 break;
833             default:
834                 g_assert_not_reached();
835         }
836     }
839 /**
840  * Append \a curve2 to \a curve.
841  */
842 void
843 sp_curve_append(SPCurve *curve,
844                 SPCurve const *curve2,
845                 bool use_lineto)
847     g_return_if_fail(curve != NULL);
848     g_return_if_fail(curve2 != NULL);
850     if (curve2->end < 1)
851         return;
853     NArtBpath const *bs = curve2->bpath;
855     bool closed = curve->closed;
857     for (NArtBpath const *bp = bs; bp->code != NR_END; bp++) {
858         switch (bp->code) {
859             case NR_MOVETO_OPEN:
860                 if (use_lineto && curve->hascpt) {
861                     sp_curve_lineto(curve, bp->x3, bp->y3);
862                     use_lineto = FALSE;
863                 } else {
864                     if (closed) sp_curve_closepath(curve);
865                     sp_curve_moveto(curve, bp->x3, bp->y3);
866                 }
867                 closed = false;
868                 break;
870             case NR_MOVETO:
871                 if (use_lineto && curve->hascpt) {
872                     sp_curve_lineto(curve, bp->x3, bp->y3);
873                     use_lineto = FALSE;
874                 } else {
875                     if (closed) sp_curve_closepath(curve);
876                     sp_curve_moveto(curve, bp->x3, bp->y3);
877                 }
878                 closed = true;
879                 break;
881             case NR_LINETO:
882                 sp_curve_lineto(curve, bp->x3, bp->y3);
883                 break;
885             case NR_CURVETO:
886                 sp_curve_curveto(curve, bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
887                 break;
889             case NR_END:
890                 g_assert_not_reached();
891         }
892     }
894     if (closed) {
895         sp_curve_closepath(curve);
896     }
899 /**
900  * Append \a c1 to \a c0 with possible fusing of close endpoints.
901  */
902 SPCurve *
903 sp_curve_append_continuous(SPCurve *c0, SPCurve const *c1, gdouble tolerance)
905     g_return_val_if_fail(c0 != NULL, NULL);
906     g_return_val_if_fail(c1 != NULL, NULL);
907     g_return_val_if_fail(!c0->closed, NULL);
908     g_return_val_if_fail(!c1->closed, NULL);
910     if (c1->end < 1) {
911         return c0;
912     }
914     NArtBpath *be = sp_curve_last_bpath(c0);
915     if (be) {
916         NArtBpath const *bs = sp_curve_first_bpath(c1);
917         if ( bs
918              && ( fabs( bs->x3 - be->x3 ) <= tolerance )
919              && ( fabs( bs->y3 - be->y3 ) <= tolerance ) )
920         {
921             /** \todo
922              * fixme: Strictly we mess in case of multisegment mixed 
923              * open/close curves 
924              */
925             bool closed = false;
926             for (bs = bs + 1; bs->code != NR_END; bs++) {
927                 switch (bs->code) {
928                     case NR_MOVETO_OPEN:
929                         if (closed) sp_curve_closepath(c0);
930                         sp_curve_moveto(c0, bs->x3, bs->y3);
931                         closed = false;
932                         break;
933                     case NR_MOVETO:
934                         if (closed) sp_curve_closepath(c0);
935                         sp_curve_moveto(c0, bs->x3, bs->y3);
936                         closed = true;
937                         break;
938                     case NR_LINETO:
939                         sp_curve_lineto(c0, bs->x3, bs->y3);
940                         break;
941                     case NR_CURVETO:
942                         sp_curve_curveto(c0, bs->x1, bs->y1, bs->x2, bs->y2, bs->x3, bs->y3);
943                         break;
944                     case NR_END:
945                         g_assert_not_reached();
946                 }
947             }
948         } else {
949             sp_curve_append(c0, c1, TRUE);
950         }
951     } else {
952         sp_curve_append(c0, c1, TRUE);
953     }
955     return c0;
958 /**
959  * Remove last segment of curve.
960  */
961 void
962 sp_curve_backspace(SPCurve *curve)
964     g_return_if_fail(curve != NULL);
966     if (curve->end > 0) {
967         curve->end -= 1;
968         if (curve->end > 0) {
969             NArtBpath *bp = curve->bpath + curve->end - 1;
970             if ((bp->code == NR_MOVETO)     ||
971                 (bp->code == NR_MOVETO_OPEN)  )
972             {
973                 curve->hascpt = true;
974                 curve->posSet = true;
975                 curve->closed = false;
976                 curve->movePos = bp->c(3);
977                 curve->end -= 1;
978             }
979         }
980         curve->bpath[curve->end].code = NR_END;
981     }
984 /* Private methods */
986 /**
987  * True if all subpaths in bpath array pass consistency check.
988  */
989 static bool sp_bpath_good(NArtBpath const bpath[])
991     g_return_val_if_fail(bpath != NULL, FALSE);
993     NArtBpath const *bp = bpath;
994     while (bp->code != NR_END) {
995         bp = sp_bpath_check_subpath(bp);
996         if (bp == NULL)
997             return false;
998     }
1000     return true;
1003 /**
1004  * Return copy of a bpath array, discarding any inconsistencies.
1005  */
1006 static NArtBpath *sp_bpath_clean(NArtBpath const bpath[])
1008     NArtBpath *new_bpath = nr_new(NArtBpath, sp_bpath_length(bpath));
1010     NArtBpath const *bp = bpath;
1011     NArtBpath *np = new_bpath;
1013     while (bp->code != NR_END) {
1014         if (sp_bpath_check_subpath(bp)) {
1015             *np++ = *bp++;
1016             while ((bp->code == NR_LINETO) ||
1017                    (bp->code == NR_CURVETO))
1018                 *np++ = *bp++;
1019         } else {
1020             bp++;
1021             while ((bp->code == NR_LINETO) ||
1022                    (bp->code == NR_CURVETO))
1023                 bp++;
1024         }
1025     }
1027     if (np == new_bpath) {
1028         nr_free(new_bpath);
1029         return NULL;
1030     }
1032     np->code = NR_END;
1033     np += 1;
1035     new_bpath = nr_renew(new_bpath, NArtBpath, np - new_bpath);
1037     return new_bpath;
1040 /**
1041  * Perform consistency check of bpath array.
1042  * \return Address of NR_END node or NULL.
1043  */
1044 static NArtBpath const *sp_bpath_check_subpath(NArtBpath const bpath[])
1046     g_return_val_if_fail(bpath != NULL, NULL);
1048     bool closed;
1049     if (bpath->code == NR_MOVETO) {
1050         closed = true;
1051     } else if (bpath->code == NR_MOVETO_OPEN) {
1052         closed = false;
1053     } else {
1054         return NULL;
1055     }
1057     gint len = 0;
1058     gint i;
1059     /** \todo
1060      * effic: consider checking for END/MOVE/MOVETO inside switch block
1061      */
1062     for (i = 1; (bpath[i].code != NR_END) && (bpath[i].code != NR_MOVETO) && (bpath[i].code != NR_MOVETO_OPEN); i++) {
1063         switch (bpath[i].code) {
1064             case NR_LINETO:
1065             case NR_CURVETO:
1066                 len++;
1067                 break;
1068             default:
1069                 return NULL;
1070         }
1071     }
1073     if (closed) {
1074         if (len < 1)
1075             return NULL;
1077         if ((bpath->x3 != bpath[i-1].x3) || (bpath->y3 != bpath[i-1].y3))
1078             return NULL;
1079     } else {
1080         if (len < 1)
1081             return NULL;
1082     }
1084     return bpath + i;
1087 /**
1088  * Returns index of first NR_END bpath in array.
1089  */
1090 static unsigned sp_bpath_length(NArtBpath const bpath[])
1092     g_return_val_if_fail(bpath != NULL, FALSE);
1094     unsigned ret = 0;
1095     while ( bpath[ret].code != NR_END ) {
1096         ++ret;
1097     }
1098     ++ret;
1100     return ret;
1103 /**
1104  * \brief
1105  *
1106  * \todo
1107  * fixme: this is bogus -- it doesn't check for nr_moveto, which will indicate 
1108  * a closing of the subpath it's nonsense to talk about a path as a whole 
1109  * being closed, although maybe someone would want that for some other reason?  
1110  * Oh, also, if the bpath just ends, then it's *open*.  I hope nobody is using 
1111  * this code for anything.
1112  */
1113 static bool sp_bpath_closed(NArtBpath const bpath[])
1115     g_return_val_if_fail(bpath != NULL, FALSE);
1117     for (NArtBpath const *bp = bpath; bp->code != NR_END; bp++) {
1118         if (bp->code == NR_MOVETO_OPEN) {
1119             return false;
1120         }
1121     }
1123     return true;
1126 /**
1127  * Returns length of bezier segment.
1128  */
1129 static double
1130 bezier_len(NR::Point const &c0,
1131            NR::Point const &c1,
1132            NR::Point const &c2,
1133            NR::Point const &c3,
1134            double const threshold)
1136     /** \todo
1137      * The SVG spec claims that a closed form exists, but for the moment I'll 
1138      * use a stupid algorithm.
1139      */
1140     double const lbound = L2( c3 - c0 );
1141     double const ubound = L2( c1 - c0 ) + L2( c2 - c1 ) + L2( c3 - c2 );
1142     double ret;
1143     if ( ubound - lbound <= threshold ) {
1144         ret = .5 * ( lbound + ubound );
1145     } else {
1146         NR::Point const a1( .5 * ( c0 + c1 ) );
1147         NR::Point const b2( .5 * ( c2 + c3 ) );
1148         NR::Point const c12( .5 * ( c1 + c2 ) );
1149         NR::Point const a2( .5 * ( a1 + c12 ) );
1150         NR::Point const b1( .5 * ( c12 + b2 ) );
1151         NR::Point const midpoint( .5 * ( a2 + b1 ) );
1152         double const rec_threshold = .625 * threshold;
1153         ret = bezier_len(c0, a1, a2, midpoint, rec_threshold) + bezier_len(midpoint, b1, b2, c3, rec_threshold);
1154         if (!(lbound - 1e-2 <= ret && ret <= ubound + 1e-2)) {
1155             using NR::X; using NR::Y;
1156             g_warning("ret=%f outside of expected bounds [%f, %f] for {(%.0f %.0f) (%.0f %.0f) (%.0f %.0f) (%.0f %.0f)}",
1157                       ret, lbound, ubound, c0[X], c0[Y], c1[X], c1[Y], c2[X], c2[Y], c3[X], c3[Y]);
1158         }
1159     }
1160     return ret;
1163 /**
1164  * Returns total length of curve, excluding length of closepath segments.
1165  */
1166 static double
1167 sp_curve_distance_including_space(SPCurve const *const curve, double seg2len[])
1169     g_return_val_if_fail(curve != NULL, 0.);
1171     double ret = 0.0;
1173     if ( curve->bpath->code == NR_END ) {
1174         return ret;
1175     }
1177     NR::Point prev(curve->bpath->c(3));
1178     for (gint i = 1; i < curve->end; ++i) {
1179         NArtBpath &p = curve->bpath[i];
1180         double seg_len = 0;
1181         switch (p.code) {
1182             case NR_MOVETO_OPEN:
1183             case NR_MOVETO:
1184             case NR_LINETO:
1185                 seg_len = L2(p.c(3) - prev);
1186                 break;
1188             case NR_CURVETO:
1189                 seg_len = bezier_len(prev, p.c(1), p.c(2), p.c(3), 1.);
1190                 break;
1192             case NR_END:
1193                 return ret;
1194         }
1195         seg2len[i - 1] = seg_len;
1196         ret += seg_len;
1197         prev = p.c(3);
1198     }
1199     g_assert(!(ret < 0));
1200     return ret;
1203 /** 
1204  * Like sp_curve_distance_including_space(), but ensures that the 
1205  * result >= 1e-18:  uses 1 per segment if necessary.
1206  */
1207 static double
1208 sp_curve_nonzero_distance_including_space(SPCurve const *const curve, double seg2len[])
1210     double const real_dist(sp_curve_distance_including_space(curve, seg2len));
1211     if (real_dist >= 1e-18) {
1212         return real_dist;
1213     } else {
1214         unsigned const nSegs = SP_CURVE_LENGTH(curve) - 1;
1215         for (unsigned i = 0; i < nSegs; ++i) {
1216             seg2len[i] = 1.;
1217         }
1218         return (double) nSegs;
1219     }
1222 void
1223 sp_curve_stretch_endpoints(SPCurve *curve, NR::Point const &new_p0, NR::Point const &new_p1)
1225     if (sp_curve_empty(curve)) {
1226         return;
1227     }
1228     g_assert(unsigned(SP_CURVE_LENGTH(curve)) + 1 == sp_bpath_length(curve->bpath));
1229     unsigned const nSegs = SP_CURVE_LENGTH(curve) - 1;
1230     g_assert(nSegs != 0);
1231     double *const seg2len = new double[nSegs];
1232     double const tot_len = sp_curve_nonzero_distance_including_space(curve, seg2len);
1233     NR::Point const offset0( new_p0 - sp_curve_first_point(curve) );
1234     NR::Point const offset1( new_p1 - sp_curve_last_point(curve) );
1235     curve->bpath->setC(3, new_p0);
1236     double begin_dist = 0.;
1237     for (unsigned si = 0; si < nSegs; ++si) {
1238         double const end_dist = begin_dist + seg2len[si];
1239         NArtBpath &p = curve->bpath[1 + si];
1240         switch (p.code) {
1241             case NR_LINETO:
1242             case NR_MOVETO:
1243             case NR_MOVETO_OPEN:
1244                 p.setC(3, p.c(3) + NR::Lerp(end_dist / tot_len, offset0, offset1));
1245                 break;
1247             case NR_CURVETO:
1248                 for (unsigned ci = 1; ci <= 3; ++ci) {
1249                     p.setC(ci, p.c(ci) + Lerp((begin_dist + ci * seg2len[si] / 3.) / tot_len, offset0, offset1));
1250                 }
1251                 break;
1253             default:
1254                 g_assert_not_reached();
1255         }
1257         begin_dist = end_dist;
1258     }
1259     g_assert(L1(curve->bpath[nSegs].c(3) - new_p1) < 1.);
1260     /* Explicit set for better numerical properties. */
1261     curve->bpath[nSegs].setC(3, new_p1);
1262     delete [] seg2len;
1265 void
1266 sp_curve_move_endpoints(SPCurve *curve, NR::Point const &new_p0,
1267         NR::Point const &new_p1)
1269     if (sp_curve_empty(curve)) {
1270         return;
1271     }
1272     unsigned const nSegs = SP_CURVE_LENGTH(curve) - 1;
1273     g_assert(nSegs != 0);
1275     curve->bpath->setC(3, new_p0);
1276     curve->bpath[nSegs].setC(3, new_p1);
1280 /*
1281   Local Variables:
1282   mode:c++
1283   c-file-style:"stroustrup"
1284   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1285   indent-tabs-mode:nil
1286   fill-column:99
1287   End:
1288 */
1289 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :