Code

remove left-overs from making SPCurve a proper class with methods
[inkscape.git] / src / display / curve.cpp
1 #define __CURVE_C__
3 /** \file
4  * Routines for SPCurve and for NArtBpath arrays / Geom::PathVector 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  * Copyright (C) 2008 Johan Engelen
15  *
16  * Released under GNU GPL
17  */
19 #include "display/curve.h"
21 #include <string.h>
22 #include <glib/gmem.h>
23 #include "libnr/nr-point.h"
24 #include "libnr/nr-rect.h"
25 #include <libnr/n-art-bpath.h>
26 #include <libnr/nr-point-matrix-ops.h>
27 #include <libnr/nr-translate-ops.h>
28 #include <libnr/n-art-bpath-2geom.h>
29 #include <libnr/nr-convert2geom.h>
30 #include <cstring>
31 #include <string>
32 #include <2geom/pathvector.h>
33 #include <2geom/sbasis-geometric.h>
34 #include <2geom/sbasis-to-bezier.h>
35 #include "svg/svg.h"
37 static unsigned sp_bpath_length(NArtBpath const bpath[]);
38 static bool sp_bpath_closed(NArtBpath const bpath[]);
40 #define NO_CHECKS   // define this to disable the checking for unequal paths in SPCurve, improves performance by a lot!
43 #ifndef NO_CHECKS
44 static void debug_out( char const * text, Geom::PathVector const & pathv) {
45     char * str = sp_svg_write_path(pathv);
46     g_message("%s : %s", text, str);
47     g_free(str);
48 }
49 #endif
51 #ifndef NO_CHECKS
52 static void debug_out( char const * text, NArtBpath const * bpath) {
53     char * str = sp_svg_write_path(bpath);
54     g_message("%s : %s", text, str);
55     g_free(str);
56 }
57 #endif
59 #ifndef NO_CHECKS
60 void SPCurve::debug_check( char const * text, SPCurve const * curve) {
61     char * pathv_str = sp_svg_write_path(curve->_pathv);
62     char * bpath_str = sp_svg_write_path(curve->_bpath);
63     if ( strcmp(pathv_str, bpath_str) ) {
64         g_message("%s : unequal paths", text);
65         g_message("bpath : %s", bpath_str);
66         g_message("pathv : %s", pathv_str);
67     }
68     g_free(pathv_str);
69     g_free(bpath_str);
70 #else
71 void SPCurve::debug_check( char const *, SPCurve const *) {
72 #endif
73 }
75 #ifndef NO_CHECKS
76 void SPCurve::debug_check( char const * text, bool a) {
77     if ( !a ) {
78         g_message("%s : bool fail", text);
79     }
80 #else
81 void SPCurve::debug_check( char const *, bool) {
82 #endif
83 }
85 /* Constructors */
87 /**
88  * The returned curve's state is as if SPCurve::reset has just been called on it.
89  * \param length Initial number of NArtBpath elements allocated for bpath (including NR_END
90  *    element).
91  * 2GEOMproof
92  */
93 SPCurve::SPCurve(guint length)
94   : _refcount(1),
95     _bpath(NULL),
96     _pathv(),
97     _end(0),
98     _length(length),
99     _substart(0),
100     _hascpt(false),
101     _posSet(false),
102     _moving(false),
103     _closed(false)
105     if (length <= 0) {
106         g_error("SPCurve::SPCurve called with invalid length parameter");
107         throw;
108     }
110     _bpath = g_new(NArtBpath, length);
111     _bpath->code = NR_END;
113     _pathv.clear();
115     debug_check("SPCurve::SPCurve(guint length)", this);
118 SPCurve::SPCurve(Geom::PathVector const& pathv)
119   : _refcount(1),
120     _bpath(NULL),
121     _pathv(pathv),
122     _end(0),
123     _length(0),
124     _substart(0),
125     _hascpt(false),
126     _posSet(false),
127     _moving(false),
128     _closed(false)
130     // temporary code to convert to _bpath as well:
131     _bpath = BPath_from_2GeomPath(_pathv);
132     unsigned const len = sp_bpath_length(_bpath);
133     _length = len;
134     _end = _length - 1;
135     gint i = _end;
136     for (; i > 0; i--)
137         if ((_bpath[i].code == NR_MOVETO) ||
138             (_bpath[i].code == NR_MOVETO_OPEN))
139             break;
140     _substart = i;
141     _closed = sp_bpath_closed(_bpath);
143     debug_check("SPCurve::SPCurve(Geom::PathVector const& pathv)", this);
146 // * 2GEOMproof
147 SPCurve *
148 SPCurve::new_from_foreign_bpath(NArtBpath const *bpath)
150     g_return_val_if_fail(bpath != NULL, NULL);
152     NArtBpath *new_bpath;
153     unsigned const len = sp_bpath_length(bpath);
154     new_bpath = g_new(NArtBpath, len);
155     memcpy(new_bpath, bpath, len * sizeof(NArtBpath));
157     SPCurve *curve = new SPCurve();
159     curve->_bpath = new_bpath;
160     curve->_length = len;
161     curve->_end = curve->_length - 1;
162     gint i = curve->_end;
163     for (; i > 0; i--)
164         if ((curve->_bpath[i].code == NR_MOVETO) ||
165             (curve->_bpath[i].code == NR_MOVETO_OPEN))
166             break;
167     curve->_substart = i;
168     curve->_closed = sp_bpath_closed(new_bpath);
170     curve->_pathv = BPath_to_2GeomPath(curve->_bpath);
172     debug_check("SPCurve::new_from_foreign_bpath", curve);
174     return curve;
177 /**
178  * Convert NArtBpath object to SPCurve object.
179  *
180  * \return new SPCurve, or NULL if the curve was not created for some reason.
181  * 2GEOMproof
182  */
183 SPCurve *
184 SPCurve::new_from_bpath(NArtBpath *bpath)
186     g_return_val_if_fail(bpath != NULL, NULL);
188     SPCurve *curve = SPCurve::new_from_foreign_bpath(bpath);
189     g_free(bpath);
191     debug_check("SPCurve::new_from_bpath", curve);
193     return curve;
196 // * 2GEOMproof
197 SPCurve *
198 SPCurve::new_from_rect(NR::Maybe<NR::Rect> const &rect)
200     g_return_val_if_fail(rect, NULL);
202     SPCurve *c =  new SPCurve();
204     NR::Point p = rect->corner(0);
205     c->moveto(p);
207     for (int i=3; i>=0; i--) {
208         c->lineto(rect->corner(i));
209     }
210     c->closepath_current();
212     debug_check("SPCurve::new_from_rect", c);
214     return c;
217 // * 2GEOMproof
218 SPCurve::~SPCurve()
220     if (_bpath) {
221         g_free(_bpath);
222         _bpath = NULL;
223     }
226 /* Methods */
228 void
229 SPCurve::set_pathvector(Geom::PathVector const & new_pathv)
231     _pathv = new_pathv;
233     _hascpt = false;
234     _posSet = false;
235     _moving = false;
237     // temporary code to convert to _bpath as well:
238     if (_bpath) {
239         g_free(_bpath);
240         _bpath = NULL;
241     }
242     _bpath = BPath_from_2GeomPath(_pathv);
243     unsigned const len = sp_bpath_length(_bpath);
244     _length = len;
245     _end = _length - 1;
246     gint i = _end;
247     for (; i > 0; i--)
248         if ((_bpath[i].code == NR_MOVETO) ||
249             (_bpath[i].code == NR_MOVETO_OPEN))
250             break;
251     _substart = i;
252     _closed = sp_bpath_closed(_bpath);
254     debug_check("SPCurve::set_pathvector", this);
257 /**
258  * Get pointer to bpath data. Don't keep this reference too long, because the path might change by another function.
259  */
260 NArtBpath const *
261 SPCurve::get_bpath() const
263     debug_check("SPCurve::get_bpath", this);
264     return _bpath;
265 };
267 Geom::PathVector const &
268 SPCurve::get_pathvector() const
270     debug_check("SPCurve::get_pathvector", this);
271     return _pathv;
274 /**
275  *Returns index in bpath[] of NR_END element.
276  * remove for 2geom
277  */
278 guint
279 SPCurve::get_length() const
281 //    g_message("SPCurve::get_length must be removed");
283     return _end;
286 /*
287  * Returns the number of segments of all paths summed
288  */
289 guint
290 SPCurve::get_segment_count() const
292     guint nr = 0;
293     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
294         nr += (*it).size();
296         if (it->closed())   nr += 1;
297     }
298     return nr;
302 /**
303  * Increase _refcount of curve.
304  *
305  * \todo should this be shared with other refcounting code?
306  */
307 SPCurve *
308 SPCurve::ref()
310     _refcount += 1;
312     return this;
315 /**
316  * Decrease refcount of curve, with possible destruction.
317  *
318  * \todo should this be shared with other refcounting code?
319  */
320 SPCurve *
321 SPCurve::unref()
323     _refcount -= 1;
325     if (_refcount < 1) {
326         delete this;
327     }
329     return NULL;
332 /**
333  * Add space for more paths in curve.
334  * This function has no meaning for 2geom representation, other than maybe for optimization issues (enlargening the vector for what is to come)
335  * 2GEOMproof
336  */
337 void
338 SPCurve::ensure_space(guint space)
340     g_return_if_fail(this != NULL);
341     g_return_if_fail(space > 0);
343     if (_end + space < _length)
344         return;
346     if (space < SP_CURVE_LENSTEP)
347         space = SP_CURVE_LENSTEP;
349     _bpath = g_renew(NArtBpath, _bpath, _length + space);
351     _length += space;
354 /**
355  * Create new curve from its own bpath array.
356  * 2GEOMproof
357  */
358 SPCurve *
359 SPCurve::copy() const
361     return SPCurve::new_from_foreign_bpath(_bpath);
364 /**
365  * Return new curve that is the concatenation of all curves in list.
366  * 2GEOMified
367  */
368 SPCurve *
369 SPCurve::concat(GSList const *list)
371     gint length = 0;
373     for (GSList const *l = list; l != NULL; l = l->next) {
374         SPCurve *c = (SPCurve *) l->data;
375         length += c->_end;
376     }
378     SPCurve *new_curve = new SPCurve(length + 1);
380     NArtBpath *bp = new_curve->_bpath;
382     for (GSList const *l = list; l != NULL; l = l->next) {
383         SPCurve *c = (SPCurve *) l->data;
384         memcpy(bp, c->_bpath, c->_end * sizeof(NArtBpath));
385         bp += c->_end;
386     }
388     bp->code = NR_END;
390     new_curve->_end = length;
391     gint i;
392     for (i = new_curve->_end; i > 0; i--) {
393         if ((new_curve->_bpath[i].code == NR_MOVETO)     ||
394             (new_curve->_bpath[i].code == NR_MOVETO_OPEN)  )
395             break;
396     }
398     new_curve->_substart = i;
400     for (GSList const *l = list; l != NULL; l = l->next) {
401         SPCurve *c = (SPCurve *) l->data;
402         new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() );
403     }
405     debug_check("SPCurve::concat", new_curve);
407     return new_curve;
410 /**
411  * Returns a list of new curves corresponding to the subpaths in \a curve.
412  * 2geomified
413  */
414 GSList *
415 SPCurve::split() const
417     guint p = 0;
418     GSList *l = NULL;
420     gint pathnr = 0;
421     while (p < _end) {
422         gint i = 1;
423         while ((_bpath[p + i].code == NR_LINETO) ||
424                (_bpath[p + i].code == NR_CURVETO))
425             i++;
426         SPCurve *new_curve = new SPCurve(i + 1);
427         memcpy(new_curve->_bpath, _bpath + p, i * sizeof(NArtBpath));
428         new_curve->_end = i;
429         new_curve->_bpath[i].code = NR_END;
430         new_curve->_substart = 0;
431         new_curve->_closed = (new_curve->_bpath->code == NR_MOVETO);
432         new_curve->_hascpt = (new_curve->_bpath->code == NR_MOVETO_OPEN);
433         new_curve->_pathv = Geom::PathVector(1, _pathv[pathnr]);
434         l = g_slist_prepend(l, new_curve);
435         p += i;
436         pathnr++;
437     }
439     return l;
442 /**
443  * Transform all paths in curve, template helper.
444  */
445 template<class M>
446 static void
447 tmpl_curve_transform(SPCurve * curve, M const &m)
449     g_return_if_fail(curve != NULL);
451     for (guint i = 0; i < curve->_end; i++) {
452         NArtBpath *p = curve->_bpath + i;
453         switch (p->code) {
454             case NR_MOVETO:
455             case NR_MOVETO_OPEN:
456             case NR_LINETO: {
457                 p->setC(3, p->c(3) * m);
458                 break;
459             }
460             case NR_CURVETO:
461                 for (unsigned i = 1; i <= 3; ++i) {
462                     p->setC(i, p->c(i) * m);
463                 }
464                 break;
465             default:
466                 g_warning("Illegal pathcode %d", p->code);
467                 break;
468         }
469     }
472 /**
473  * Transform all paths in curve using matrix.
474  * 2GEOMified, can be deleted when completely 2geom
475  */
476 void
477 SPCurve::transform(NR::Matrix const &m)
479     tmpl_curve_transform<NR::Matrix>(this, m);
481     _pathv = _pathv * to_2geom(m);
483     debug_check("SPCurve::transform(NR::Matrix const &m)", this);
486 /**
487  * Transform all paths in curve using matrix.
488  */
489 void
490 SPCurve::transform(Geom::Matrix const &m)
492     tmpl_curve_transform<NR::Matrix>(this, from_2geom(m));
494     _pathv = _pathv * m;
496     debug_check("SPCurve::transform(Geom::Matrix const &m)", this);
499 /**
500  * Transform all paths in curve using NR::translate.
501  * 2GEOMified, can be deleted when completely 2geom
502  */
503 void
504 SPCurve::transform(NR::translate const &m)
506     tmpl_curve_transform<NR::translate>(this, m);
508     _pathv = _pathv * to_2geom(m);
510     debug_check("SPCurve::transform(NR::translate const &m)", this);
513 /**
514  * Set curve to empty curve.
515  * 2GEOMified
516  */
517 void
518 SPCurve::reset()
520     _bpath->code = NR_END;
521     _end = 0;
522     _substart = 0;
523     _hascpt = false;
524     _posSet = false;
525     _moving = false;
526     _closed = false;
528     _pathv.clear();
530     debug_check("SPCurve::reset", this);
533 /* Several consecutive movetos are ALLOWED */
535 /**
536  * Calls SPCurve::moveto() with point made of given coordinates.
537  */
538 void
539 SPCurve::moveto(gdouble x, gdouble y)
541     moveto(NR::Point(x, y));
543 /**
544  * Calls SPCurve::moveto() with point made of given coordinates.
545  */
546 void
547 SPCurve::moveto(Geom::Point const &p)
549     moveto(from_2geom(p));
551 /**
552  * Perform a moveto to a point, thus starting a new subpath.
553  * 2GEOMified
554  */
555 void
556 SPCurve::moveto(NR::Point const &p)
558     g_return_if_fail(!_moving);
560     _substart = _end;
561     _hascpt = true;
562     _posSet = true;
563     _movePos = p;
564     _pathv.push_back( Geom::Path() );  // for some reason Geom::Path(p) does not work...
565     _pathv.back().start(to_2geom(p));
567     // the output is not the same. This is because SPCurve *incorrectly* coaslesces multiple moveto's into one for NArtBpath.
568 //    debug_check("SPCurve::moveto", this);
571 /**
572  * Calls SPCurve::lineto() with a point's coordinates.
573  */
574 void
575 SPCurve::lineto(Geom::Point const &p)
577     lineto(p[Geom::X], p[Geom::Y]);
579 /**
580  * Calls SPCurve::lineto() with a point's coordinates.
581  */
582 void
583 SPCurve::lineto(NR::Point const &p)
585     lineto(p[NR::X], p[NR::Y]);
587 /**
588  * Adds a line to the current subpath.
589  * 2GEOMified
590  */
591 void
592 SPCurve::lineto(gdouble x, gdouble y)
594     g_return_if_fail(_hascpt);
596     if (_moving) {
597         /* fix endpoint */
598         g_return_if_fail(!_posSet);
599         g_return_if_fail(_end > 1);
600         NArtBpath *bp = _bpath + _end - 1;
601         g_return_if_fail(bp->code == NR_LINETO);
602         bp->x3 = x;
603         bp->y3 = y;
604         _moving = false;
606         Geom::Path::iterator it = _pathv.back().end();
607         if ( Geom::LineSegment const *last_line_segment = dynamic_cast<Geom::LineSegment const *>( &(*it) )) {
608             Geom::LineSegment new_seg( *last_line_segment );
609             new_seg.setFinal( Geom::Point(x,y) );
610             _pathv.back().replace(it, new_seg);
611         }
612     } else if (_posSet) {
613         /* start a new segment */
614         ensure_space(2);
615         NArtBpath *bp = _bpath + _end;
616         bp->code = NR_MOVETO_OPEN;
617         bp->setC(3, _movePos);
618         bp++;
619         bp->code = NR_LINETO;
620         bp->x3 = x;
621         bp->y3 = y;
622         bp++;
623         bp->code = NR_END;
624         _end += 2;
625         _posSet = false;
626         _closed = false;
628         _pathv.back().appendNew<Geom::LineSegment>( Geom::Point(x,y) );
629         return;
630     } else {
631         /* add line */
633         g_return_if_fail(_end > 1);
634         ensure_space(1);
635         NArtBpath *bp = _bpath + _end;
636         bp->code = NR_LINETO;
637         bp->x3 = x;
638         bp->y3 = y;
639         bp++;
640         bp->code = NR_END;
641         _end++;
642         _pathv.back().appendNew<Geom::LineSegment>( Geom::Point(x,y) );
643     }
645     debug_check("SPCurve::lineto", this);
648 /**
649  * Calls SPCurve::curveto() with coordinates of three points.
650  */
651 void
652 SPCurve::curveto(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2)
654     using Geom::X;
655     using Geom::Y;
656     curveto( p0[X], p0[Y],
657              p1[X], p1[Y],
658              p2[X], p2[Y] );
660 /**
661  * Calls SPCurve::curveto() with coordinates of three points.
662  */
663 void
664 SPCurve::curveto(NR::Point const &p0, NR::Point const &p1, NR::Point const &p2)
666     using NR::X;
667     using NR::Y;
668     curveto( p0[X], p0[Y],
669              p1[X], p1[Y],
670              p2[X], p2[Y] );
672 /**
673  * Adds a bezier segment to the current subpath.
674  * 2GEOMified
675  */
676 void
677 SPCurve::curveto(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
679     g_return_if_fail(_hascpt);
680     g_return_if_fail(!_moving);
682     if (_posSet) {
683         /* start a new segment */
684         ensure_space(2);
685         NArtBpath *bp = _bpath + _end;
686         bp->code = NR_MOVETO_OPEN;
687         bp->setC(3, _movePos);
688         bp++;
689         bp->code = NR_CURVETO;
690         bp->x1 = x0;
691         bp->y1 = y0;
692         bp->x2 = x1;
693         bp->y2 = y1;
694         bp->x3 = x2;
695         bp->y3 = y2;
696         bp++;
697         bp->code = NR_END;
698         _end += 2;
699         _posSet = false;
700         _closed = false;
701         _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
702     } else {
703         /* add curve */
705         g_return_if_fail(_end > 1);
706         ensure_space(1);
707         NArtBpath *bp = _bpath + _end;
708         bp->code = NR_CURVETO;
709         bp->x1 = x0;
710         bp->y1 = y0;
711         bp->x2 = x1;
712         bp->y2 = y1;
713         bp->x3 = x2;
714         bp->y3 = y2;
715         bp++;
716         bp->code = NR_END;
717         _end++;
718         if (_pathv.empty())  g_message("leeg");
719         else _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
720     }
722     debug_check("SPCurve::curveto", this);
725 /**
726  * Close current subpath by possibly adding a line between start and end.
727   * 2GEOMified
728  */
729 void
730 SPCurve::closepath()
732     g_return_if_fail(_hascpt);
733     g_return_if_fail(!_posSet);
734     g_return_if_fail(!_moving);
735     g_return_if_fail(!_closed);
736     /* We need at least moveto, curveto, end. */
737     g_return_if_fail(_end - _substart > 1);
739     {
740         NArtBpath *bs = _bpath + _substart;
741         NArtBpath *be = _bpath + _end - 1;
743         if (bs->c(3) != be->c(3)) {
744             lineto(bs->c(3));
745             bs = _bpath + _substart;
746         }
748         bs->code = NR_MOVETO;
749     }
750     // Inkscape always manually adds the closing line segment to SPCurve with a lineto.
751     // This lineto is removed in the writing function for NArtBpath, 
752     // so when path is closed and the last segment is a lineto, the closing line segment must really be removed first!
753     // TODO: fix behavior in Inkscape!
754     if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const  *>(&_pathv.back().back())) {
755         _pathv.back().erase_last();
756     }
757     _pathv.back().close(true);
758     _closed = true;
760     for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) {
761          if ( ! it->closed() ) {
762             _closed = false;
763             break;
764         }
765     }
767     for (NArtBpath const *bp = _bpath; bp->code != NR_END; bp++) {
768         /** \todo
769          * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of
770          * the closed boolean).
771          */
772         if (bp->code == NR_MOVETO_OPEN) {
773             _closed = false;
774             break;
775         }
776     }
778     _hascpt = false;
780     debug_check("SPCurve::closepath", this);
783 /** Like SPCurve::closepath() but sets the end point of the current
784     command to the subpath start point instead of adding a new lineto.
786     Used for freehand drawing when the user draws back to the start point.
787   
788   2GEOMified
789 **/
790 void
791 SPCurve::closepath_current()
793     g_return_if_fail(_hascpt);
794     g_return_if_fail(!_posSet);
795     g_return_if_fail(!_closed);
796     /* We need at least moveto, curveto, end. */
797     g_return_if_fail(_end - _substart > 1);
799     {
800         NArtBpath *bs = _bpath + _substart;
801         NArtBpath *be = _bpath + _end - 1;
803         be->x3 = bs->x3;
804         be->y3 = bs->y3;
806         bs->code = NR_MOVETO;
807     }
808     // Inkscape always manually adds the closing line segment to SPCurve with a lineto.
809     // This lineto is removed in the writing function for NArtBpath, 
810     // so when path is closed and the last segment is a lineto, the closing line segment must really be removed first!
811     // TODO: fix behavior in Inkscape!
812     if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const  *>(&_pathv.back().back())) {
813         _pathv.back().erase_last();
814     }
815     _pathv.back().close(true);
816     _closed = true;
818     for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) {
819          if ( ! it->closed() ) {
820             _closed = false;
821             break;
822         }
823     }
825     for (NArtBpath const *bp = _bpath; bp->code != NR_END; bp++) {
826         /** \todo
827          * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of
828          * the closed boolean).
829          */
830         if (bp->code == NR_MOVETO_OPEN) {
831             _closed = false;
832             break;
833         }
834     }
836     _hascpt = false;
837     _moving = false;
839     debug_check("SPCurve::closepath_current", this);
842 /**
843  * True if no paths are in curve. If it only contains a path with only a moveto, the path is considered NON-empty
844  */
845 bool
846 SPCurve::is_empty() const
848     bool empty = _pathv.empty();
850     return empty;
853 /**
854  * True iff all subpaths are closed.
855  */
856 bool
857 SPCurve::is_closed() const
859     bool closed = true;
860     for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) {
861          if ( ! it->closed() ) {
862             closed = false;
863             break;
864         }
865     }
867     return closed;
870 /**
871  * Return last subpath or NULL.
872  */
873 NArtBpath const *
874 SPCurve::last_bpath() const
876     if (_end == 0) {
877         return NULL;
878     }
880     return _bpath + _end - 1;
883 /**
884  * Return last pathsegment (possibly the closing path segment) in PathVector or NULL.
885  * equal in functionality to SPCurve::last_bpath()
886  */
887 Geom::Curve const *
888 SPCurve::last_segment() const
890     if (is_empty()) {
891         return NULL;
892     }
893     if (_pathv.back().empty()) {
894         return NULL;
895     }
897     return &_pathv.back().back_default();
900 /**
901  * Return last path in PathVector or NULL.
902  */
903 Geom::Path const *
904 SPCurve::last_path() const
906     if (is_empty()) {
907         return NULL;
908     }
910     return &_pathv.back();
913 /**
914  * Return first pathsegment in PathVector or NULL.
915  * equal in functionality to SPCurve::first_bpath()
916  */
917 Geom::Curve const *
918 SPCurve::first_segment() const
920     if (is_empty()) {
921         return NULL;
922     }
923     if (_pathv.front().empty()) {
924         return NULL;
925     }
927     return &_pathv.front().front();
930 /**
931  * Return first path in PathVector or NULL.
932  */
933 Geom::Path const *
934 SPCurve::first_path() const
936     if (is_empty()) {
937         return NULL;
938     }
940     return &_pathv.front();
943 /**
944  * Return first point of first subpath or (0,0).  TODO: shouldn't this be (NR_HUGE, NR_HUGE) to be able to tell it apart from normal (0,0) ?
945  */
946 NR::Point
947 SPCurve::first_point() const
949     if (is_empty())
950         return NR::Point(0, 0);
952     return from_2geom( _pathv.front().initialPoint() );
955 /**
956  * Return the second point of first subpath or _movePos if curve too short.
957  * If the pathvector is empty, this returns (0,0). If the first path is only a moveto, this method
958  * returns the first point of the second path, if it exists. If there is no 2nd path, it returns the
959  * first point of the first path.
960  *
961  * FIXME: for empty paths shouldn't this return (NR_HUGE,NR_HUGE)
962  */
963 NR::Point
964 SPCurve::second_point() const
966     if (is_empty()) {
967         return NR::Point(0,0);
968     }
969     else if (_pathv.front().empty()) {
970         // first path is only a moveto
971         // check if there is second path
972         if (_pathv.size() > 1) {
973             return _pathv[1].initialPoint();
974         } else {
975             return _pathv[0].initialPoint();
976         }
977     }
978     else
979         return _pathv.front()[0].finalPoint();
982 /**
983  * Return the second-last point of last subpath or _movePos if curve too short.
984  */
985 NR::Point
986 SPCurve::penultimate_point() const
988     if (_end < 2) {
989         return _movePos;
990     }
992     NArtBpath *const bpath = _bpath + _end - 2;
993     g_return_val_if_fail(bpath != NULL, NR::Point(0, 0));
996     Geom::Curve const& back = _pathv.back().back_default();
997     Geom::Point p = back.initialPoint();
999     debug_check("SPCurve::penultimate_point", bpath->c(3) == from_2geom(p) );
1000     return from_2geom(p);
1003 /**
1004  * Return last point of last subpath or (0,0).  TODO: shouldn't this be (NR_HUGE, NR_HUGE) to be able to tell it apart from normal (0,0) ?
1005  * If the last path is only a moveto, then return that point.
1006  */
1007 NR::Point
1008 SPCurve::last_point() const
1010     if (is_empty())
1011         return NR::Point(0, 0);
1013     return from_2geom( _pathv.back().finalPoint() );
1016 inline static bool
1017 is_moveto(NRPathcode const c)
1019     return c == NR_MOVETO || c == NR_MOVETO_OPEN;
1022 /**
1023  * Returns a *new* \a curve but drawn in the opposite direction.
1024  * Should result in the same shape, but
1025  * with all its markers drawn facing the other direction.
1026  * Reverses the order of subpaths as well
1027  * 2GEOMified
1028  **/
1029 SPCurve *
1030 SPCurve::create_reverse() const
1032     /* We need at least moveto, curveto, end. */
1033     g_return_val_if_fail(_end - _substart > 1, NULL);
1035     NArtBpath const *be = _bpath + _end - 1;
1037     g_assert(is_moveto(_bpath[_substart].code));
1038     g_assert(is_moveto(_bpath[0].code));
1039     g_assert((be+1)->code == NR_END);
1041     SPCurve  *new_curve = new SPCurve(_length);
1042     new_curve->moveto(be->c(3));
1044     for (NArtBpath const *bp = be; ; --bp) {
1045         switch (bp->code) {
1046             case NR_MOVETO:
1047                 g_assert(new_curve->_bpath[new_curve->_substart].code == NR_MOVETO_OPEN);
1048                 new_curve->_bpath[new_curve->_substart].code = NR_MOVETO;
1049                 /* FALL-THROUGH */
1050             case NR_MOVETO_OPEN:
1051                 if (bp == _bpath) {
1052                     return new_curve;
1053                 }
1054                 new_curve->moveto((bp-1)->c(3));
1055                 break;
1057             case NR_LINETO:
1058                 new_curve->lineto((bp-1)->c(3));
1059                 break;
1061             case NR_CURVETO:
1062                 new_curve->curveto(bp->c(2), bp->c(1), (bp-1)->c(3));
1063                 break;
1065             default:
1066                 g_assert_not_reached();
1067         }
1068     }
1070     new_curve->_pathv = Geom::reverse_paths_and_order(_pathv);
1072     debug_check("SPCurve::create_reverse", new_curve);
1075 /**
1076  * Append \a curve2 to \a this.
1077  * If \a use_lineto is false, simply add all paths in \a curve2 to \a this;
1078  * if \a use_lineto is true, combine \a this's last path and \a curve2's first path and add the rest of the paths in \a curve2 to \a this.
1079  * 2GEOMified
1080  */
1081 void
1082 SPCurve::append(SPCurve const *curve2,
1083                 bool use_lineto)
1085     g_return_if_fail(curve2 != NULL);
1087     if (curve2->is_empty())
1088         return;
1089     if (curve2->_end < 1)
1090         return;
1092     NArtBpath const *bs = curve2->_bpath;
1094     bool closed = this->_closed;
1096     for (NArtBpath const *bp = bs; bp->code != NR_END; bp++) {
1097         switch (bp->code) {
1098             case NR_MOVETO_OPEN:
1099                 if (use_lineto && _hascpt) {
1100                     lineto(bp->x3, bp->y3);
1101                     use_lineto = false;
1102                 } else {
1103                     if (closed && _hascpt) closepath();
1104                     moveto(bp->x3, bp->y3);
1105                 }
1106                 closed = false;
1107                 break;
1109             case NR_MOVETO:
1110                 if (use_lineto && _hascpt) {
1111                     lineto(bp->x3, bp->y3);
1112                     use_lineto = FALSE;
1113                 } else {
1114                     if (closed && _hascpt) closepath();
1115                     moveto(bp->x3, bp->y3);
1116                 }
1117                 closed = true;
1118                 break;
1120             case NR_LINETO:
1121                 lineto(bp->x3, bp->y3);
1122                 break;
1124             case NR_CURVETO:
1125                 curveto(bp->x1, bp->y1, bp->x2, bp->y2, bp->x3, bp->y3);
1126                 break;
1128             case NR_END:
1129                 g_assert_not_reached();
1130         }
1131     }
1133     if (closed) {
1134         closepath();
1135     }
1137     debug_check("SPCurve::append", this);
1139     /* 2GEOM code when code above is removed:
1140     if (use_lineto) {
1141         Geom::PathVector::const_iterator it = curve2->_pathv.begin();
1142         if ( ! _pathv.empty() ) {
1143             Geom::Path & lastpath = _pathv.back();
1144             lastpath.appendNew<Geom::LineSegment>( (*it).initialPoint() );
1145             lastpath.append( (*it) );
1146         } else {
1147             _pathv.push_back( (*it) );
1148         }
1150         for (it++; it != curve2->_pathv.end(); it++) {
1151             _pathv.push_back( (*it) );
1152         }
1153     } else {
1154         for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) {
1155             _pathv.push_back( (*it) );
1156         }
1157     }
1158     */
1161 /**
1162  * Append \a c1 to \a this with possible fusing of close endpoints.
1163  * 2GEOMproof. Needs to be recoded when NArtBpath is no longer there. Right now, it applies the same changes to bpath and pathv depending on bpath
1164  */
1165 SPCurve *
1166 SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance)
1168     g_return_val_if_fail(c1 != NULL, NULL);
1169     g_return_val_if_fail(!_closed, NULL);
1170     g_return_val_if_fail(!c1->_closed, NULL);
1172     if (c1->_end < 1) {
1173         return this;
1174     }
1176     debug_check("SPCurve::append_continuous 11", this);
1178     NArtBpath const *be = last_bpath();
1179     if (be) {
1180         NArtBpath const *bs = c1->get_bpath();
1181         if ( bs
1182              && ( fabs( bs->x3 - be->x3 ) <= tolerance )
1183              && ( fabs( bs->y3 - be->y3 ) <= tolerance ) )
1184         {
1185             /** \todo
1186              * fixme: Strictly we mess in case of multisegment mixed
1187              * open/close curves
1188              */
1189             bool closed = false;
1190             for (bs = bs + 1; bs->code != NR_END; bs++) {
1191                 switch (bs->code) {
1192                     case NR_MOVETO_OPEN:
1193                         if (closed) closepath();
1194                         moveto(bs->x3, bs->y3);
1195                         closed = false;
1196                         break;
1197                     case NR_MOVETO:
1198                         if (closed) closepath();
1199                         moveto(bs->x3, bs->y3);
1200                         closed = true;
1201                         break;
1202                     case NR_LINETO:
1203                         lineto(bs->x3, bs->y3);
1204                         break;
1205                     case NR_CURVETO:
1206                         curveto(bs->x1, bs->y1, bs->x2, bs->y2, bs->x3, bs->y3);
1207                         break;
1208                     case NR_END:
1209                         g_assert_not_reached();
1210                 }
1211             }
1212         } else {
1213             append(c1, TRUE);
1214         }
1215     } else {
1216         append(c1, TRUE);
1217     }
1219     debug_check("SPCurve::append_continuous", this);
1221     return this;
1224 /**
1225  * Remove last segment of curve.
1226  * (Only used once in /src/pen-context.cpp)
1227  * 2GEOMified
1228  */
1229 void
1230 SPCurve::backspace()
1232     if ( is_empty() )
1233         return;
1235     if (_end > 0) {
1236         _end -= 1;
1237         if (_end > 0) {
1238             NArtBpath *bp = _bpath + _end - 1;
1239             if ((bp->code == NR_MOVETO)     ||
1240                 (bp->code == NR_MOVETO_OPEN)  )
1241             {
1242                 _hascpt = true;
1243                 _posSet = true;
1244                 _closed = false;
1245                 _movePos = bp->c(3);
1246                 _end -= 1;
1247             }
1248         }
1249         _bpath[_end].code = NR_END;
1250     }
1252     if ( !_pathv.back().empty() ) {
1253         _pathv.back().erase_last();
1254         _pathv.back().close(false);
1255     }
1257     debug_check("SPCurve::backspace", this);
1260 /* Private methods */
1262 /**
1263  * Returns index of first NR_END bpath in array.
1264  */
1265 static unsigned sp_bpath_length(NArtBpath const bpath[])
1267     g_return_val_if_fail(bpath != NULL, FALSE);
1269     unsigned ret = 0;
1270     while ( bpath[ret].code != NR_END ) {
1271         ++ret;
1272     }
1273     ++ret;
1275     return ret;
1278 /**
1279  * \brief
1280  *
1281  * \todo
1282  * fixme: this is bogus -- it doesn't check for nr_moveto, which will indicate
1283  * a closing of the subpath it's nonsense to talk about a path as a whole
1284  * being closed, although maybe someone would want that for some other reason?
1285  * Oh, also, if the bpath just ends, then it's *open*.  I hope nobody is using
1286  * this code for anything.
1287  */
1288 static bool sp_bpath_closed(NArtBpath const bpath[])
1290     g_return_val_if_fail(bpath != NULL, FALSE);
1292     for (NArtBpath const *bp = bpath; bp->code != NR_END; bp++) {
1293         if (bp->code == NR_MOVETO_OPEN) {
1294             return false;
1295         }
1296     }
1298     return true;
1301 /**
1302  * Returns length of bezier segment.
1303  */
1304 static double
1305 bezier_len(NR::Point const &c0,
1306            NR::Point const &c1,
1307            NR::Point const &c2,
1308            NR::Point const &c3,
1309            double const threshold)
1311     /** \todo
1312      * The SVG spec claims that a closed form exists, but for the moment I'll
1313      * use a stupid algorithm.
1314      */
1315     double const lbound = L2( c3 - c0 );
1316     double const ubound = L2( c1 - c0 ) + L2( c2 - c1 ) + L2( c3 - c2 );
1317     double ret;
1318     if ( ubound - lbound <= threshold ) {
1319         ret = .5 * ( lbound + ubound );
1320     } else {
1321         NR::Point const a1( .5 * ( c0 + c1 ) );
1322         NR::Point const b2( .5 * ( c2 + c3 ) );
1323         NR::Point const c12( .5 * ( c1 + c2 ) );
1324         NR::Point const a2( .5 * ( a1 + c12 ) );
1325         NR::Point const b1( .5 * ( c12 + b2 ) );
1326         NR::Point const midpoint( .5 * ( a2 + b1 ) );
1327         double const rec_threshold = .625 * threshold;
1328         ret = bezier_len(c0, a1, a2, midpoint, rec_threshold) + bezier_len(midpoint, b1, b2, c3, rec_threshold);
1329         if (!(lbound - 1e-2 <= ret && ret <= ubound + 1e-2)) {
1330             using NR::X; using NR::Y;
1331             g_warning("ret=%f outside of expected bounds [%f, %f] for {(%.0f %.0f) (%.0f %.0f) (%.0f %.0f) (%.0f %.0f)}",
1332                       ret, lbound, ubound, c0[X], c0[Y], c1[X], c1[Y], c2[X], c2[Y], c3[X], c3[Y]);
1333         }
1334     }
1335     return ret;
1338 /**
1339  * Returns total length of curve, excluding length of closepath segments.
1340  */
1341 double
1342 sp_curve_distance_including_space(SPCurve const *const curve, double seg2len[])
1344     g_return_val_if_fail(curve != NULL, 0.);
1346     double ret = 0.0;
1348     if ( curve->_bpath->code == NR_END ) {
1349         return ret;
1350     }
1352     NR::Point prev(curve->_bpath->c(3));
1353     for (guint i = 1; i < curve->_end; ++i) {
1354         NArtBpath &p = curve->_bpath[i];
1355         double seg_len = 0;
1356         switch (p.code) {
1357             case NR_MOVETO_OPEN:
1358             case NR_MOVETO:
1359             case NR_LINETO:
1360                 seg_len = L2(p.c(3) - prev);
1361                 break;
1363             case NR_CURVETO:
1364                 seg_len = bezier_len(prev, p.c(1), p.c(2), p.c(3), 1.);
1365                 break;
1367             case NR_END:
1368                 return ret;
1369         }
1370         seg2len[i - 1] = seg_len;
1371         ret += seg_len;
1372         prev = p.c(3);
1373     }
1374     g_assert(!(ret < 0));
1375     return ret;
1378 /**
1379  * Like sp_curve_distance_including_space(), but ensures that the
1380  * result >= 1e-18:  uses 1 per segment if necessary.
1381  */
1382 double
1383 sp_curve_nonzero_distance_including_space(SPCurve const *const curve, double seg2len[])
1385     double const real_dist(sp_curve_distance_including_space(curve, seg2len));
1386     if (real_dist >= 1e-18) {
1387         return real_dist;
1388     } else {
1389         unsigned const nSegs = SP_CURVE_LENGTH(curve) - 1;
1390         for (unsigned i = 0; i < nSegs; ++i) {
1391             seg2len[i] = 1.;
1392         }
1393         return (double) nSegs;
1394     }
1397 /**
1398  * 2GEOMified
1399  */
1400 void
1401 SPCurve::stretch_endpoints(NR::Point const &new_p0, NR::Point const &new_p1)
1403     if (is_empty()) {
1404         return;
1405     }
1406     g_assert(unsigned(SP_CURVE_LENGTH(this)) + 1 == sp_bpath_length(_bpath));
1407     unsigned const nSegs = SP_CURVE_LENGTH(this) - 1;
1408     g_assert(nSegs != 0);
1409     double *const seg2len = new double[nSegs];
1410     double const tot_len = sp_curve_nonzero_distance_including_space(this, seg2len);
1411     NR::Point const offset0( new_p0 - first_point() );
1412     NR::Point const offset1( new_p1 - last_point() );
1413     _bpath->setC(3, new_p0);
1414     double begin_dist = 0.;
1415     for (unsigned si = 0; si < nSegs; ++si) {
1416         double const end_dist = begin_dist + seg2len[si];
1417         NArtBpath &p = _bpath[1 + si];
1418         switch (p.code) {
1419             case NR_LINETO:
1420             case NR_MOVETO:
1421             case NR_MOVETO_OPEN:
1422                 p.setC(3, p.c(3) + NR::Lerp(end_dist / tot_len, offset0, offset1));
1423                 break;
1425             case NR_CURVETO:
1426                 for (unsigned ci = 1; ci <= 3; ++ci) {
1427                     p.setC(ci, p.c(ci) + Lerp((begin_dist + ci * seg2len[si] / 3.) / tot_len, offset0, offset1));
1428                 }
1429                 break;
1431             default:
1432                 g_assert_not_reached();
1433         }
1435         begin_dist = end_dist;
1436     }
1437     g_assert(L1(_bpath[nSegs].c(3) - new_p1) < 1.);
1438     /* Explicit set for better numerical properties. */
1439     _bpath[nSegs].setC(3, new_p1);
1440     delete [] seg2len;
1442     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = _pathv.front().toPwSb();
1443     Geom::Piecewise<Geom::SBasis> arclength = Geom::arcLengthSb(pwd2);
1444     if ( arclength.lastValue() <= 0 ) {
1445         g_error("SPCurve::stretch_endpoints - arclength <= 0");
1446         throw;
1447     }
1448     arclength *= 1./arclength.lastValue();
1449     Geom::Point const A( to_2geom(offset0) );
1450     Geom::Point const B( to_2geom(offset1) );
1451     Geom::Piecewise<Geom::SBasis> offsetx = (arclength*-1.+1)*A[0] + arclength*B[0];
1452     Geom::Piecewise<Geom::SBasis> offsety = (arclength*-1.+1)*A[1] + arclength*B[1];
1453     Geom::Piecewise<Geom::D2<Geom::SBasis> > offsetpath = Geom::sectionize( Geom::D2<Geom::Piecewise<Geom::SBasis> >(offsetx, offsety) );
1454     pwd2 += offsetpath;
1455     _pathv = Geom::path_from_piecewise( pwd2, 0.001 );
1457     debug_check("SPCurve::stretch_endpoints", this);
1460 /**
1461  *  sets start of first path to new_p0, and end of first path to  new_p1
1462  * 2GEOMified
1463  */
1464 void
1465 SPCurve::move_endpoints(NR::Point const &new_p0, NR::Point const &new_p1)
1467     if (is_empty()) {
1468         return;
1469     }
1470     unsigned const nSegs = SP_CURVE_LENGTH(this) - 1;
1471     g_assert(nSegs != 0);
1473     _bpath->setC(3, new_p0);
1474     _bpath[nSegs].setC(3, new_p1);
1476     _pathv.front().setInitial(to_2geom(new_p0));
1477     _pathv.front().setFinal(to_2geom(new_p1));
1479     debug_check("SPCurve::move_endpoints", this);
1482 /**
1483  * returns the number of nodes in a path, used for statusbar text when selecting an spcurve.
1484  * 2GEOMified
1485  */
1486 guint
1487 SPCurve::nodes_in_path() const
1489     gint r = _end;
1490     gint i = _length - 1;
1491     if (i > r) i = r; // sometimes after switching from node editor length is wrong, e.g. f6 - draw - f2 - tab - f1, this fixes it
1492     for (; i >= 0; i --)
1493         if (_bpath[i].code == NR_MOVETO)
1494             r --;
1496     guint nr = 0;
1497     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
1498         nr += (*it).size();
1500         nr++; // count last node (this works also for closed paths because although they don't have a 'last node', they do have an extra segment
1501     }
1503     debug_check("SPCurve::nodes_in_path", r == (gint)nr);
1505     return r;
1508 /**
1509  *  Adds p to the last point (and last handle if present) of the last path
1510  * 2GEOMified
1511  */
1512 void
1513 SPCurve::last_point_additive_move(Geom::Point const & p)
1515     if (is_empty()) {
1516         return;
1517     }
1518     if (_end == 0) {
1519         return;
1520     }
1521     NArtBpath * path = _bpath + _end - 1;
1523     if (path->code == NR_CURVETO) {
1524         path->x2 += p[Geom::X];
1525         path->y2 += p[Geom::Y];
1526     }
1527     path->x3 += p[Geom::X];
1528     path->y3 += p[Geom::Y];
1530     _pathv.back().setFinal( _pathv.back().finalPoint() + p );
1532     // Move handle as well when the last segment is a cubic bezier segment:
1533     // TODO: what to do for quadratic beziers?
1534     if ( Geom::CubicBezier const *lastcube = dynamic_cast<Geom::CubicBezier const *>(&_pathv.back().back()) ) {
1535         Geom::CubicBezier newcube( *lastcube );
1536         newcube.setPoint(2, newcube[2] + p);
1537         _pathv.back().replace( --_pathv.back().end(), newcube );
1538     }
1540     debug_check("SPCurve::last_point_additive_move", this);
1543 /*
1544   Local Variables:
1545   mode:c++
1546   c-file-style:"stroustrup"
1547   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1548   indent-tabs-mode:nil
1549   fill-column:99
1550   End:
1551 */
1552 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :