Code

Merge from trunk.
[inkscape.git] / src / display / curve.cpp
1 #define __CURVE_C__
3 /** \file
4  * Routines for SPCurve and for its Geom::PathVector
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   Johan Engelen
11  *
12  * Copyright (C) 2000 Lauris Kaplinski
13  * Copyright (C) 2000-2001 Ximian, Inc.
14  * Copyright (C) 2002 Lauris Kaplinski
15  * Copyright (C) 2008 Johan Engelen
16  *
17  * Released under GNU GPL
18  */
20 #include "display/curve.h"
22 #include <glib/gmessages.h>
23 #include <2geom/pathvector.h>
24 #include <2geom/sbasis-geometric.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include <2geom/point.h>
28 /* Constructors */
30 /**
31  * The returned curve's state is as if SPCurve::reset has just been called on it.
32  */
33 SPCurve::SPCurve()
34   : _refcount(1),
35     _pathv()
36 {
37     _pathv.clear();
38 }
40 SPCurve::SPCurve(Geom::PathVector const& pathv)
41   : _refcount(1),
42     _pathv(pathv)
43 {
44 }
46 SPCurve *
47 SPCurve::new_from_rect(Geom::Rect const &rect, bool all_four_sides)
48 {
49     SPCurve *c =  new SPCurve();
51     Geom::Point p = rect.corner(0);
52     c->moveto(p);
54     for (int i=3; i>=1; i--) {
55         c->lineto(rect.corner(i));
56     }
58     if (all_four_sides) {
59         // When _constrained_ snapping to a path, the 2geom::SimpleCrosser will be invoked which doesn't consider the closing segment.
60         // of a path. Consequently, in case we want to snap to for example the page border, we must provide all four sides of the
61         // rectangle explicitly
62         c->lineto(rect.corner(0));
63     } else {
64         // ... instead of just three plus a closing segment
65         c->closepath();
66     }
68     return c;
69 }
71 SPCurve::~SPCurve()
72 {
73 }
75 /* Methods */
77 void
78 SPCurve::set_pathvector(Geom::PathVector const & new_pathv)
79 {
80     _pathv = new_pathv;
81 }
83 Geom::PathVector const &
84 SPCurve::get_pathvector() const
85 {
86     return _pathv;
87 }
89 /*
90  * Returns the number of segments of all paths summed
91  * This count includes the closing line segment of a closed path.
92  */
93 guint
94 SPCurve::get_segment_count() const
95 {
96     guint nr = 0;
97     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
98         nr += (*it).size();
100         if (it->closed())   nr += 1;
101     }
102     return nr;
105 /**
106  * Increase _refcount of curve.
107  *
108  * \todo should this be shared with other refcounting code?
109  */
110 SPCurve *
111 SPCurve::ref()
113     _refcount += 1;
115     return this;
118 /**
119  * Decrease refcount of curve, with possible destruction.
120  *
121  * \todo should this be shared with other refcounting code?
122  */
123 SPCurve *
124 SPCurve::unref()
126     _refcount -= 1;
128     if (_refcount < 1) {
129         delete this;
130     }
132     return NULL;
135 /**
136  * Create new curve from this curve's pathvector array.
137  */
138 SPCurve *
139 SPCurve::copy() const
141     return new SPCurve(_pathv);
144 /**
145  * Return new curve that is the concatenation of all curves in list.
146  */
147 SPCurve *
148 SPCurve::concat(GSList const *list)
150     SPCurve *new_curve = new SPCurve();
152     for (GSList const *l = list; l != NULL; l = l->next) {
153         SPCurve *c = (SPCurve *) l->data;
154         new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() );
155     }
157     return new_curve;
160 /**
161  * Returns a list of new curves corresponding to the subpaths in \a curve.
162  * 2geomified
163  */
164 GSList *
165 SPCurve::split() const
167     GSList *l = NULL;
169     for (Geom::PathVector::const_iterator path_it = _pathv.begin(); path_it != _pathv.end(); ++path_it) {
170         Geom::PathVector newpathv;
171         newpathv.push_back(*path_it);
172         SPCurve * newcurve = new SPCurve(newpathv);
173         l = g_slist_prepend(l, newcurve);
174     }
176     return l;
179 /**
180  * Transform all paths in curve using matrix.
181  */
182 void
183 SPCurve::transform(Geom::Matrix const &m)
185     _pathv *= m;
188 /**
189  * Set curve to empty curve.
190  * In more detail: this clears the internal pathvector from all its paths.
191  */
192 void
193 SPCurve::reset()
195     _pathv.clear();
198 /** Several consecutive movetos are ALLOWED
199  *  Ref: http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
200  * (first subitem of the item about zero-length path segments) */
202 /**
203  * Calls SPCurve::moveto() with point made of given coordinates.
204  */
205 void
206 SPCurve::moveto(gdouble x, gdouble y)
208     moveto(Geom::Point(x, y));
210 /**
211  * Perform a moveto to a point, thus starting a new subpath.
212  * Point p must be finite.
213  */
214 void
215 SPCurve::moveto(Geom::Point const &p)
217     _pathv.push_back( Geom::Path() );  // for some reason Geom::Path(p) does not work...
218     _pathv.back().start(p);
221 /**
222  * Adds a line to the current subpath.
223  * Point p must be finite.
224  */
225 void
226 SPCurve::lineto(Geom::Point const &p)
228     if (_pathv.empty())  g_message("SPCurve::lineto - path is empty!");
229     else _pathv.back().appendNew<Geom::LineSegment>( p );
231 /**
232  * Calls SPCurve::lineto( Geom::Point(x,y) )
233  */
234 void
235 SPCurve::lineto(gdouble x, gdouble y)
237     lineto(Geom::Point(x,y));
240 /**
241  * Adds a quadratic bezier segment to the current subpath.
242  * All points must be finite.
243  */
244 void
245 SPCurve::quadto(Geom::Point const &p1, Geom::Point const &p2)
247     if (_pathv.empty())  g_message("SPCurve::quadto - path is empty!");
248     else _pathv.back().appendNew<Geom::QuadraticBezier>( p1, p2);
250 /**
251  * Calls SPCurve::quadto( Geom::Point(x1,y1), Geom::Point(x2,y2) )
252  * All coordinates must be finite.
253  */
254 void
255 SPCurve::quadto(gdouble x1, gdouble y1, gdouble x2, gdouble y2)
257     quadto( Geom::Point(x1,y1), Geom::Point(x2,y2) );
260 /**
261  * Adds a bezier segment to the current subpath.
262  * All points must be finite.
263  */
264 void
265 SPCurve::curveto(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2)
267     if (_pathv.empty())  g_message("SPCurve::curveto - path is empty!");
268     else _pathv.back().appendNew<Geom::CubicBezier>( p0, p1, p2 );
270 /**
271  * Calls SPCurve::curveto( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) )
272  * All coordinates must be finite.
273  */
274 void
275 SPCurve::curveto(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
277     curveto( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
280 /**
281  * Close current subpath by possibly adding a line between start and end.
282  */
283 void
284 SPCurve::closepath()
286     _pathv.back().close(true);
289 /** Like SPCurve::closepath() but sets the end point of the last subpath
290     to the subpath start point instead of adding a new lineto.
292     Used for freehand drawing when the user draws back to the start point.
293 **/
294 void
295 SPCurve::closepath_current()
297     if (_pathv.back().size() > 0 && dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back_open())) {
298         _pathv.back().erase_last();
299     } else {
300         _pathv.back().setFinal(_pathv.back().initialPoint());
301     }
302     _pathv.back().close(true);
305 /**
306  * True if no paths are in curve. If it only contains a path with only a moveto, the path is considered NON-empty
307  */
308 bool
309 SPCurve::is_empty() const
311     return _pathv.empty();
314 /**
315  * True iff all subpaths are closed.
316  * Returns false if the curve is empty.
317  */
318 bool
319 SPCurve::is_closed() const
321     if (is_empty()) {
322         return false;
323     } else {
324         bool closed = true;
325         for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) {
326              if ( ! it->closed() ) {
327                 closed = false;
328                 break;
329             }
330         }
331         return closed;
332     }
335 /**
336  * Return last pathsegment (possibly the closing path segment) of the last path in PathVector or NULL.
337  * If the last path is empty (contains only a moveto), the function returns NULL
338  */
339 Geom::Curve const *
340 SPCurve::last_segment() const
342     if (is_empty()) {
343         return NULL;
344     }
345     if (_pathv.back().empty()) {
346         return NULL;
347     }
349     return &_pathv.back().back_default();
352 /**
353  * Return last path in PathVector or NULL.
354  */
355 Geom::Path const *
356 SPCurve::last_path() const
358     if (is_empty()) {
359         return NULL;
360     }
362     return &_pathv.back();
365 /**
366  * Return first pathsegment in PathVector or NULL.
367  * equal in functionality to SPCurve::first_bpath()
368  */
369 Geom::Curve const *
370 SPCurve::first_segment() const
372     if (is_empty()) {
373         return NULL;
374     }
375     if (_pathv.front().empty()) {
376         return NULL;
377     }
379     return &_pathv.front().front();
382 /**
383  * Return first path in PathVector or NULL.
384  */
385 Geom::Path const *
386 SPCurve::first_path() const
388     if (is_empty()) {
389         return NULL;
390     }
392     return &_pathv.front();
395 /**
396  * Return first point of first subpath or nothing when the path is empty.
397  */
398 boost::optional<Geom::Point>
399 SPCurve::first_point() const
401     boost::optional<Geom::Point> retval;
403     if (!is_empty()) {
404         retval = _pathv.front().initialPoint();
405     }
407     return retval;
410 /**
411  * Return the second point of first subpath or _movePos if curve too short.
412  * If the pathvector is empty, this returns nothing. If the first path is only a moveto, this method
413  * returns the first point of the second path, if it exists. If there is no 2nd path, it returns the
414  * first point of the first path.
415  */
416 boost::optional<Geom::Point>
417 SPCurve::second_point() const
419     boost::optional<Geom::Point> retval;
420     if (!is_empty()) {
421         if (_pathv.front().empty()) {
422             // first path is only a moveto
423             // check if there is second path
424             if (_pathv.size() > 1) {
425                 retval = _pathv[1].initialPoint();
426             } else {
427                 retval = _pathv[0].initialPoint();
428             }
429         } else {
430             retval = _pathv.front()[0].finalPoint();
431         }
432     }
434     return retval;
437 /**
438  * Return the second-last point of last subpath or first point when that last subpath has only a moveto.
439  */
440 boost::optional<Geom::Point>
441 SPCurve::penultimate_point() const
443     boost::optional<Geom::Point> retval;
444     if (!is_empty()) {
445         Geom::Path const &lastpath = _pathv.back();
446         if (!lastpath.empty()) {
447             Geom::Curve const &back = lastpath.back_default();
448             retval = back.initialPoint();
449         } else {
450             retval = lastpath.initialPoint();
451         }
452     }
454     return retval;
457 /**
458  * Return last point of last subpath or nothing when the curve is empty.
459  * If the last path is only a moveto, then return that point.
460  */
461 boost::optional<Geom::Point>
462 SPCurve::last_point() const
464     boost::optional<Geom::Point> retval;
466     if (!is_empty()) {
467         retval = _pathv.back().finalPoint();
468     }
470     return retval;
473 /**
474  * Returns a *new* \a curve but drawn in the opposite direction.
475  * Should result in the same shape, but
476  * with all its markers drawn facing the other direction.
477  * Reverses the order of subpaths as well
478  **/
479 SPCurve *
480 SPCurve::create_reverse() const
482     SPCurve *new_curve = new SPCurve(Geom::reverse_paths_and_order(_pathv));
484     return new_curve;
487 /**
488  * Append \a curve2 to \a this.
489  * If \a use_lineto is false, simply add all paths in \a curve2 to \a this;
490  * 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.
491  */
492 void
493 SPCurve::append(SPCurve const *curve2,
494                 bool use_lineto)
496     if (curve2->is_empty())
497         return;
499     if (use_lineto) {
500         Geom::PathVector::const_iterator it = curve2->_pathv.begin();
501         if ( ! _pathv.empty() ) {
502             Geom::Path & lastpath = _pathv.back();
503             lastpath.appendNew<Geom::LineSegment>( (*it).initialPoint() );
504             lastpath.append( (*it) );
505         } else {
506             _pathv.push_back( (*it) );
507         }
509         for (it++; it != curve2->_pathv.end(); it++) {
510             _pathv.push_back( (*it) );
511         }
512     } else {
513         for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) {
514             _pathv.push_back( (*it) );
515         }
516     }
519 /**
520  * Append \a c1 to \a this with possible fusing of close endpoints. If the end of this curve and the start of c1 are within tolerance distance,
521  * then the startpoint of c1 is moved to the end of this curve and the first subpath of c1 is appended to the last subpath of this curve.
522  * When one of the curves (this curve or the argument curve) is closed, the returned value is NULL; otherwise the returned value is this curve.
523  * When one of the curves is empty, this curves path becomes the non-empty path.
524  */
525 SPCurve *
526 SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance)
528     using Geom::X;
529     using Geom::Y;
531     g_return_val_if_fail(c1 != NULL, NULL);
532     if ( this->is_closed() || c1->is_closed() ) {
533         return NULL;
534     }
536     if (c1->is_empty()) {
537         return this;
538     }
540     if (this->is_empty()) {
541         _pathv = c1->_pathv;
542         return this;
543     }
545     if ( (fabs((*this->last_point())[X] - (*c1->first_point())[X]) <= tolerance)
546          && (fabs((*this->last_point())[Y] - (*c1->first_point())[Y]) <= tolerance) )
547     {
548     // c1's first subpath can be appended to this curve's last subpath
549         Geom::PathVector::const_iterator path_it = c1->_pathv.begin();
550         Geom::Path & lastpath = _pathv.back();
552         Geom::Path newfirstpath(*path_it);
553         newfirstpath.setInitial(lastpath.finalPoint());
554         lastpath.append( newfirstpath );
556         for (path_it++; path_it != c1->_pathv.end(); path_it++) {
557             _pathv.push_back( (*path_it) );
558         }
560     } else {
561         append(c1, true);
562     }
564     return this;
567 /**
568  * Remove last segment of curve.
569  * (Only used once in /src/pen-context.cpp)
570  */
571 void
572 SPCurve::backspace()
574     if ( is_empty() )
575         return;
577     if ( !_pathv.back().empty() ) {
578         _pathv.back().erase_last();
579         _pathv.back().close(false);
580     }
583 /**
584  * TODO: add comments about what this method does and what assumptions are made and requirements are put on SPCurve
585  (2:08:18 AM) Johan: basically, i convert the path to pw<d2>
586 (2:08:27 AM) Johan: then i calculate an offset path
587 (2:08:29 AM) Johan: to move the knots
588 (2:08:36 AM) Johan: then i add it
589 (2:08:40 AM) Johan: then convert back to path
590 If I remember correctly, this moves the firstpoint to new_p0, and the lastpoint to new_p1, and moves all nodes in between according to their arclength (interpolates the movement amount)
591  */
592 void
593 SPCurve::stretch_endpoints(Geom::Point const &new_p0, Geom::Point const &new_p1)
595     if (is_empty()) {
596         return;
597     }
599     Geom::Point const offset0( new_p0 - *first_point() );
600     Geom::Point const offset1( new_p1 - *last_point() );
602     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = _pathv.front().toPwSb();
603     Geom::Piecewise<Geom::SBasis> arclength = Geom::arcLengthSb(pwd2);
604     if ( arclength.lastValue() <= 0 ) {
605         g_error("SPCurve::stretch_endpoints - arclength <= 0");
606         throw;
607     }
608     arclength *= 1./arclength.lastValue();
609     Geom::Point const A( offset0 );
610     Geom::Point const B( offset1 );
611     Geom::Piecewise<Geom::SBasis> offsetx = (arclength*-1.+1)*A[0] + arclength*B[0];
612     Geom::Piecewise<Geom::SBasis> offsety = (arclength*-1.+1)*A[1] + arclength*B[1];
613     Geom::Piecewise<Geom::D2<Geom::SBasis> > offsetpath = Geom::sectionize( Geom::D2<Geom::Piecewise<Geom::SBasis> >(offsetx, offsety) );
614     pwd2 += offsetpath;
615     _pathv = Geom::path_from_piecewise( pwd2, 0.001 );
618 /**
619  *  sets start of first path to new_p0, and end of first path to  new_p1
620  */
621 void
622 SPCurve::move_endpoints(Geom::Point const &new_p0, Geom::Point const &new_p1)
624     if (is_empty()) {
625         return;
626     }
627     _pathv.front().setInitial(new_p0);
628     _pathv.front().setFinal(new_p1);
631 /**
632  * returns the number of nodes in a path, used for statusbar text when selecting an spcurve.
633  * Sum of nodes in all the paths. When a path is closed, and its closing line segment is of zero-length,
634  * this function will not count the closing knot double (so basically ignores the closing line segment when it has zero length)
635  */
636 guint
637 SPCurve::nodes_in_path() const
639     guint nr = 0;
640     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
641         nr += (*it).size();
643         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
645         // do not count closing knot double for zero-length closing line segments
646         // however, if the path is only a moveto, and is closed, do not subtract 1 (otherwise the result will be zero nodes)
647         if ( it->closed()
648              && ((*it).size() != 0) )
649         {
650             Geom::Curve const &c = it->back_closed();
651             if (are_near(c.initialPoint(), c.finalPoint())) {
652                 nr--;   
653             }
654         }
655     }
657     return nr;
660 /**
661  *  Adds p to the last point (and last handle if present) of the last path
662  */
663 void
664 SPCurve::last_point_additive_move(Geom::Point const & p)
666     if (is_empty()) {
667         return;
668     }
670     _pathv.back().setFinal( _pathv.back().finalPoint() + p );
672     // Move handle as well when the last segment is a cubic bezier segment:
673     // TODO: what to do for quadratic beziers?
674     if ( Geom::CubicBezier const *lastcube = dynamic_cast<Geom::CubicBezier const *>(&_pathv.back().back()) ) {
675         Geom::CubicBezier newcube( *lastcube );
676         newcube.setPoint(2, newcube[2] + p);
677         _pathv.back().replace( --_pathv.back().end(), newcube );
678     }
681 /*
682   Local Variables:
683   mode:c++
684   c-file-style:"stroustrup"
685   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
686   indent-tabs-mode:nil
687   fill-column:99
688   End:
689 */
690 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :