3
* \brief Path - Series of continuous curves
6
* MenTaLguY <mental@rydia.net>
7
* Marco Cecchetti <mrcekets at gmail.com>
9
* Copyright 2007-2008 authors
11
* This library is free software; you can redistribute it and/or
12
* modify it either under the terms of the GNU Lesser General Public
13
* License version 2.1 as published by the Free Software Foundation
14
* (the "LGPL") or, at your option, under the terms of the Mozilla
15
* Public License Version 1.1 (the "MPL"). If you do not alter this
16
* notice, a recipient may use your version of this file under either
17
* the MPL or the LGPL.
19
* You should have received a copy of the LGPL along with this library
20
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
21
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
* You should have received a copy of the MPL along with this library
23
* in the file COPYING-MPL-1.1
25
* The contents of this file are subject to the Mozilla Public License
26
* Version 1.1 (the "License"); you may not use this file except in
27
* compliance with the License. You may obtain a copy of the License at
28
* http://www.mozilla.org/MPL/
30
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
31
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
32
* the specific language governing rights and limitations.
38
#ifndef SEEN_GEOM_PATH_H
39
#define SEEN_GEOM_PATH_H
42
#include <boost/shared_ptr.hpp>
43
#include <2geom/curve.h>
44
#include <2geom/bezier-curve.h>
55
namespace PathInternal {
57
typedef std::vector<boost::shared_ptr<Curve const> > Sequence;
59
template <typename C, typename P>
62
BaseIterator() : path(NULL), index(0) {}
63
BaseIterator(P *p, unsigned i) : path(p), index(i) {}
64
// default copy, default assign
67
bool operator==(BaseIterator const &other) {
68
return path == other.path && index == other.index;
70
bool operator!=(BaseIterator const &other) {
71
return path != other.path || index != other.index;
74
Curve const &operator*() const { return (*path)[index]; }
75
Curve const *operator->() const { return &(*path)[index]; }
76
boost::shared_ptr<Curve const> get_ref() const {
77
return path->get_ref_at_index(index);
82
return static_cast<C &>(*this);
85
C old(static_cast<C &>(*this));
92
return static_cast<C &>(*this);
95
C old(static_cast<C &>(*this));
104
friend class ::Geom::Path;
107
class ConstIterator : public BaseIterator<ConstIterator, Path const> {
109
typedef BaseIterator<ConstIterator, Path const> Base;
111
ConstIterator() : Base() {}
112
// default copy, default assign
115
ConstIterator(Path const &p, unsigned i) : Base(&p, i) {}
116
friend class ::Geom::Path;
119
class Iterator : public BaseIterator<Iterator, Path> {
121
typedef BaseIterator<Iterator, Path> Base;
123
Iterator() : Base() {}
124
// default copy, default assign
126
operator ConstIterator const &() const {
127
return reinterpret_cast<ConstIterator const &>(*this);
131
Iterator(Path &p, unsigned i) : Base(&p, i) {}
132
friend class ::Geom::Path;
138
* Open and closed paths: all paths, whether open or closed, store a final
139
* segment which connects the initial and final endpoints of the "real"
140
* path data. While similar to the "z" in an SVG path, it exists for
141
* both open and closed paths, and is not considered part of the "normal"
142
* path data, which is always covered by the range [begin(), end_open()).
143
* Conversely, the range [begin(), end_closed()) always contains the "extra"
146
* The only difference between a closed and an open path is whether
147
* end_default() returns end_closed() or end_open(). The idea behind this
148
* is to let any path be stroked using [begin(), end_default()), and filled
149
* using [begin(), end_closed()), without requiring a separate "filled" version
150
* of the path to use for filling.
152
* \invariant : curves_ always contains at least one segment. The last segment
153
* is always of type ClosingSegment. All constructors take care of this.
154
(curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back())
158
typedef PathInternal::Sequence Sequence;
159
typedef PathInternal::Iterator iterator;
160
typedef PathInternal::ConstIterator const_iterator;
161
typedef Sequence::size_type size_type;
162
typedef Sequence::difference_type difference_type;
164
class ClosingSegment : public LineSegment {
166
ClosingSegment() : LineSegment() {}
167
ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
168
virtual Curve *duplicate() const { return new ClosingSegment(*this); }
169
virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
177
class StitchSegment : public LineSegment {
179
StitchSegment() : LineSegment() {}
180
StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
181
virtual Curve *duplicate() const { return new StitchSegment(*this); }
182
virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
185
// Path(Path const &other) - use default copy constructor
187
explicit Path(Point p=Point())
188
: curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))),
189
final_(new ClosingSegment(p, p)),
192
get_curves().back() = boost::shared_ptr<Curve>(final_);
195
Path(const_iterator const &first,
196
const_iterator const &last,
198
: curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first),
202
if (!get_curves().empty()) {
203
final_ = new ClosingSegment(get_curves().back()->finalPoint(),
204
get_curves().front()->initialPoint());
206
final_ = new ClosingSegment();
208
get_curves().push_back(boost::shared_ptr<Curve>(final_));
213
// Path &operator=(Path const &other) - use default assignment operator
215
void swap(Path &other) {
216
std::swap(other.curves_, curves_);
217
std::swap(other.final_, final_);
218
std::swap(other.closed_, closed_);
221
Curve const &operator[](unsigned i) const { return *get_curves()[i]; }
222
Curve const &at_index(unsigned i) const { return *get_curves()[i]; }
223
boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) {
224
return get_curves()[i];
227
Curve const &front() const { return *get_curves()[0]; }
228
Curve const &back() const { return back_open(); }
229
Curve const &back_open() const {
230
if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); }
231
return *get_curves()[get_curves().size()-2];
233
Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; }
234
Curve const &back_default() const {
235
return ( closed_ ? back_closed() : back_open() );
238
const_iterator begin() const { return const_iterator(*this, 0); }
239
const_iterator end() const { return const_iterator(*this, size()); }
240
iterator begin() { return iterator(*this, 0); }
241
iterator end() { return iterator(*this, size()); }
243
const_iterator end_open() const { return const_iterator(*this, size()); }
244
const_iterator end_closed() const { return const_iterator(*this, size()+1); }
245
const_iterator end_default() const {
246
return ( closed_ ? end_closed() : end_open() );
249
size_type size_open() const { return get_curves().size()-1; }
250
size_type size_closed() const { return get_curves().size(); }
251
size_type size_default() const {
252
return ( closed_ ? size_closed() : size_open() );
254
size_type size() const { return size_open(); }
256
size_type max_size() const { return get_curves().max_size()-1; }
258
bool empty() const { return (get_curves().size() == 1); }
259
bool closed() const { return closed_; }
260
void close(bool closed=true) { closed_ = closed; }
262
OptRect boundsFast() const;
263
OptRect boundsExact() const;
265
Piecewise<D2<SBasis> > toPwSb() const {
266
Piecewise<D2<SBasis> > ret;
269
bool degenerate = true;
270
// pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
271
for(const_iterator it = begin(); it != end_default(); ++it) {
272
if (!it->isDegenerate()) {
273
ret.push(it->toSBasis(), i++);
278
// if path only contains degenerate curves, no second cut is added
279
// so we need to create at least one segment manually
280
ret = Piecewise<D2<SBasis> >(initialPoint());
285
bool operator==(Path const &other) const {
286
if (this == &other) return true;
287
if (closed_ != other.closed_) return false;
288
return get_curves() == other.get_curves();
290
bool operator!=(Path const &other) const {
291
return !( *this == other );
294
Path operator*(Matrix const &m) const {
300
Path &operator*=(Matrix const &m);
302
Point pointAt(double t) const
304
unsigned int sz = size();
305
if ( closed() ) ++sz;
306
if ( t < 0 || t > sz )
308
THROW_RANGEERROR("parameter t out of bounds");
310
if ( empty() ) return initialPoint(); // naked moveto
311
double k, lt = modf(t, &k);
312
unsigned int i = static_cast<unsigned int>(k);
318
return (*this)[i].pointAt(lt);
321
double valueAt(double t, Dim2 d) const
323
unsigned int sz = size();
324
if ( closed() ) ++sz;
325
if ( t < 0 || t > sz )
327
THROW_RANGEERROR("parameter t out of bounds");
329
if ( empty() ) return initialPoint()[d]; // naked moveto
330
double k, lt = modf(t, &k);
331
unsigned int i = static_cast<unsigned int>(k);
337
return (*this)[i].valueAt(lt, d);
341
Point operator() (double t) const
346
std::vector<double> roots(double v, Dim2 d) const {
347
std::vector<double> res;
348
for(unsigned i = 0; i <= size(); i++) {
349
std::vector<double> temp = (*this)[i].roots(v, d);
350
for(unsigned j = 0; j < temp.size(); j++)
351
res.push_back(temp[j] + i);
357
allNearestPoints(Point const& _point, double from, double to) const;
360
allNearestPoints(Point const& _point) const
362
unsigned int sz = size();
363
if ( closed() ) ++sz;
364
return allNearestPoints(_point, 0, sz);
368
nearestPointPerCurve(Point const& _point) const;
370
double nearestPoint(Point const& _point, double from, double to, double *distance_squared = NULL) const;
372
double nearestPoint(Point const& _point, double *distance_squared = NULL) const
374
unsigned int sz = size();
375
if ( closed() ) ++sz;
376
return nearestPoint(_point, 0, sz, distance_squared);
379
void appendPortionTo(Path &p, double f, double t) const;
381
Path portion(double f, double t) const {
384
appendPortionTo(ret, f, t);
387
Path portion(Interval i) const { return portion(i.min(), i.max()); }
389
Path reverse() const {
392
for ( Sequence::iterator iter = ret.get_curves().begin() ;
393
iter != ret.get_curves().end()-1 ; ++iter )
395
*iter = boost::shared_ptr<Curve>((*iter)->reverse());
397
std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1);
398
ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse());
399
ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_);
403
void insert(iterator const &pos,
404
Curve const &curve, Stitching stitching=NO_STITCHING)
407
Sequence::iterator seq_pos(seq_iter(pos));
408
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
409
if (stitching) stitch(seq_pos, seq_pos, source);
410
do_update(seq_pos, seq_pos, source.begin(), source.end());
413
void insert(iterator const &pos,
414
const_iterator const &first,
415
const_iterator const &last,
416
Stitching stitching=NO_STITCHING)
419
Sequence::iterator seq_pos(seq_iter(pos));
420
Sequence source(seq_iter(first), seq_iter(last));
421
if (stitching) stitch(seq_pos, seq_pos, source);
422
do_update(seq_pos, seq_pos, source.begin(), source.end());
427
do_update(get_curves().begin(), get_curves().end()-1,
428
get_curves().begin(), get_curves().begin());
431
void erase(iterator const &pos, Stitching stitching=NO_STITCHING) {
433
Sequence::iterator seq_pos(seq_iter(pos));
436
stitch(seq_pos, seq_pos+1, stitched);
437
do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end());
439
do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin());
443
void erase(iterator const &first,
444
iterator const &last,
445
Stitching stitching=NO_STITCHING)
448
Sequence::iterator seq_first=seq_iter(first);
449
Sequence::iterator seq_last=seq_iter(last);
452
stitch(seq_first, seq_last, stitched);
453
do_update(seq_first, seq_last, stitched.begin(), stitched.end());
455
do_update(seq_first, seq_last,
456
get_curves().begin(), get_curves().begin());
460
// erase last segment of path
462
erase(iterator(*this, size()-1));
465
void replace(iterator const &replaced,
467
Stitching stitching=NO_STITCHING)
470
Sequence::iterator seq_replaced(seq_iter(replaced));
471
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
472
if (stitching) stitch(seq_replaced, seq_replaced+1, source);
473
do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
476
void replace(iterator const &first_replaced,
477
iterator const &last_replaced,
478
Curve const &curve, Stitching stitching=NO_STITCHING)
481
Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
482
Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
483
Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
484
if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
485
do_update(seq_first_replaced, seq_last_replaced,
486
source.begin(), source.end());
489
void replace(iterator const &replaced,
490
const_iterator const &first,
491
const_iterator const &last,
492
Stitching stitching=NO_STITCHING)
495
Sequence::iterator seq_replaced(seq_iter(replaced));
496
Sequence source(seq_iter(first), seq_iter(last));
497
if (stitching) stitch(seq_replaced, seq_replaced+1, source);
498
do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
501
void replace(iterator const &first_replaced,
502
iterator const &last_replaced,
503
const_iterator const &first,
504
const_iterator const &last,
505
Stitching stitching=NO_STITCHING)
508
Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
509
Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
510
Sequence source(seq_iter(first), seq_iter(last));
511
if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
512
do_update(seq_first_replaced, seq_last_replaced,
513
source.begin(), source.end());
516
void start(Point p) {
518
final_->setPoint(0, p);
519
final_->setPoint(1, p);
522
Point initialPoint() const { return (*final_)[1]; }
523
Point finalPoint() const { return (*final_)[0]; }
525
void setInitial(Point const& p)
527
if ( empty() ) return;
529
boost::shared_ptr<Curve> head(front().duplicate());
531
Sequence::iterator replaced = get_curves().begin();
532
Sequence source(1, head);
533
do_update(replaced, replaced + 1, source.begin(), source.end());
536
void setFinal(Point const& p)
538
if ( empty() ) return;
540
boost::shared_ptr<Curve> tail(back().duplicate());
542
Sequence::iterator replaced = get_curves().end() - 2;
543
Sequence source(1, tail);
544
do_update(replaced, replaced + 1, source.begin(), source.end());
547
void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
549
if (stitching) stitchTo(curve.initialPoint());
550
do_append(curve.duplicate());
552
void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
554
if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
555
do_append(new SBasisCurve(curve));
557
void append(Path const &other, Stitching stitching=NO_STITCHING) {
558
insert(end(), other.begin(), other.end(), stitching);
561
void stitchTo(Point const &p) {
562
if (!empty() && finalPoint() != p) {
564
do_append(new StitchSegment(finalPoint(), p));
570
* It is important to note that the coordinates passed to appendNew should be finite!
571
* If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
574
template <typename CurveType, typename A>
575
void appendNew(A a) {
577
do_append(new CurveType(finalPoint(), a));
580
template <typename CurveType, typename A, typename B>
581
void appendNew(A a, B b) {
583
do_append(new CurveType(finalPoint(), a, b));
586
template <typename CurveType, typename A, typename B, typename C>
587
void appendNew(A a, B b, C c) {
589
do_append(new CurveType(finalPoint(), a, b, c));
592
template <typename CurveType, typename A, typename B, typename C,
594
void appendNew(A a, B b, C c, D d) {
596
do_append(new CurveType(finalPoint(), a, b, c, d));
599
template <typename CurveType, typename A, typename B, typename C,
600
typename D, typename E>
601
void appendNew(A a, B b, C c, D d, E e) {
603
do_append(new CurveType(finalPoint(), a, b, c, d, e));
606
template <typename CurveType, typename A, typename B, typename C,
607
typename D, typename E, typename F>
608
void appendNew(A a, B b, C c, D d, E e, F f) {
610
do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
613
template <typename CurveType, typename A, typename B, typename C,
614
typename D, typename E, typename F,
616
void appendNew(A a, B b, C c, D d, E e, F f, G g) {
618
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
621
template <typename CurveType, typename A, typename B, typename C,
622
typename D, typename E, typename F,
623
typename G, typename H>
624
void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
626
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
629
template <typename CurveType, typename A, typename B, typename C,
630
typename D, typename E, typename F,
631
typename G, typename H, typename I>
632
void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
634
do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
638
static Sequence::iterator seq_iter(iterator const &iter) {
639
return iter.path->get_curves().begin() + iter.index;
641
static Sequence::const_iterator seq_iter(const_iterator const &iter) {
642
return iter.path->get_curves().begin() + iter.index;
645
Sequence &get_curves() { return *curves_; }
646
Sequence const &get_curves() const { return *curves_; }
649
if (!curves_.unique()) {
650
curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_));
652
if (!get_curves().back().unique()) {
653
final_ = static_cast<ClosingSegment *>(final_->duplicate());
654
get_curves().back() = boost::shared_ptr<Curve>(final_);
658
void stitch(Sequence::iterator first_replaced,
659
Sequence::iterator last_replaced,
662
void do_update(Sequence::iterator first_replaced,
663
Sequence::iterator last_replaced,
664
Sequence::iterator first,
665
Sequence::iterator last);
667
// n.b. takes ownership of curve object
668
void do_append(Curve *curve);
670
void check_continuity(Sequence::iterator first_replaced,
671
Sequence::iterator last_replaced,
672
Sequence::iterator first,
673
Sequence::iterator last);
675
boost::shared_ptr<Sequence> curves_;
676
ClosingSegment *final_;
680
inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
681
Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
682
for(unsigned i = 1; i < paths.size(); i++) {
683
ret.concat(paths[i].toPwSb());
689
Coord nearest_point(Point const& p, Path const& c)
691
return c.nearestPoint(p);
694
} // end namespace Geom
699
inline void swap<Geom::Path>(Geom::Path &a, Geom::Path &b)
704
} // end namespace std
706
#endif // SEEN_GEOM_PATH_H
714
c-file-style:"stroustrup"
715
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
720
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :