Code

Correct path separators and missed variable assignment due to indention
[inkscape.git] / src / 2geom / path.h
1 /*
2  * Path - Series of continuous curves
3  *
4  * Authors:
5  *              MenTaLguY <mental@rydia.net>
6  *              Marco Cecchetti <mrcekets at gmail.com>
7  * 
8  * Copyright 2007-2008  authors
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it either under the terms of the GNU Lesser General Public
12  * License version 2.1 as published by the Free Software Foundation
13  * (the "LGPL") or, at your option, under the terms of the Mozilla
14  * Public License Version 1.1 (the "MPL"). If you do not alter this
15  * notice, a recipient may use your version of this file under either
16  * the MPL or the LGPL.
17  *
18  * You should have received a copy of the LGPL along with this library
19  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  * You should have received a copy of the MPL along with this library
22  * in the file COPYING-MPL-1.1
23  *
24  * The contents of this file are subject to the Mozilla Public License
25  * Version 1.1 (the "License"); you may not use this file except in
26  * compliance with the License. You may obtain a copy of the License at
27  * http://www.mozilla.org/MPL/
28  *
29  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
30  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
31  * the specific language governing rights and limitations.
32  */
37 #ifndef SEEN_GEOM_PATH_H
38 #define SEEN_GEOM_PATH_H
41 #include <2geom/curves.h>
43 #include <iterator>
46 namespace Geom
47 {
49 // Conditional expression for types. If true, first, if false, second.
50 template<bool _Cond, typename _Iftrue, typename _Iffalse>
51   struct __conditional_type
52   { typedef _Iftrue __type; };
54 template<typename _Iftrue, typename _Iffalse>
55   struct __conditional_type<false, _Iftrue, _Iffalse>
56   { typedef _Iffalse __type; };
59 template <typename IteratorImpl>
60 class BaseIterator
61 : public std::iterator<std::forward_iterator_tag, Curve const>
62 {
63 public:
64   BaseIterator() {}
66   // default construct
67   // default copy
69   // Allow Sequence::iterator to Sequence::const_iterator conversion
70   // unfortunately I do not know how to imitate the way __normal_iterator 
71   // does it, because I don't see a way to get the typename of the container 
72   // IteratorImpl is pointing at...
73   typedef std::vector<Curve *> Sequence;
74   BaseIterator (  typename __conditional_type<
75                     (std::__are_same<IteratorImpl, Sequence::const_iterator >::__value),  // check if this instantiation is of const_iterator type
76                     const BaseIterator< Sequence::iterator >,     // if true:  accept iterator in const_iterator instantiation
77                     const BaseIterator<IteratorImpl> > ::__type   // if false: default to standard copy constructor
78                   & __other)
79     : impl_(__other.impl_) { }
80   friend class BaseIterator< Sequence::const_iterator >;
83   bool operator==(BaseIterator const &other) {
84     return other.impl_ == impl_;
85   }
86   bool operator!=(BaseIterator const &other) {
87     return other.impl_ != impl_;
88   }
90   Curve const &operator*() const { return **impl_; }
91   Curve const *operator->() const { return *impl_; }
93   BaseIterator &operator++() {
94     ++impl_;
95     return *this;
96   }
98   BaseIterator operator++(int) {
99     BaseIterator old=*this;
100     ++(*this);
101     return old;
102   }
104   BaseIterator &operator--() {
105     --impl_;
106     return *this;
107   }
109   BaseIterator operator--(int) {
110     BaseIterator old=*this;
111     --(*this);
112     return old;
113   }
115 private:
116   BaseIterator(IteratorImpl const &pos) : impl_(pos) {}
118   IteratorImpl impl_;
119   friend class Path;
120 };
122 template <typename Iterator>
123 class DuplicatingIterator
124 : public std::iterator<std::input_iterator_tag, Curve *>
126 public:
127   DuplicatingIterator() {}
128   DuplicatingIterator(Iterator const &iter) : impl_(iter) {}
130   bool operator==(DuplicatingIterator const &other) {
131     return other.impl_ == impl_;
132   }
133   bool operator!=(DuplicatingIterator const &other) {
134     return other.impl_ != impl_;
135   }
137   Curve *operator*() const { return (*impl_)->duplicate(); }
139   DuplicatingIterator &operator++() {
140     ++impl_;
141     return *this;
142   }
143   DuplicatingIterator operator++(int) {
144     DuplicatingIterator old=*this;
145     ++(*this);
146     return old;
147   }
149 private:
150   Iterator impl_;
151 };
153 /*
154  * Open and closed paths: all paths, whether open or closed, store a final
155  * segment which connects the initial and final endpoints of the "real"
156  * path data.  While similar to the "z" in an SVG path, it exists for
157  * both open and closed paths, and is not considered part of the "normal"
158  * path data, which is always covered by the range [begin(), end_open()).
159  * Conversely, the range [begin(), end_closed()) always contains the "extra"
160  * closing segment.
161  *
162  * The only difference between a closed and an open path is whether end()
163  * returns end_closed() or end_open().  The idea behind this is to let
164  * any path be stroked using [begin(), end_default()), and filled using
165  * [begin(), end_closed()), without requiring a separate "filled" version
166  * of the path to use for filling.
167  */
168 class Path {
169 private:
170   typedef std::vector<Curve *> Sequence;
172 public:
173   typedef BaseIterator<Sequence::iterator> iterator;
174   typedef BaseIterator<Sequence::const_iterator> const_iterator;
175   typedef Sequence::size_type size_type;
176   typedef Sequence::difference_type difference_type;
178   class ClosingSegment : public LineSegment {
179   public:
180     ClosingSegment() : LineSegment() {}
181     ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
182     virtual Curve *duplicate() const { return new ClosingSegment(*this); }
183   };
185   enum Stitching {
186     NO_STITCHING=0,
187     STITCH_DISCONTINUOUS
188   };
190   class StitchSegment : public LineSegment {
191   public:
192     StitchSegment() : LineSegment() {}
193     StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
194     virtual Curve *duplicate() const { return new StitchSegment(*this); }
195   };
197   Path()
198   : final_(new ClosingSegment()), closed_(false)
199   {
200     curves_.push_back(final_);
201   }
203   Path(Path const &other)
204   : final_(new ClosingSegment()), closed_(other.closed_)
205   {
206     curves_.push_back(final_);
207     insert(begin(), other.begin(), other.end());
208   }
210   explicit Path(Point p)
211   : final_(new ClosingSegment(p, p)), closed_(false)
212   {
213     curves_.push_back(final_);
214   }
216   template <typename Impl>
217   Path(BaseIterator<Impl> first, BaseIterator<Impl> last, bool closed=false)
218   : closed_(closed), final_(new ClosingSegment())
219   {
220     curves_.push_back(final_);
221     insert(begin(), first, last);
222   }
224   virtual ~Path() {
225       delete_range(curves_.begin(), curves_.end()-1);
226       delete final_;
227   }
229   Path &operator=(Path const &other) {
230     clear();
231     insert(begin(), other.begin(), other.end());
232     close(other.closed_);
233     return *this;
234   }
236   void swap(Path &other);
238   Curve const &operator[](unsigned i) const { return *curves_[i]; }
240   Curve const &front() const { return *curves_[0]; }
241   Curve const &back() const { return *curves_[curves_.size()-2]; }
242   Curve const &back_open() const { return *curves_[curves_.size()-2]; }
243   Curve const &back_closed() const { return *curves_[curves_.size()-1]; }
244   Curve const &back_default() const {
245     return ( closed_ ? back_closed() : back_open() );
246   }
248   const_iterator begin() const { return curves_.begin(); }
249   const_iterator end() const { return curves_.end()-1; }
250   iterator begin() { return curves_.begin(); }
251   iterator end() { return curves_.end()-1; }
253   const_iterator end_open() const { return curves_.end()-1; }
254   const_iterator end_closed() const { return curves_.end(); }
255   const_iterator end_default() const {
256     return ( closed_ ? end_closed() : end_open() );
257   }
259   size_type size() const { return curves_.size()-1; }
260   size_type max_size() const { return curves_.max_size()-1; }
262   bool empty() const { return curves_.size() == 1; }
263   bool closed() const { return closed_; }
264   void close(bool closed=true) { closed_ = closed; }
266   Rect boundsFast() const;
267   Rect boundsExact() const;
269   Piecewise<D2<SBasis> > toPwSb() const {
270     Piecewise<D2<SBasis> > ret;
271     ret.push_cut(0);
272     unsigned i = 1;
273     // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
274     for(const_iterator it = begin(); it != end_default(); ++it) {
275       if (!it->isDegenerate()) {
276         ret.push(it->toSBasis(), i++);
277       }
278     }
279     return ret;
280   }
282   bool operator==(Path const &m) const {
283       if (size() != m.size() || closed() != m.closed())
284           return false;
285       const_iterator it2 = m.curves_.begin();
286     for(const_iterator it = curves_.begin(); it != curves_.end(); ++it) {
287         const Curve& a = (*it);
288         const Curve& b = (*it2);
289         if(!(a == b))
290             return false;
291         ++it2;
292     }
293     return true;
294   }
296   /*
297      Path operator*=(Matrix)
298      This is not possible without at least partly regenerating the curves of 
299      the path, because a path can consist of many types of curves, 
300      e.g. a HLineSegment.
301      Such a segment cannot be transformed and stay a HLineSegment in general 
302      (take for example rotations).
303      This means that these curves of the path have to be replaced with 
304      LineSegments: new Curves.
305      So an implementation of this method should check the curve's type to see 
306      whether operator*= is doable for that curve type, ...
307   */
308   Path operator*(Matrix const &m) const {
309     Path ret;
310     ret.curves_.reserve(curves_.size());
311     for(const_iterator it = begin(); it != end(); ++it) {
312       Curve *curve = it->transformed(m);
313       ret.do_append(curve);
314     }
315     ret.close(closed_);
316     return ret;
317   }
319   /*
320   // this should be even quickier but it works at low level
321   Path operator*(Matrix const &m) const
322   {
323       Path result;
324       size_t sz = curves_.size() - 1;
325       if (sz == 0) return result;
326       result.curves_.resize(curves_.size());
327       result.curves_.back() = result.final_;
328       result.curves_[0] = (curves_[0])->transformed(m);
329       for (size_t i = 1; i < sz; ++i)
330       {
331           result.curves_[i] = (curves_[i])->transformed(m);
332           if ( result.curves_[i]->initialPoint() != result.curves_[i-1]->finalPoint() ) {
333               THROW_CONTINUITYERROR();
334           }
335       }
336       result.final_->setInitial( (result.curves_[sz])->finalPoint() );
337       result.final_->setFinal( (result.curves_[0])->initialPoint() );
338       result.closed_ = closed_;
339       return result;
340   }
341   */
342   
343   Point pointAt(double t) const 
344   {
345           unsigned int sz = size();
346           if ( closed() ) ++sz;
347           if ( t < 0 || t > sz  )
348           {
349                   THROW_RANGEERROR("parameter t out of bounds");
350           }
351           if ( empty() ) return Point(0,0);
352           double k, lt = modf(t, &k);
353           unsigned int i = static_cast<unsigned int>(k);
354           if ( i == sz ) 
355           { 
356                   --i;
357                   lt = 1;
358           }
359           return (*this)[i].pointAt(lt);
360   }
362   double valueAt(double t, Dim2 d) const 
363   {
364           unsigned int sz = size();
365           if ( closed() ) ++sz;
366           if ( t < 0 || t > sz  )
367           {
368                   THROW_RANGEERROR("parameter t out of bounds");
369           }
370           if ( empty() ) return 0;
371           double k, lt = modf(t, &k);
372           unsigned int i = static_cast<unsigned int>(k);
373           if ( i == sz ) 
374           { 
375                   --i;
376                   lt = 1;
377           }
378           return (*this)[i].valueAt(lt, d);
379   }
381   
382   Point operator() (double t) const
383   {
384           return pointAt(t);
385   }
386   
387   std::vector<double> roots(double v, Dim2 d) const {
388     std::vector<double> res;
389     for(unsigned i = 0; i <= size(); i++) {
390       std::vector<double> temp = (*this)[i].roots(v, d);
391       for(unsigned j = 0; j < temp.size(); j++)
392         res.push_back(temp[j] + i);
393     }
394     return res;
395   }
396   
397   std::vector<double> 
398   allNearestPoints(Point const& _point, double from, double to) const;
399   
400   std::vector<double>
401   allNearestPoints(Point const& _point) const
402   {
403           unsigned int sz = size();
404           if ( closed() ) ++sz;
405           return allNearestPoints(_point, 0, sz);
406   }
407   
408   
409   double nearestPoint(Point const& _point, double from, double to) const;
410   
411   double nearestPoint(Point const& _point) const
412   {
413           unsigned int sz = size();
414           if ( closed() ) ++sz;
415           return nearestPoint(_point, 0, sz);
416   }
417    
418   void appendPortionTo(Path &p, double f, double t) const;
420   Path portion(double f, double t) const {
421     Path ret;
422     ret.close(false);
423     appendPortionTo(ret, f, t);
424     return ret;
425   }
426   Path portion(Interval i) const { return portion(i.min(), i.max()); }
428   Path reverse() const {
429     Path ret;
430     ret.close(closed_);
431     for(int i = size() - (closed_ ? 0 : 1); i >= 0; i--) {
432       Curve *temp = (*this)[i].reverse();
433       ret.append(*temp);
434       // delete since append makes a copy
435       delete temp;
436     }
437     return ret;
438   }
440   void insert(iterator pos, Curve const &curve, Stitching stitching=NO_STITCHING) {
441     Sequence source(1, curve.duplicate());
442     try {
443       if (stitching) stitch(pos.impl_, pos.impl_, source);
444       do_update(pos.impl_, pos.impl_, source.begin(), source.end());
445     } catch (...) {
446       delete_range(source.begin(), source.end());
447       throw;
448     }
449   }
451   template <typename Impl>
452   void insert(iterator pos, BaseIterator<Impl> first, BaseIterator<Impl> last, Stitching stitching=NO_STITCHING)
453   {
454     Sequence source(DuplicatingIterator<Impl>(first.impl_),
455                     DuplicatingIterator<Impl>(last.impl_));
456     try {
457       if (stitching) stitch(pos.impl_, pos.impl_, source);
458       do_update(pos.impl_, pos.impl_, source.begin(), source.end());
459     } catch (...) {
460       delete_range(source.begin(), source.end());
461       throw;
462     }
463   }
465   void clear() {
466     do_update(curves_.begin(), curves_.end()-1,
467               curves_.begin(), curves_.begin());
468   }
470   void erase(iterator pos, Stitching stitching=NO_STITCHING) {
471     if (stitching) {
472       Sequence stitched;
473       stitch(pos.impl_, pos.impl_+1, stitched);
474       try {
475         do_update(pos.impl_, pos.impl_+1, stitched.begin(), stitched.end());
476       } catch (...) {
477         delete_range(stitched.begin(), stitched.end());
478         throw;
479       }
480     } else {
481       do_update(pos.impl_, pos.impl_+1, curves_.begin(), curves_.begin());
482     }
483   }
485   void erase(iterator first, iterator last, Stitching stitching=NO_STITCHING) {
486     if (stitching) {
487       Sequence stitched;
488       stitch(first.impl_, last.impl_, stitched);
489       try {
490         do_update(first.impl_, last.impl_, stitched.begin(), stitched.end());
491       } catch (...) {
492         delete_range(stitched.begin(), stitched.end());
493         throw;
494       }
495     } else {
496       do_update(first.impl_, last.impl_, curves_.begin(), curves_.begin());
497     }
498   }
500   // erase last segment of path
501   void erase_last() {
502     erase(curves_.end()-2);
503   }
505   void replace(iterator replaced, Curve const &curve, Stitching stitching=NO_STITCHING) {
506     Sequence source(1, curve.duplicate());
507     try {
508       if (stitching) stitch(replaced.impl_, replaced.impl_+1, source);
509       do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end());
510     } catch (...) {
511       delete_range(source.begin(), source.end());
512       throw;
513     }
514   }
516   void replace(iterator first_replaced, iterator last_replaced,
517                Curve const &curve, Stitching stitching=NO_STITCHING)
518   {
519     Sequence source(1, curve.duplicate());
520     try {
521       if (stitching) stitch(first_replaced.impl_, last_replaced.impl_, source);
522       do_update(first_replaced.impl_, last_replaced.impl_,
523                 source.begin(), source.end());
524     } catch (...) {
525       delete_range(source.begin(), source.end());
526       throw;
527     }
528   }
530   template <typename Impl>
531   void replace(iterator replaced,
532                BaseIterator<Impl> first, BaseIterator<Impl> last,
533                Stitching stitching=NO_STITCHING)
534   {
535     Sequence source(DuplicatingIterator<Impl>(first.impl_),
536                     DuplicatingIterator<Impl>(last.impl_));
537     try {
538       if (stitching) stitch(replaced.impl_, replaced.impl_+1, source);
539       do_update(replaced.impl_, replaced.impl_+1, source.begin(), source.end());
540     } catch (...) {
541       delete_range(source.begin(), source.end());
542       throw;
543     }
544   }
546   template <typename Impl>
547   void replace(iterator first_replaced, iterator last_replaced,
548                BaseIterator<Impl> first, BaseIterator<Impl> last,
549                Stitching stitching=NO_STITCHING)
550   {
551     Sequence source(first.impl_, last.impl_);
552     try {
553       if (stitching) stitch(first_replaced.impl_, last_replaced.impl_, source);
554       do_update(first_replaced.impl_, last_replaced.impl_,
555                 source.begin(), source.end());
556     } catch (...) {
557       delete_range(source.begin(), source.end());
558       throw;
559     }
560   }
562   void start(Point p) {
563     clear();
564     final_->setPoint(0, p);
565     final_->setPoint(1, p);
566   }
568   Point initialPoint() const { return (*final_)[1]; }
569   Point finalPoint() const { return (*final_)[0]; }
571   void setInitial(Point const& p)
572   {
573           if ( empty() ) return;
574           Curve* head = front().duplicate();
575           head->setInitial(p);
576           Sequence::iterator replaced = curves_.begin();
577           Sequence source(1, head);
578           try 
579           {
580                   do_update(replaced, replaced + 1, source.begin(), source.end());
581           } 
582           catch (...) 
583           {
584                   delete_range(source.begin(), source.end());
585                   throw;
586           }
587   }
589   void setFinal(Point const& p)
590   {
591           if ( empty() ) return;
592           Curve* tail = back().duplicate();
593           tail->setFinal(p);
594           Sequence::iterator replaced = curves_.end() - 2;
595           Sequence source(1, tail);
596           try 
597           {
598                   do_update(replaced, replaced + 1, source.begin(), source.end());
599           } 
600           catch (...) 
601           {
602                   delete_range(source.begin(), source.end());
603                   throw;
604           }      
605   }
607   void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
608     if (stitching) stitchTo(curve.initialPoint());
609     do_append(curve.duplicate());
610   }
611   void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
612     if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
613     do_append(new SBasisCurve(curve));
614   }
615   void append(Path const &other, Stitching stitching=NO_STITCHING) {
616     insert(end(), other.begin(), other.end(), stitching);
617   }
619   void stitchTo(Point const &p) {
620     if (!empty() && finalPoint() != p) {
621       do_append(new StitchSegment(finalPoint(), p));
622     }
623   }
625   template <typename CurveType, typename A>
626   void appendNew(A a) {
627     do_append(new CurveType(finalPoint(), a));
628   }
630   template <typename CurveType, typename A, typename B>
631   void appendNew(A a, B b) {
632     do_append(new CurveType(finalPoint(), a, b));
633   }
635   template <typename CurveType, typename A, typename B, typename C>
636   void appendNew(A a, B b, C c) {
637     do_append(new CurveType(finalPoint(), a, b, c));
638   }
640   template <typename CurveType, typename A, typename B, typename C,
641                                 typename D>
642   void appendNew(A a, B b, C c, D d) {
643     do_append(new CurveType(finalPoint(), a, b, c, d));
644   }
646   template <typename CurveType, typename A, typename B, typename C,
647                                 typename D, typename E>
648   void appendNew(A a, B b, C c, D d, E e) {
649     do_append(new CurveType(finalPoint(), a, b, c, d, e));
650   }
652   template <typename CurveType, typename A, typename B, typename C,
653                                 typename D, typename E, typename F>
654   void appendNew(A a, B b, C c, D d, E e, F f) {
655     do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
656   }
658   template <typename CurveType, typename A, typename B, typename C,
659                                 typename D, typename E, typename F,
660                                 typename G>
661   void appendNew(A a, B b, C c, D d, E e, F f, G g) {
662     do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
663   }
665   template <typename CurveType, typename A, typename B, typename C,
666                                 typename D, typename E, typename F,
667                                 typename G, typename H>
668   void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
669     do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
670   }
672   template <typename CurveType, typename A, typename B, typename C,
673                                 typename D, typename E, typename F,
674                                 typename G, typename H, typename I>
675   void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
676     do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
677   }
679 private:
680   void stitch(Sequence::iterator first_replaced,
681               Sequence::iterator last_replaced,
682               Sequence &sequence);
684   void do_update(Sequence::iterator first_replaced,
685                  Sequence::iterator last_replaced,
686                  Sequence::iterator first,
687                  Sequence::iterator last);
689   void do_append(Curve *curve);
691   static void delete_range(Sequence::iterator first, Sequence::iterator last);
693   void check_continuity(Sequence::iterator first_replaced,
694                         Sequence::iterator last_replaced,
695                         Sequence::iterator first,
696                         Sequence::iterator last);
698   Sequence curves_;
699   ClosingSegment *final_;
700   bool closed_;
701   
702 };  // end class Path
704 inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
705     Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
706     for(unsigned i = 1; i < paths.size(); i++) {
707         ret.concat(paths[i].toPwSb());
708     }
709     return ret;
712 inline
713 Coord nearest_point(Point const& p, Path const& c)
715         return c.nearestPoint(p);
718 }  // end namespace Geom
720 namespace std {
722 template <>
723 inline void swap<Geom::Path>(Geom::Path &a, Geom::Path &b)
725   a.swap(b);
728 }  // end namespace std
731 #endif // SEEN_GEOM_PATH_H
736 /*
737   Local Variables:
738   mode:c++
739   c-file-style:"stroustrup"
740   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
741   indent-tabs-mode:nil
742   fill-column:99
743   End:
744 */
745 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :