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 <boost/shared_ptr.hpp>
42 #include <2geom/curves.h>
44 #include <iterator>
45 #include <algorithm>
48 namespace Geom
49 {
51 class Path;
53 namespace PathInternal {
55 typedef std::vector<boost::shared_ptr<Curve const> > Sequence;
57 template <typename C, typename P>
58 class BaseIterator {
59 protected:
60 BaseIterator() : path(NULL), index(0) {}
61 BaseIterator(P *p, unsigned i) : path(p), index(i) {}
62 // default copy, default assign
64 public:
65 bool operator==(BaseIterator const &other) {
66 return path == other.path && index == other.index;
67 }
68 bool operator!=(BaseIterator const &other) {
69 return path != other.path || index != other.index;
70 }
72 Curve const &operator*() const { return (*path)[index]; }
73 Curve const *operator->() const { return &(*path)[index]; }
74 boost::shared_ptr<Curve const> get_ref() const {
75 return path->get_ref_at_index(index);
76 }
78 C &operator++() {
79 ++index;
80 return static_cast<C &>(*this);
81 }
82 C operator++(int) {
83 C old(static_cast<C &>(*this));
84 ++(*this);
85 return old;
86 }
88 C &operator--() {
89 --index;
90 return static_cast<C &>(*this);
91 }
92 C operator--(int) {
93 C old(static_cast<C &>(*this));
94 --(*this);
95 return old;
96 }
98 private:
99 P *path;
100 unsigned index;
102 friend class ::Geom::Path;
103 };
105 class ConstIterator : public BaseIterator<ConstIterator, Path const> {
106 public:
107 typedef BaseIterator<ConstIterator, Path const> Base;
109 ConstIterator() : Base() {}
110 // default copy, default assign
112 private:
113 ConstIterator(Path const &p, unsigned i) : Base(&p, i) {}
114 friend class ::Geom::Path;
115 };
117 class Iterator : public BaseIterator<Iterator, Path> {
118 public:
119 typedef BaseIterator<Iterator, Path> Base;
121 Iterator() : Base() {}
122 // default copy, default assign
124 operator ConstIterator const &() const {
125 return reinterpret_cast<ConstIterator const &>(*this);
126 }
128 private:
129 Iterator(Path &p, unsigned i) : Base(&p, i) {}
130 friend class ::Geom::Path;
131 };
133 }
135 /*
136 * Open and closed paths: all paths, whether open or closed, store a final
137 * segment which connects the initial and final endpoints of the "real"
138 * path data. While similar to the "z" in an SVG path, it exists for
139 * both open and closed paths, and is not considered part of the "normal"
140 * path data, which is always covered by the range [begin(), end_open()).
141 * Conversely, the range [begin(), end_closed()) always contains the "extra"
142 * closing segment.
143 *
144 * The only difference between a closed and an open path is whether
145 * end_default() returns end_closed() or end_open(). The idea behind this
146 * is to let any path be stroked using [begin(), end_default()), and filled
147 * using [begin(), end_closed()), without requiring a separate "filled" version
148 * of the path to use for filling.
149 *
150 * \invariant : curves_ always contains at least one segment. The last segment
151 * is always of type ClosingSegment. All constructors take care of this.
152 (curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back())
153 */
154 class Path {
155 public:
156 typedef PathInternal::Sequence Sequence;
157 typedef PathInternal::Iterator iterator;
158 typedef PathInternal::ConstIterator const_iterator;
159 typedef Sequence::size_type size_type;
160 typedef Sequence::difference_type difference_type;
162 class ClosingSegment : public LineSegment {
163 public:
164 ClosingSegment() : LineSegment() {}
165 ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
166 virtual Curve *duplicate() const { return new ClosingSegment(*this); }
167 virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
168 };
170 enum Stitching {
171 NO_STITCHING=0,
172 STITCH_DISCONTINUOUS
173 };
175 class StitchSegment : public LineSegment {
176 public:
177 StitchSegment() : LineSegment() {}
178 StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
179 virtual Curve *duplicate() const { return new StitchSegment(*this); }
180 virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
181 };
183 // Path(Path const &other) - use default copy constructor
185 explicit Path(Point p=Point())
186 : curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))),
187 final_(new ClosingSegment(p, p)),
188 closed_(false)
189 {
190 get_curves().back() = boost::shared_ptr<Curve>(final_);
191 }
193 Path(const_iterator const &first,
194 const_iterator const &last,
195 bool closed=false)
196 : curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first),
197 seq_iter(last)))),
198 closed_(closed)
199 {
200 if (!get_curves().empty()) {
201 final_ = new ClosingSegment(get_curves().back()->finalPoint(),
202 get_curves().front()->initialPoint());
203 } else {
204 final_ = new ClosingSegment();
205 }
206 get_curves().push_back(boost::shared_ptr<Curve>(final_));
207 }
209 virtual ~Path() {}
211 // Path &operator=(Path const &other) - use default assignment operator
213 void swap(Path &other) {
214 std::swap(other.curves_, curves_);
215 std::swap(other.final_, final_);
216 std::swap(other.closed_, closed_);
217 }
219 Curve const &operator[](unsigned i) const { return *get_curves()[i]; }
220 Curve const &at_index(unsigned i) const { return *get_curves()[i]; }
221 boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) {
222 return get_curves()[i];
223 }
225 Curve const &front() const { return *get_curves()[0]; }
226 Curve const &back() const { return back_open(); }
227 Curve const &back_open() const {
228 if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); }
229 return *get_curves()[get_curves().size()-2];
230 }
231 Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; }
232 Curve const &back_default() const {
233 return ( closed_ ? back_closed() : back_open() );
234 }
236 const_iterator begin() const { return const_iterator(*this, 0); }
237 const_iterator end() const { return const_iterator(*this, size()); }
238 iterator begin() { return iterator(*this, 0); }
239 iterator end() { return iterator(*this, size()); }
241 const_iterator end_open() const { return const_iterator(*this, size()); }
242 const_iterator end_closed() const { return const_iterator(*this, size()+1); }
243 const_iterator end_default() const {
244 return ( closed_ ? end_closed() : end_open() );
245 }
247 size_type size() const { return get_curves().size()-1; }
248 size_type max_size() const { return get_curves().max_size()-1; }
250 bool empty() const { return get_curves().size() == 1; }
251 bool closed() const { return closed_; }
252 void close(bool closed=true) { closed_ = closed; }
254 Rect boundsFast() const;
255 Rect boundsExact() const;
257 Piecewise<D2<SBasis> > toPwSb() const {
258 Piecewise<D2<SBasis> > ret;
259 ret.push_cut(0);
260 unsigned i = 1;
261 bool degenerate = true;
262 // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
263 for(const_iterator it = begin(); it != end_default(); ++it) {
264 if (!it->isDegenerate()) {
265 ret.push(it->toSBasis(), i++);
266 degenerate = false;
267 }
268 }
269 if (degenerate) {
270 // if path only contains degenerate curves, no second cut is added
271 // so we need to create at least one segment manually
272 ret = Piecewise<D2<SBasis> >(initialPoint());
273 }
274 return ret;
275 }
277 bool operator==(Path const &other) const {
278 if (this == &other) return true;
279 if (closed_ != other.closed_) return false;
280 return get_curves() == other.get_curves();
281 }
282 bool operator!=(Path const &other) const {
283 return !( *this == other );
284 }
286 Path operator*(Matrix const &m) const {
287 Path ret(*this);
288 ret *= m;
289 return ret;
290 }
292 Path &operator*=(Matrix const &m);
294 Point pointAt(double t) const
295 {
296 unsigned int sz = size();
297 if ( closed() ) ++sz;
298 if ( t < 0 || t > sz )
299 {
300 THROW_RANGEERROR("parameter t out of bounds");
301 }
302 if ( empty() ) return Point(0,0);
303 double k, lt = modf(t, &k);
304 unsigned int i = static_cast<unsigned int>(k);
305 if ( i == sz )
306 {
307 --i;
308 lt = 1;
309 }
310 return (*this)[i].pointAt(lt);
311 }
313 double valueAt(double t, Dim2 d) const
314 {
315 unsigned int sz = size();
316 if ( closed() ) ++sz;
317 if ( t < 0 || t > sz )
318 {
319 THROW_RANGEERROR("parameter t out of bounds");
320 }
321 if ( empty() ) return 0;
322 double k, lt = modf(t, &k);
323 unsigned int i = static_cast<unsigned int>(k);
324 if ( i == sz )
325 {
326 --i;
327 lt = 1;
328 }
329 return (*this)[i].valueAt(lt, d);
330 }
333 Point operator() (double t) const
334 {
335 return pointAt(t);
336 }
338 std::vector<double> roots(double v, Dim2 d) const {
339 std::vector<double> res;
340 for(unsigned i = 0; i <= size(); i++) {
341 std::vector<double> temp = (*this)[i].roots(v, d);
342 for(unsigned j = 0; j < temp.size(); j++)
343 res.push_back(temp[j] + i);
344 }
345 return res;
346 }
348 std::vector<double>
349 allNearestPoints(Point const& _point, double from, double to) const;
351 std::vector<double>
352 allNearestPoints(Point const& _point) const
353 {
354 unsigned int sz = size();
355 if ( closed() ) ++sz;
356 return allNearestPoints(_point, 0, sz);
357 }
360 double nearestPoint(Point const& _point, double from, double to) const;
362 double nearestPoint(Point const& _point) const
363 {
364 unsigned int sz = size();
365 if ( closed() ) ++sz;
366 return nearestPoint(_point, 0, sz);
367 }
369 void appendPortionTo(Path &p, double f, double t) const;
371 Path portion(double f, double t) const {
372 Path ret;
373 ret.close(false);
374 appendPortionTo(ret, f, t);
375 return ret;
376 }
377 Path portion(Interval i) const { return portion(i.min(), i.max()); }
379 Path reverse() const {
380 Path ret(*this);
381 ret.unshare();
382 for ( Sequence::iterator iter = ret.get_curves().begin() ;
383 iter != ret.get_curves().end()-1 ; ++iter )
384 {
385 *iter = boost::shared_ptr<Curve>((*iter)->reverse());
386 }
387 std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1);
388 ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse());
389 ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_);
390 return ret;
391 }
393 void insert(iterator const &pos,
394 Curve const &curve, Stitching stitching=NO_STITCHING)
395 {
396 unshare();
397 Sequence::iterator seq_pos(seq_iter(pos));
398 Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
399 if (stitching) stitch(seq_pos, seq_pos, source);
400 do_update(seq_pos, seq_pos, source.begin(), source.end());
401 }
403 void insert(iterator const &pos,
404 const_iterator const &first,
405 const_iterator const &last,
406 Stitching stitching=NO_STITCHING)
407 {
408 unshare();
409 Sequence::iterator seq_pos(seq_iter(pos));
410 Sequence source(seq_iter(first), seq_iter(last));
411 if (stitching) stitch(seq_pos, seq_pos, source);
412 do_update(seq_pos, seq_pos, source.begin(), source.end());
413 }
415 void clear() {
416 unshare();
417 do_update(get_curves().begin(), get_curves().end()-1,
418 get_curves().begin(), get_curves().begin());
419 }
421 void erase(iterator const &pos, Stitching stitching=NO_STITCHING) {
422 unshare();
423 Sequence::iterator seq_pos(seq_iter(pos));
424 if (stitching) {
425 Sequence stitched;
426 stitch(seq_pos, seq_pos+1, stitched);
427 do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end());
428 } else {
429 do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin());
430 }
431 }
433 void erase(iterator const &first,
434 iterator const &last,
435 Stitching stitching=NO_STITCHING)
436 {
437 unshare();
438 Sequence::iterator seq_first=seq_iter(first);
439 Sequence::iterator seq_last=seq_iter(last);
440 if (stitching) {
441 Sequence stitched;
442 stitch(seq_first, seq_last, stitched);
443 do_update(seq_first, seq_last, stitched.begin(), stitched.end());
444 } else {
445 do_update(seq_first, seq_last,
446 get_curves().begin(), get_curves().begin());
447 }
448 }
450 // erase last segment of path
451 void erase_last() {
452 erase(iterator(*this, size()-1));
453 }
455 void replace(iterator const &replaced,
456 Curve const &curve,
457 Stitching stitching=NO_STITCHING)
458 {
459 unshare();
460 Sequence::iterator seq_replaced(seq_iter(replaced));
461 Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
462 if (stitching) stitch(seq_replaced, seq_replaced+1, source);
463 do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
464 }
466 void replace(iterator const &first_replaced,
467 iterator const &last_replaced,
468 Curve const &curve, Stitching stitching=NO_STITCHING)
469 {
470 unshare();
471 Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
472 Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
473 Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
474 if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
475 do_update(seq_first_replaced, seq_last_replaced,
476 source.begin(), source.end());
477 }
479 void replace(iterator const &replaced,
480 const_iterator const &first,
481 const_iterator const &last,
482 Stitching stitching=NO_STITCHING)
483 {
484 unshare();
485 Sequence::iterator seq_replaced(seq_iter(replaced));
486 Sequence source(seq_iter(first), seq_iter(last));
487 if (stitching) stitch(seq_replaced, seq_replaced+1, source);
488 do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
489 }
491 void replace(iterator const &first_replaced,
492 iterator const &last_replaced,
493 const_iterator const &first,
494 const_iterator const &last,
495 Stitching stitching=NO_STITCHING)
496 {
497 unshare();
498 Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
499 Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
500 Sequence source(seq_iter(first), seq_iter(last));
501 if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
502 do_update(seq_first_replaced, seq_last_replaced,
503 source.begin(), source.end());
504 }
506 void start(Point p) {
507 clear();
508 final_->setPoint(0, p);
509 final_->setPoint(1, p);
510 }
512 Point initialPoint() const { return (*final_)[1]; }
513 Point finalPoint() const { return (*final_)[0]; }
515 void setInitial(Point const& p)
516 {
517 if ( empty() ) return;
518 unshare();
519 boost::shared_ptr<Curve> head(front().duplicate());
520 head->setInitial(p);
521 Sequence::iterator replaced = get_curves().begin();
522 Sequence source(1, head);
523 do_update(replaced, replaced + 1, source.begin(), source.end());
524 }
526 void setFinal(Point const& p)
527 {
528 if ( empty() ) return;
529 unshare();
530 boost::shared_ptr<Curve> tail(back().duplicate());
531 tail->setFinal(p);
532 Sequence::iterator replaced = get_curves().end() - 2;
533 Sequence source(1, tail);
534 do_update(replaced, replaced + 1, source.begin(), source.end());
535 }
537 void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
538 unshare();
539 if (stitching) stitchTo(curve.initialPoint());
540 do_append(curve.duplicate());
541 }
542 void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
543 unshare();
544 if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
545 do_append(new SBasisCurve(curve));
546 }
547 void append(Path const &other, Stitching stitching=NO_STITCHING) {
548 insert(end(), other.begin(), other.end(), stitching);
549 }
551 void stitchTo(Point const &p) {
552 if (!empty() && finalPoint() != p) {
553 unshare();
554 do_append(new StitchSegment(finalPoint(), p));
555 }
556 }
558 template <typename CurveType, typename A>
559 void appendNew(A a) {
560 unshare();
561 do_append(new CurveType(finalPoint(), a));
562 }
564 template <typename CurveType, typename A, typename B>
565 void appendNew(A a, B b) {
566 unshare();
567 do_append(new CurveType(finalPoint(), a, b));
568 }
570 template <typename CurveType, typename A, typename B, typename C>
571 void appendNew(A a, B b, C c) {
572 unshare();
573 do_append(new CurveType(finalPoint(), a, b, c));
574 }
576 template <typename CurveType, typename A, typename B, typename C,
577 typename D>
578 void appendNew(A a, B b, C c, D d) {
579 unshare();
580 do_append(new CurveType(finalPoint(), a, b, c, d));
581 }
583 template <typename CurveType, typename A, typename B, typename C,
584 typename D, typename E>
585 void appendNew(A a, B b, C c, D d, E e) {
586 unshare();
587 do_append(new CurveType(finalPoint(), a, b, c, d, e));
588 }
590 template <typename CurveType, typename A, typename B, typename C,
591 typename D, typename E, typename F>
592 void appendNew(A a, B b, C c, D d, E e, F f) {
593 unshare();
594 do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
595 }
597 template <typename CurveType, typename A, typename B, typename C,
598 typename D, typename E, typename F,
599 typename G>
600 void appendNew(A a, B b, C c, D d, E e, F f, G g) {
601 unshare();
602 do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
603 }
605 template <typename CurveType, typename A, typename B, typename C,
606 typename D, typename E, typename F,
607 typename G, typename H>
608 void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
609 unshare();
610 do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
611 }
613 template <typename CurveType, typename A, typename B, typename C,
614 typename D, typename E, typename F,
615 typename G, typename H, typename I>
616 void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
617 unshare();
618 do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
619 }
621 private:
622 static Sequence::iterator seq_iter(iterator const &iter) {
623 return iter.path->get_curves().begin() + iter.index;
624 }
625 static Sequence::const_iterator seq_iter(const_iterator const &iter) {
626 return iter.path->get_curves().begin() + iter.index;
627 }
629 Sequence &get_curves() { return *curves_; }
630 Sequence const &get_curves() const { return *curves_; }
632 void unshare() {
633 if (!curves_.unique()) {
634 curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_));
635 }
636 if (!get_curves().back().unique()) {
637 final_ = static_cast<ClosingSegment *>(final_->duplicate());
638 get_curves().back() = boost::shared_ptr<Curve>(final_);
639 }
640 }
642 void stitch(Sequence::iterator first_replaced,
643 Sequence::iterator last_replaced,
644 Sequence &sequence);
646 void do_update(Sequence::iterator first_replaced,
647 Sequence::iterator last_replaced,
648 Sequence::iterator first,
649 Sequence::iterator last);
651 // n.b. takes ownership of curve object
652 void do_append(Curve *curve);
654 void check_continuity(Sequence::iterator first_replaced,
655 Sequence::iterator last_replaced,
656 Sequence::iterator first,
657 Sequence::iterator last);
659 boost::shared_ptr<Sequence> curves_;
660 ClosingSegment *final_;
661 bool closed_;
662 }; // end class Path
664 inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
665 Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
666 for(unsigned i = 1; i < paths.size(); i++) {
667 ret.concat(paths[i].toPwSb());
668 }
669 return ret;
670 }
672 inline
673 Coord nearest_point(Point const& p, Path const& c)
674 {
675 return c.nearestPoint(p);
676 }
678 } // end namespace Geom
680 namespace std {
682 template <>
683 inline void swap<Geom::Path>(Geom::Path &a, Geom::Path &b)
684 {
685 a.swap(b);
686 }
688 } // end namespace std
690 #endif // SEEN_GEOM_PATH_H
695 /*
696 Local Variables:
697 mode:c++
698 c-file-style:"stroustrup"
699 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
700 indent-tabs-mode:nil
701 fill-column:99
702 End:
703 */
704 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :