Code

b48c941ba27066039c6558ba3be2ab42232df1e6
[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 <string.h>
23 #include "libnr/nr-point.h"
24 #include "libnr/nr-rect.h"
25 #include <libnr/nr-point-matrix-ops.h>
26 #include <libnr/nr-convert2geom.h>
27 #include <cstring>
28 #include <string>
29 #include <2geom/pathvector.h>
30 #include <2geom/sbasis-geometric.h>
31 #include <2geom/sbasis-to-bezier.h>
32 #include "svg/svg.h"
33 #include <2geom/point.h>
35 /* Constructors */
37 /**
38  * The returned curve's state is as if SPCurve::reset has just been called on it.
39  * \param length Initial number of NArtBpath elements allocated for bpath (including NR_END
40  *    element).
41  * 2GEOMproof
42  */
43 SPCurve::SPCurve(guint length)
44   : _refcount(1),
45     _pathv()
46 {
47     if (length <= 0) {
48         g_error("SPCurve::SPCurve called with invalid length parameter");
49         throw;
50     }
52     _pathv.clear();
53 }
55 SPCurve::SPCurve(Geom::PathVector const& pathv)
56   : _refcount(1),
57     _pathv(pathv)
58 {
59 }
61 SPCurve *
62 SPCurve::new_from_rect(Geom::Rect const &rect)
63 {
64     SPCurve *c =  new SPCurve();
66     NR::Point p = rect.corner(0);
67     c->moveto(p);
69     for (int i=3; i>=0; i--) {
70         c->lineto(rect.corner(i));
71     }
72     c->closepath_current();
74     return c;
75 }
77 SPCurve::~SPCurve()
78 {
79 }
81 /* Methods */
83 void
84 SPCurve::set_pathvector(Geom::PathVector const & new_pathv)
85 {
86     _pathv = new_pathv;
87 }
89 Geom::PathVector const &
90 SPCurve::get_pathvector() const
91 {
92     return _pathv;
93 }
95 /*
96  * Returns the number of segments of all paths summed
97  * This count includes the closing line segment of a closed path.
98  */
99 guint
100 SPCurve::get_segment_count() const
102     guint nr = 0;
103     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
104         nr += (*it).size();
106         if (it->closed())   nr += 1;
107     }
108     return nr;
111 /**
112  * Increase _refcount of curve.
113  *
114  * \todo should this be shared with other refcounting code?
115  */
116 SPCurve *
117 SPCurve::ref()
119     _refcount += 1;
121     return this;
124 /**
125  * Decrease refcount of curve, with possible destruction.
126  *
127  * \todo should this be shared with other refcounting code?
128  */
129 SPCurve *
130 SPCurve::unref()
132     _refcount -= 1;
134     if (_refcount < 1) {
135         delete this;
136     }
138     return NULL;
141 /**
142  * Create new curve from this curve's pathvector array.
143  */
144 SPCurve *
145 SPCurve::copy() const
147     return new SPCurve(_pathv);
150 /**
151  * Return new curve that is the concatenation of all curves in list.
152  */
153 SPCurve *
154 SPCurve::concat(GSList const *list)
156     SPCurve *new_curve = new SPCurve();
158     for (GSList const *l = list; l != NULL; l = l->next) {
159         SPCurve *c = (SPCurve *) l->data;
160         new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() );
161     }
163     return new_curve;
166 /**
167  * Returns a list of new curves corresponding to the subpaths in \a curve.
168  * 2geomified
169  */
170 GSList *
171 SPCurve::split() const
173     GSList *l = NULL;
175     for (Geom::PathVector::const_iterator path_it = _pathv.begin(); path_it != _pathv.end(); ++path_it) {
176         Geom::PathVector newpathv;
177         newpathv.push_back(*path_it);
178         SPCurve * newcurve = new SPCurve(newpathv);
179         l = g_slist_prepend(l, newcurve);
180     }
182     return l;
186 void
187 SPCurve::transform(NR::Matrix const &m)
189     transform(to_2geom(m));
190 };
192     
193 /**
194  * Transform all paths in curve using matrix.
195  */
196 void
197 SPCurve::transform(Geom::Matrix const &m)
199     _pathv = _pathv * m;
202 /**
203  * Set curve to empty curve.
204  */
205 void
206 SPCurve::reset()
208     _pathv.clear();
211 /** Several consecutive movetos are ALLOWED
212  *  Ref: http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
213  * (first subitem of the item about zero-length path segments) */
215 /**
216  * Calls SPCurve::moveto() with point made of given coordinates.
217  */
218 void
219 SPCurve::moveto(gdouble x, gdouble y)
221     moveto(NR::Point(x, y));
223 /**
224  * Calls SPCurve::moveto() with point made of given coordinates.
225  */
226 void
227 SPCurve::moveto(Geom::Point const &p)
229     moveto(from_2geom(p));
231 /**
232  * Perform a moveto to a point, thus starting a new subpath.
233  */
234 void
235 SPCurve::moveto(NR::Point const &p)
237     _pathv.push_back( Geom::Path() );  // for some reason Geom::Path(p) does not work...
238     _pathv.back().start(to_2geom(p));
241 /**
242  * Calls SPCurve::lineto() with a point's coordinates.
243  */
244 void
245 SPCurve::lineto(Geom::Point const &p)
247     lineto(p[Geom::X], p[Geom::Y]);
249 /**
250  * Calls SPCurve::lineto() with a point's coordinates.
251  */
252 void
253 SPCurve::lineto(NR::Point const &p)
255     lineto(p[NR::X], p[NR::Y]);
257 /**
258  * Adds a line to the current subpath.
259  */
260 void
261 SPCurve::lineto(gdouble x, gdouble y)
263     if (_pathv.empty())  g_message("leeg");
264     else _pathv.back().appendNew<Geom::LineSegment>( Geom::Point(x,y) );
267 /**
268  * Calls SPCurve::curveto() with coordinates of three points.
269  */
270 void
271 SPCurve::curveto(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2)
273     using Geom::X;
274     using Geom::Y;
275     curveto( p0[X], p0[Y],
276              p1[X], p1[Y],
277              p2[X], p2[Y] );
279 /**
280  * Calls SPCurve::curveto() with coordinates of three points.
281  */
282 void
283 SPCurve::curveto(NR::Point const &p0, NR::Point const &p1, NR::Point const &p2)
285     using NR::X;
286     using NR::Y;
287     curveto( p0[X], p0[Y],
288              p1[X], p1[Y],
289              p2[X], p2[Y] );
291 /**
292  * Adds a bezier segment to the current subpath.
293  * 2GEOMified
294  */
295 void
296 SPCurve::curveto(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
298     if (_pathv.empty())  g_message("leeg");
299     else _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) );
302 /**
303  * Close current subpath by possibly adding a line between start and end.
304  */
305 void
306 SPCurve::closepath()
308     _pathv.back().close(true);
311 /** Like SPCurve::closepath() but sets the end point of the last subpath
312     to the subpath start point instead of adding a new lineto.
314     Used for freehand drawing when the user draws back to the start point.
315 **/
316 void
317 SPCurve::closepath_current()
319     _pathv.back().setFinal(_pathv.back().initialPoint());
320     _pathv.back().close(true);
323 /**
324  * True if no paths are in curve. If it only contains a path with only a moveto, the path is considered NON-empty
325  */
326 bool
327 SPCurve::is_empty() const
329     return _pathv.empty();
332 /**
333  * True iff all subpaths are closed.
334  * Returns false if the curve is empty.
335  */
336 bool
337 SPCurve::is_closed() const
339     if (is_empty()) {
340         return false;
341     } else {
342         bool closed = true;
343         for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) {
344              if ( ! it->closed() ) {
345                 closed = false;
346                 break;
347             }
348         }
349         return closed;
350     }
353 /**
354  * Return last pathsegment (possibly the closing path segment) of the last path in PathVector or NULL.
355  * If the last path is empty (contains only a moveto), the function returns NULL
356  */
357 Geom::Curve const *
358 SPCurve::last_segment() const
360     if (is_empty()) {
361         return NULL;
362     }
363     if (_pathv.back().empty()) {
364         return NULL;
365     }
367     return &_pathv.back().back_default();
370 /**
371  * Return last path in PathVector or NULL.
372  */
373 Geom::Path const *
374 SPCurve::last_path() const
376     if (is_empty()) {
377         return NULL;
378     }
380     return &_pathv.back();
383 /**
384  * Return first pathsegment in PathVector or NULL.
385  * equal in functionality to SPCurve::first_bpath()
386  */
387 Geom::Curve const *
388 SPCurve::first_segment() const
390     if (is_empty()) {
391         return NULL;
392     }
393     if (_pathv.front().empty()) {
394         return NULL;
395     }
397     return &_pathv.front().front();
400 /**
401  * Return first path in PathVector or NULL.
402  */
403 Geom::Path const *
404 SPCurve::first_path() const
406     if (is_empty()) {
407         return NULL;
408     }
410     return &_pathv.front();
413 /**
414  * 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) ?
415  */
416 NR::Point
417 SPCurve::first_point() const
419     if (is_empty())
420         return NR::Point(0, 0);
422     return from_2geom( _pathv.front().initialPoint() );
425 /**
426  * Return the second point of first subpath or _movePos if curve too short.
427  * If the pathvector is empty, this returns (0,0). If the first path is only a moveto, this method
428  * returns the first point of the second path, if it exists. If there is no 2nd path, it returns the
429  * first point of the first path.
430  *
431  * FIXME: for empty paths shouldn't this return (NR_HUGE,NR_HUGE)
432  */
433 NR::Point
434 SPCurve::second_point() const
436     if (is_empty()) {
437         return NR::Point(0,0);
438     }
439     else if (_pathv.front().empty()) {
440         // first path is only a moveto
441         // check if there is second path
442         if (_pathv.size() > 1) {
443             return _pathv[1].initialPoint();
444         } else {
445             return _pathv[0].initialPoint();
446         }
447     }
448     else
449         return _pathv.front()[0].finalPoint();
452 /**
453  * TODO: fix comment: Return the second-last point of last subpath or _movePos if curve too short.
454  */
455 NR::Point
456 SPCurve::penultimate_point() const
458     Geom::Curve const& back = _pathv.back().back_default();
459     Geom::Point p = back.initialPoint();
460     return from_2geom(p);
463 /**
464  * 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) ?
465  * If the last path is only a moveto, then return that point.
466  */
467 NR::Point
468 SPCurve::last_point() const
470     if (is_empty())
471         return NR::Point(0, 0);
473     return from_2geom( _pathv.back().finalPoint() );
476 /**
477  * Returns a *new* \a curve but drawn in the opposite direction.
478  * Should result in the same shape, but
479  * with all its markers drawn facing the other direction.
480  * Reverses the order of subpaths as well
481  **/
482 SPCurve *
483 SPCurve::create_reverse() const
485     SPCurve *new_curve = new SPCurve(Geom::reverse_paths_and_order(_pathv));
487     return new_curve;
490 /**
491  * Append \a curve2 to \a this.
492  * If \a use_lineto is false, simply add all paths in \a curve2 to \a this;
493  * 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.
494  */
495 void
496 SPCurve::append(SPCurve const *curve2,
497                 bool use_lineto)
499     if (curve2->is_empty())
500         return;
502     if (use_lineto) {
503         Geom::PathVector::const_iterator it = curve2->_pathv.begin();
504         if ( ! _pathv.empty() ) {
505             Geom::Path & lastpath = _pathv.back();
506             lastpath.appendNew<Geom::LineSegment>( (*it).initialPoint() );
507             lastpath.append( (*it) );
508         } else {
509             _pathv.push_back( (*it) );
510         }
512         for (it++; it != curve2->_pathv.end(); it++) {
513             _pathv.push_back( (*it) );
514         }
515     } else {
516         for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) {
517             _pathv.push_back( (*it) );
518         }
519     }
522 /**
523  * 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,
524  * 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.
525  * 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.
526  * When one of the curves is empty, this curves path becomes the non-empty path.
527  */
528 SPCurve *
529 SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance)
531     using Geom::X;
532     using Geom::Y;
534     g_return_val_if_fail(c1 != NULL, NULL);
535     if ( this->is_closed() || c1->is_closed() ) {
536         return NULL;
537     }
539     if (c1->is_empty()) {
540         return this;
541     }
543     if (this->is_empty()) {
544         _pathv = c1->_pathv;
545         return this;
546     }
548     if ( (fabs(this->last_point()[X] - c1->first_point()[X]) <= tolerance)
549          && (fabs(this->last_point()[Y] - c1->first_point()[Y]) <= tolerance) )
550     {
551     // c1's first subpath can be appended to this curve's last subpath
552         Geom::PathVector::const_iterator path_it = c1->_pathv.begin();
553         Geom::Path & lastpath = _pathv.back();
555         Geom::Path newfirstpath(*path_it);
556         newfirstpath.setInitial(lastpath.finalPoint());
557         lastpath.append( newfirstpath );
559         for (path_it++; path_it != c1->_pathv.end(); path_it++) {
560             _pathv.push_back( (*path_it) );
561         }
563     } else {
564         append(c1, false);
565     }
567     return this;
570 /**
571  * Remove last segment of curve.
572  * (Only used once in /src/pen-context.cpp)
573  */
574 void
575 SPCurve::backspace()
577     if ( is_empty() )
578         return;
580     if ( !_pathv.back().empty() ) {
581         _pathv.back().erase_last();
582         _pathv.back().close(false);
583     }
586 /**
587  * TODO: add comments about what this method does and what assumptions are made and requirements are put on SPCurve
588  */
589 void
590 SPCurve::stretch_endpoints(NR::Point const &new_p0, NR::Point const &new_p1)
592     if (is_empty()) {
593         return;
594     }
596     NR::Point const offset0( new_p0 - first_point() );
597     NR::Point const offset1( new_p1 - last_point() );
599     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = _pathv.front().toPwSb();
600     Geom::Piecewise<Geom::SBasis> arclength = Geom::arcLengthSb(pwd2);
601     if ( arclength.lastValue() <= 0 ) {
602         g_error("SPCurve::stretch_endpoints - arclength <= 0");
603         throw;
604     }
605     arclength *= 1./arclength.lastValue();
606     Geom::Point const A( to_2geom(offset0) );
607     Geom::Point const B( to_2geom(offset1) );
608     Geom::Piecewise<Geom::SBasis> offsetx = (arclength*-1.+1)*A[0] + arclength*B[0];
609     Geom::Piecewise<Geom::SBasis> offsety = (arclength*-1.+1)*A[1] + arclength*B[1];
610     Geom::Piecewise<Geom::D2<Geom::SBasis> > offsetpath = Geom::sectionize( Geom::D2<Geom::Piecewise<Geom::SBasis> >(offsetx, offsety) );
611     pwd2 += offsetpath;
612     _pathv = Geom::path_from_piecewise( pwd2, 0.001 );
615 /**
616  *  sets start of first path to new_p0, and end of first path to  new_p1
617  */
618 void
619 SPCurve::move_endpoints(NR::Point const &new_p0, NR::Point const &new_p1)
621     if (is_empty()) {
622         return;
623     }
624     _pathv.front().setInitial(to_2geom(new_p0));
625     _pathv.front().setFinal(to_2geom(new_p1));
628 /**
629  * returns the number of nodes in a path, used for statusbar text when selecting an spcurve.
630  */
631 guint
632 SPCurve::nodes_in_path() const
634     guint nr = 0;
635     for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
636         nr += (*it).size();
638         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
639     }
641     return nr;
644 /**
645  *  Adds p to the last point (and last handle if present) of the last path
646  */
647 void
648 SPCurve::last_point_additive_move(Geom::Point const & p)
650     if (is_empty()) {
651         return;
652     }
654     _pathv.back().setFinal( _pathv.back().finalPoint() + p );
656     // Move handle as well when the last segment is a cubic bezier segment:
657     // TODO: what to do for quadratic beziers?
658     if ( Geom::CubicBezier const *lastcube = dynamic_cast<Geom::CubicBezier const *>(&_pathv.back().back()) ) {
659         Geom::CubicBezier newcube( *lastcube );
660         newcube.setPoint(2, newcube[2] + p);
661         _pathv.back().replace( --_pathv.back().end(), newcube );
662     }
665 /*
666   Local Variables:
667   mode:c++
668   c-file-style:"stroustrup"
669   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
670   indent-tabs-mode:nil
671   fill-column:99
672   End:
673 */
674 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :