diff options
| author | Johan B. C. Engelen <jbc.engelen@swissonline.ch> | 2008-06-06 01:43:35 +0000 |
|---|---|---|
| committer | johanengelen <johanengelen@users.sourceforge.net> | 2008-06-06 01:43:35 +0000 |
| commit | 8a38c52bce619b07117cdd87a183eb05fb51e28e (patch) | |
| tree | 39b9f2af1ce9df43884a3b33ca2445097fe8f5a5 /src/display/curve.cpp | |
| parent | oops. sys/wait.h not on win32 (diff) | |
| download | inkscape-8a38c52bce619b07117cdd87a183eb05fb51e28e.tar.gz inkscape-8a38c52bce619b07117cdd87a183eb05fb51e28e.zip | |
merge gsoc2008_johan_path2geom into trunk
(bzr r5823)
Diffstat (limited to 'src/display/curve.cpp')
| -rw-r--r-- | src/display/curve.cpp | 573 |
1 files changed, 464 insertions, 109 deletions
diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 966a35f5b..eb86773f9 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -25,29 +25,74 @@ #include <libnr/n-art-bpath.h> #include <libnr/nr-point-matrix-ops.h> #include <libnr/nr-translate-ops.h> +#include <libnr/n-art-bpath-2geom.h> +#include <libnr/nr-convert2geom.h> #include <cstring> #include <string> +#include <2geom/pathvector.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/sbasis-to-bezier.h> +#include "svg/svg.h" static unsigned sp_bpath_length(NArtBpath const bpath[]); static bool sp_bpath_closed(NArtBpath const bpath[]); +#define NO_CHECKS + +static void debug_out( char const * text, Geom::PathVector const & pathv) { +#ifndef NO_CHECKS + char * str = sp_svg_write_path(pathv); + g_message("%s : %s", text, str); + g_free(str); +#endif +} +static void debug_out( char const * text, NArtBpath const * bpath) { +#ifndef NO_CHECKS + char * str = sp_svg_write_path(bpath); + g_message("%s : %s", text, str); + g_free(str); +#endif +} +void SPCurve::debug_check( char const * text, SPCurve const * curve) { +#ifndef NO_CHECKS + char * pathv_str = sp_svg_write_path(curve->_pathv); + char * bpath_str = sp_svg_write_path(curve->_bpath); + if ( strcmp(pathv_str, bpath_str) ) { + g_message("%s : unequal paths", text); + g_message("bpath : %s", bpath_str); + g_message("pathv : %s", pathv_str); + } + g_free(pathv_str); + g_free(bpath_str); +#endif +} +void SPCurve::debug_check( char const * text, bool a) { +#ifndef NO_CHECKS + if ( !a ) { + g_message("%s : bool fail", text); + } +#endif +} + /* Constructors */ /** * The returned curve's state is as if SPCurve::reset has just been called on it. * \param length Initial number of NArtBpath elements allocated for bpath (including NR_END * element). + * 2GEOMproof */ SPCurve::SPCurve(guint length) - : _end(0), + : _refcount(1), + _bpath(NULL), + _pathv(), + _end(0), _length(length), _substart(0), _hascpt(false), _posSet(false), _moving(false), - _closed(false), - _refcount(1), - _bpath(NULL) + _closed(false) { if (length <= 0) { g_error("SPCurve::SPCurve called with invalid length parameter"); @@ -56,8 +101,41 @@ SPCurve::SPCurve(guint length) _bpath = g_new(NArtBpath, length); _bpath->code = NR_END; + + _pathv.clear(); + + debug_check("SPCurve::SPCurve(guint length)", this); } +SPCurve::SPCurve(Geom::PathVector const& pathv) + : _refcount(1), + _bpath(NULL), + _pathv(pathv), + _end(0), + _length(0), + _substart(0), + _hascpt(false), + _posSet(false), + _moving(false), + _closed(false) +{ + // temporary code to convert to _bpath as well: + _bpath = BPath_from_2GeomPath(_pathv); + unsigned const len = sp_bpath_length(_bpath); + _length = len; + _end = _length - 1; + gint i = _end; + for (; i > 0; i--) + if ((_bpath[i].code == NR_MOVETO) || + (_bpath[i].code == NR_MOVETO_OPEN)) + break; + _substart = i; + _closed = sp_bpath_closed(_bpath); + + debug_check("SPCurve::SPCurve(Geom::PathVector const& pathv)", this); +} + +// * 2GEOMproof SPCurve * SPCurve::new_from_foreign_bpath(NArtBpath const *bpath) { @@ -81,6 +159,10 @@ SPCurve::new_from_foreign_bpath(NArtBpath const *bpath) curve->_substart = i; curve->_closed = sp_bpath_closed(new_bpath); + curve->_pathv = BPath_to_2GeomPath(curve->_bpath); + + debug_check("SPCurve::new_from_foreign_bpath", curve); + return curve; } @@ -88,6 +170,7 @@ SPCurve::new_from_foreign_bpath(NArtBpath const *bpath) * Convert NArtBpath object to SPCurve object. * * \return new SPCurve, or NULL if the curve was not created for some reason. + * 2GEOMproof */ SPCurve * SPCurve::new_from_bpath(NArtBpath *bpath) @@ -96,9 +179,13 @@ SPCurve::new_from_bpath(NArtBpath *bpath) SPCurve *curve = SPCurve::new_from_foreign_bpath(bpath); g_free(bpath); + + debug_check("SPCurve::new_from_bpath", curve); + return curve; } +// * 2GEOMproof SPCurve * SPCurve::new_from_rect(NR::Maybe<NR::Rect> const &rect) { @@ -114,9 +201,12 @@ SPCurve::new_from_rect(NR::Maybe<NR::Rect> const &rect) } c->closepath_current(); + debug_check("SPCurve::new_from_rect", c); + return c; } +// * 2GEOMproof SPCurve::~SPCurve() { if (_bpath) { @@ -127,19 +217,33 @@ SPCurve::~SPCurve() /* Methods */ -/** - * Frees old path and sets new path - * This does not copy the bpath, so the new_bpath should not be deleted by caller - */ void -SPCurve::set_bpath(NArtBpath * new_bpath) +SPCurve::set_pathv(Geom::PathVector const & new_pathv) { - if (new_bpath && new_bpath != _bpath) { // FIXME, add function to SPCurve to change bpath? or a copy function? - if (_bpath) { - g_free(_bpath); //delete old bpath - } - _bpath = new_bpath; + _pathv = new_pathv; + + _hascpt = false; + _posSet = false; + _moving = false; + + // temporary code to convert to _bpath as well: + if (_bpath) { + g_free(_bpath); + _bpath = NULL; } + _bpath = BPath_from_2GeomPath(_pathv); + unsigned const len = sp_bpath_length(_bpath); + _length = len; + _end = _length - 1; + gint i = _end; + for (; i > 0; i--) + if ((_bpath[i].code == NR_MOVETO) || + (_bpath[i].code == NR_MOVETO_OPEN)) + break; + _substart = i; + _closed = sp_bpath_closed(_bpath); + + debug_check("SPCurve::set_pathv", this); } /** @@ -150,18 +254,30 @@ SPCurve::get_bpath() const { return _bpath; }; -/* -NArtBpath * -SPCurve::get_bpath() + +Geom::PathVector const & +SPCurve::get_pathvector() const { - return _bpath; -}; -*/ + return _pathv; +} + +/** + *Returns index in bpath[] of NR_END element. + * remove for 2geom + */ +guint +SPCurve::get_length() const +{ +// g_message("SPCurve::get_length must be removed"); + + return _end; +} /** * Increase _refcount of curve. * * \todo should this be shared with other refcounting code? + * 2GEOMproof */ SPCurve * SPCurve::ref() @@ -177,6 +293,7 @@ SPCurve::ref() * Decrease refcount of curve, with possible destruction. * * \todo should this be shared with other refcounting code? + * 2GEOMproof */ SPCurve * SPCurve::unref() @@ -186,10 +303,6 @@ SPCurve::unref() _refcount -= 1; if (_refcount < 1) { - if (_bpath) { - g_free(_bpath); - _bpath = NULL; - } delete this; } @@ -198,6 +311,8 @@ SPCurve::unref() /** * Add space for more paths in curve. + * This function has no meaning for 2geom representation, other than maybe for optimization issues (enlargening the vector for what is to come) + * 2GEOMproof */ void SPCurve::ensure_space(guint space) @@ -218,6 +333,7 @@ SPCurve::ensure_space(guint space) /** * Create new curve from its own bpath array. + * 2GEOMproof */ SPCurve * SPCurve::copy() const @@ -229,6 +345,7 @@ SPCurve::copy() const /** * Return new curve that is the concatenation of all curves in list. + * 2GEOMified */ SPCurve * SPCurve::concat(GSList const *list) @@ -264,11 +381,19 @@ SPCurve::concat(GSList const *list) new_curve->_substart = i; + for (GSList const *l = list; l != NULL; l = l->next) { + SPCurve *c = (SPCurve *) l->data; + new_curve->_pathv.insert( new_curve->_pathv.end(), c->get_pathvector().begin(), c->get_pathvector().end() ); + } + + debug_check("SPCurve::concat", new_curve); + return new_curve; } /** * Returns a list of new curves corresponding to the subpaths in \a curve. + * 2geomified */ GSList * SPCurve::split() const @@ -278,6 +403,7 @@ SPCurve::split() const guint p = 0; GSList *l = NULL; + gint pathnr = 0; while (p < _end) { gint i = 1; while ((_bpath[p + i].code == NR_LINETO) || @@ -290,8 +416,10 @@ SPCurve::split() const new_curve->_substart = 0; new_curve->_closed = (new_curve->_bpath->code == NR_MOVETO); new_curve->_hascpt = (new_curve->_bpath->code == NR_MOVETO_OPEN); + new_curve->_pathv = Geom::PathVector(1, _pathv[pathnr]); l = g_slist_prepend(l, new_curve); p += i; + pathnr++; } return l; @@ -302,7 +430,7 @@ SPCurve::split() const */ template<class M> static void -tmpl_curve_transform(SPCurve *const curve, M const &m) +tmpl_curve_transform(SPCurve * curve, M const &m) { g_return_if_fail(curve != NULL); @@ -329,24 +457,44 @@ tmpl_curve_transform(SPCurve *const curve, M const &m) /** * Transform all paths in curve using matrix. + * 2GEOMified, can be deleted when completely 2geom */ void SPCurve::transform(NR::Matrix const &m) { tmpl_curve_transform<NR::Matrix>(this, m); + + transform(to_2geom(m)); + + debug_check("SPCurve::transform", this); +} + +/** + * Transform all paths in curve using matrix. + */ +void +SPCurve::transform(Geom::Matrix const &m) +{ + _pathv = _pathv * m; } /** * Transform all paths in curve using NR::translate. + * 2GEOMified, can be deleted when completely 2geom */ void SPCurve::transform(NR::translate const &m) { tmpl_curve_transform<NR::translate>(this, m); + + transform(to_2geom(m)); + + debug_check("SPCurve::transform translate", this); } /** * Set curve to empty curve. + * 2GEOMified */ void SPCurve::reset() @@ -360,6 +508,10 @@ SPCurve::reset() _posSet = false; _moving = false; _closed = false; + + _pathv.clear(); + + debug_check("SPCurve::reset", this); } /* Several consecutive movetos are ALLOWED */ @@ -372,9 +524,17 @@ SPCurve::moveto(gdouble x, gdouble y) { moveto(NR::Point(x, y)); } - +/** + * Calls SPCurve::moveto() with point made of given coordinates. + */ +void +SPCurve::moveto(Geom::Point const &p) +{ + moveto(from_2geom(p)); +} /** * Perform a moveto to a point, thus starting a new subpath. + * 2GEOMified */ void SPCurve::moveto(NR::Point const &p) @@ -386,19 +546,32 @@ SPCurve::moveto(NR::Point const &p) _hascpt = true; _posSet = true; _movePos = p; + _pathv.push_back( Geom::Path() ); // for some reason Geom::Path(p) does not work... + _pathv.back().start(to_2geom(p)); + + // the output is not the same. This is because SPCurve *incorrectly* coaslesces multiple moveto's into one for NArtBpath. +// debug_check("SPCurve::moveto", this); } /** * Calls SPCurve::lineto() with a point's coordinates. */ void +SPCurve::lineto(Geom::Point const &p) +{ + lineto(p[Geom::X], p[Geom::Y]); +} +/** + * Calls SPCurve::lineto() with a point's coordinates. + */ +void SPCurve::lineto(NR::Point const &p) { lineto(p[NR::X], p[NR::Y]); } - /** * Adds a line to the current subpath. + * 2GEOMified */ void SPCurve::lineto(gdouble x, gdouble y) @@ -415,10 +588,14 @@ SPCurve::lineto(gdouble x, gdouble y) bp->x3 = x; bp->y3 = y; _moving = false; - return; - } - if (_posSet) { + Geom::Path::iterator it = _pathv.back().end(); + if ( Geom::LineSegment const *last_line_segment = dynamic_cast<Geom::LineSegment const *>( &(*it) )) { + Geom::LineSegment new_seg( *last_line_segment ); + new_seg.setFinal( Geom::Point(x,y) ); + _pathv.back().replace(it, new_seg); + } + } else if (_posSet) { /* start a new segment */ ensure_space(2); NArtBpath *bp = _bpath + _end; @@ -433,77 +610,43 @@ SPCurve::lineto(gdouble x, gdouble y) _end += 2; _posSet = false; _closed = false; - return; - } - - /* add line */ - - g_return_if_fail(_end > 1); - ensure_space(1); - NArtBpath *bp = _bpath + _end; - bp->code = NR_LINETO; - bp->x3 = x; - bp->y3 = y; - bp++; - bp->code = NR_END; - _end++; -} -/// Unused -void -SPCurve::lineto_moving(gdouble x, gdouble y) -{ - g_return_if_fail(this != NULL); - g_return_if_fail(_hascpt); - - if (_moving) { - /* change endpoint */ - g_return_if_fail(!_posSet); - g_return_if_fail(_end > 1); - NArtBpath *bp = _bpath + _end - 1; - g_return_if_fail(bp->code == NR_LINETO); - bp->x3 = x; - bp->y3 = y; + _pathv.back().appendNew<Geom::LineSegment>( Geom::Point(x,y) ); return; - } + } else { + /* add line */ - if (_posSet) { - /* start a new segment */ - ensure_space(2); + g_return_if_fail(_end > 1); + ensure_space(1); NArtBpath *bp = _bpath + _end; - bp->code = NR_MOVETO_OPEN; - bp->setC(3, _movePos); - bp++; bp->code = NR_LINETO; bp->x3 = x; bp->y3 = y; bp++; bp->code = NR_END; - _end += 2; - _posSet = false; - _moving = true; - _closed = false; - return; + _end++; + _pathv.back().appendNew<Geom::LineSegment>( Geom::Point(x,y) ); } - /* add line */ - - g_return_if_fail(_end > 1); - ensure_space(1); - NArtBpath *bp = _bpath + _end; - bp->code = NR_LINETO; - bp->x3 = x; - bp->y3 = y; - bp++; - bp->code = NR_END; - _end++; - _moving = true; + debug_check("SPCurve::lineto", this); } /** * Calls SPCurve::curveto() with coordinates of three points. */ void +SPCurve::curveto(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2) +{ + using Geom::X; + using Geom::Y; + curveto( p0[X], p0[Y], + p1[X], p1[Y], + p2[X], p2[Y] ); +} +/** + * Calls SPCurve::curveto() with coordinates of three points. + */ +void SPCurve::curveto(NR::Point const &p0, NR::Point const &p1, NR::Point const &p2) { using NR::X; @@ -512,9 +655,9 @@ SPCurve::curveto(NR::Point const &p0, NR::Point const &p1, NR::Point const &p2) p1[X], p1[Y], p2[X], p2[Y] ); } - /** * Adds a bezier segment to the current subpath. + * 2GEOMified */ void SPCurve::curveto(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2) @@ -542,28 +685,33 @@ SPCurve::curveto(gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdo _end += 2; _posSet = false; _closed = false; - return; + _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) ); + } else { + /* add curve */ + + g_return_if_fail(_end > 1); + ensure_space(1); + NArtBpath *bp = _bpath + _end; + bp->code = NR_CURVETO; + bp->x1 = x0; + bp->y1 = y0; + bp->x2 = x1; + bp->y2 = y1; + bp->x3 = x2; + bp->y3 = y2; + bp++; + bp->code = NR_END; + _end++; + if (_pathv.empty()) g_message("leeg"); + else _pathv.back().appendNew<Geom::CubicBezier>( Geom::Point(x0,y0), Geom::Point(x1,y1), Geom::Point(x2,y2) ); } - /* add curve */ - - g_return_if_fail(_end > 1); - ensure_space(1); - NArtBpath *bp = _bpath + _end; - bp->code = NR_CURVETO; - bp->x1 = x0; - bp->y1 = y0; - bp->x2 = x1; - bp->y2 = y1; - bp->x3 = x2; - bp->y3 = y2; - bp++; - bp->code = NR_END; - _end++; + debug_check("SPCurve::curveto", this); } /** * Close current subpath by possibly adding a line between start and end. + * 2GEOMified */ void SPCurve::closepath() @@ -587,8 +735,23 @@ SPCurve::closepath() bs->code = NR_MOVETO; } + // Inkscape always manually adds the closing line segment to SPCurve with a lineto. + // This lineto is removed in the writing function for NArtBpath, + // so when path is closed and the last segment is a lineto, the closing line segment must really be removed first! + // TODO: fix behavior in Inkscape! + if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back())) { + _pathv.back().erase_last(); + } + _pathv.back().close(true); _closed = true; + for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) { + if ( ! it->closed() ) { + _closed = false; + break; + } + } + for (NArtBpath const *bp = _bpath; bp->code != NR_END; bp++) { /** \todo * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of @@ -601,6 +764,8 @@ SPCurve::closepath() } _hascpt = false; + + debug_check("SPCurve::closepath", this); } /** Like SPCurve::closepath() but sets the end point of the current @@ -627,8 +792,23 @@ SPCurve::closepath_current() bs->code = NR_MOVETO; } + // Inkscape always manually adds the closing line segment to SPCurve with a lineto. + // This lineto is removed in the writing function for NArtBpath, + // so when path is closed and the last segment is a lineto, the closing line segment must really be removed first! + // TODO: fix behavior in Inkscape! + if ( /*Geom::LineSegment const *line_segment = */ dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back())) { + _pathv.back().erase_last(); + } + _pathv.back().close(true); _closed = true; + for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) { + if ( ! it->closed() ) { + _closed = false; + break; + } + } + for (NArtBpath const *bp = _bpath; bp->code != NR_END; bp++) { /** \todo * effic: Maintain a count of NR_MOVETO_OPEN's (e.g. instead of @@ -642,6 +822,8 @@ SPCurve::closepath_current() _hascpt = false; _moving = false; + + debug_check("SPCurve::closepath_current", this); } /** @@ -652,6 +834,9 @@ SPCurve::is_empty() const { g_return_val_if_fail(this != NULL, TRUE); + bool empty = _pathv.empty(); /* || _pathv.front().empty(); */ + debug_check("SPCurve::is_empty", (_bpath->code == NR_END) == empty ); + return (_bpath->code == NR_END); } @@ -661,13 +846,22 @@ SPCurve::is_empty() const bool SPCurve::is_closed() const { + bool closed = true; + for (Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); it++) { + if ( ! it->closed() ) { + closed = false; + break; + } + } + debug_check("SPCurve::is_closed", (closed) == (_closed) ); + return _closed; } /** * Return last subpath or NULL. */ -NArtBpath * +NArtBpath const * SPCurve::last_bpath() const { g_return_val_if_fail(this != NULL, NULL); @@ -682,7 +876,7 @@ SPCurve::last_bpath() const /** * Return first subpath or NULL. */ -NArtBpath * +NArtBpath const * SPCurve::first_bpath() const { g_return_val_if_fail(this != NULL, NULL); @@ -695,14 +889,20 @@ SPCurve::first_bpath() const } /** - * Return first point of first subpath or (0,0). + * 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) ? */ NR::Point SPCurve::first_point() const { - NArtBpath *const bpath = first_bpath(); + NArtBpath const * bpath = first_bpath(); g_return_val_if_fail(bpath != NULL, NR::Point(0, 0)); + if (is_empty()) + return NR::Point(0, 0); + + debug_check("SPCurve::first_point", bpath->c(3) == _pathv.front().initialPoint() ); + return bpath->c(3); + // return from_2geom( _pathv.front().initialPoint() ); } /** @@ -724,6 +924,9 @@ SPCurve::second_point() const bpath = _bpath + 1; } g_return_val_if_fail(bpath != NULL, NR::Point(0, 0)); + + debug_check("SPCurve::second_point", bpath->c(3) == _pathv.front()[0].finalPoint() ); + return bpath->c(3); } @@ -741,18 +944,33 @@ SPCurve::penultimate_point() const NArtBpath *const bpath = _bpath + _end - 2; g_return_val_if_fail(bpath != NULL, NR::Point(0, 0)); + + Geom::Point p(NR_HUGE, NR_HUGE); + Geom::Curve const& back = _pathv.back().back(); + if (_pathv.back().closed()) { + p = back.finalPoint(); + } else { + p = back.initialPoint(); + } + + debug_check("SPCurve::penultimate_point", bpath->c(3) == p ); return bpath->c(3); } /** - * Return last point of last subpath or (0,0). + * 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) ? */ NR::Point SPCurve::last_point() const { - NArtBpath *const bpath = last_bpath(); + NArtBpath const * bpath = last_bpath(); g_return_val_if_fail(bpath != NULL, NR::Point(0, 0)); + if (is_empty()) + return NR::Point(0, 0); + + debug_check("SPCurve::last_point", bpath->c(3) == _pathv.back().finalPoint() ); return bpath->c(3); + // return from_2geom( _pathv.back().finalPoint() ); } inline static bool @@ -765,6 +983,8 @@ is_moveto(NRPathcode const c) * Returns a *new* \a curve but drawn in the opposite direction. * Should result in the same shape, but * with all its markers drawn facing the other direction. + * Reverses the order of subpaths as well + * 2GEOMified **/ SPCurve * SPCurve::create_reverse() const @@ -806,10 +1026,17 @@ SPCurve::create_reverse() const g_assert_not_reached(); } } + + new_curve->_pathv = Geom::reverse_paths_and_order(_pathv); + + debug_check("SPCurve::create_reverse", new_curve); } /** - * Append \a curve2 to \a curve. + * Append \a curve2 to \a this. + * If \a use_lineto is false, simply add all paths in \a curve2 to \a this; + * 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. + * 2GEOMified */ void SPCurve::append(SPCurve const *curve2, @@ -818,6 +1045,8 @@ SPCurve::append(SPCurve const *curve2, g_return_if_fail(this != NULL); g_return_if_fail(curve2 != NULL); + if (curve2->is_empty()) + return; if (curve2->_end < 1) return; @@ -865,6 +1094,29 @@ SPCurve::append(SPCurve const *curve2, if (closed) { closepath(); } + + debug_check("SPCurve::append", this); + + /* 2GEOM code when code above is removed: + if (use_lineto) { + Geom::PathVector::const_iterator it = curve2->_pathv.begin(); + if ( ! _pathv.empty() ) { + Geom::Path & lastpath = _pathv.back(); + lastpath.appendNew<Geom::LineSegment>( (*it).initialPoint() ); + lastpath.append( (*it) ); + } else { + _pathv.push_back( (*it) ); + } + + for (it++; it != curve2->_pathv.end(); it++) { + _pathv.push_back( (*it) ); + } + } else { + for (Geom::PathVector::const_iterator it = curve2->_pathv.begin(); it != curve2->_pathv.end(); it++) { + _pathv.push_back( (*it) ); + } + } + */ } /** @@ -882,7 +1134,9 @@ SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance) return this; } - NArtBpath *be = last_bpath(); + debug_check("SPCurve::append_continuous 11", this); + + NArtBpath const *be = last_bpath(); if (be) { NArtBpath const *bs = c1->first_bpath(); if ( bs @@ -923,17 +1177,23 @@ SPCurve::append_continuous(SPCurve const *c1, gdouble tolerance) append(c1, TRUE); } + debug_check("SPCurve::append_continuous", this); + return this; } /** * Remove last segment of curve. + * (Only used once in /src/pen-context.cpp) */ void SPCurve::backspace() { g_return_if_fail(this != NULL); + if ( is_empty() ) + return; + if (_end > 0) { _end -= 1; if (_end > 0) { @@ -950,6 +1210,13 @@ SPCurve::backspace() } _bpath[_end].code = NR_END; } + + if ( !_pathv.back().empty() ) { + _pathv.back().erase_last(); + _pathv.back().close(false); + } + + debug_check("SPCurve::backspace", this); } /* Private methods */ @@ -1089,6 +1356,9 @@ sp_curve_nonzero_distance_including_space(SPCurve const *const curve, double seg } } +/** + * + */ void SPCurve::stretch_endpoints(NR::Point const &new_p0, NR::Point const &new_p1) { @@ -1130,8 +1400,29 @@ SPCurve::stretch_endpoints(NR::Point const &new_p0, NR::Point const &new_p1) /* Explicit set for better numerical properties. */ _bpath[nSegs].setC(3, new_p1); delete [] seg2len; + + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2 = _pathv.front().toPwSb(); + Geom::Piecewise<Geom::SBasis> arclength = Geom::arcLengthSb(pwd2); + if ( arclength.lastValue() <= 0 ) { + g_error("SPCurve::stretch_endpoints - arclength <= 0"); + throw; + } + arclength *= 1./arclength.lastValue(); + Geom::Point const A( to_2geom(offset0) ); + Geom::Point const B( to_2geom(offset1) ); + Geom::Piecewise<Geom::SBasis> offsetx = (arclength*-1.+1)*A[0] + arclength*B[0]; + Geom::Piecewise<Geom::SBasis> offsety = (arclength*-1.+1)*A[1] + arclength*B[1]; + Geom::Piecewise<Geom::D2<Geom::SBasis> > offsetpath = Geom::sectionize( Geom::D2<Geom::Piecewise<Geom::SBasis> >(offsetx, offsety) ); + pwd2 += offsetpath; + _pathv = Geom::path_from_piecewise( pwd2, 0.001 ); + + debug_check("SPCurve::stretch_endpoints", this); } +/** + * sets start of first path to new_p0, and end of first path to new_p1 + * 2GEOMified + */ void SPCurve::move_endpoints(NR::Point const &new_p0, NR::Point const &new_p1) { @@ -1143,8 +1434,72 @@ SPCurve::move_endpoints(NR::Point const &new_p0, NR::Point const &new_p1) _bpath->setC(3, new_p0); _bpath[nSegs].setC(3, new_p1); + + _pathv.front().setInitial(to_2geom(new_p0)); + _pathv.front().setFinal(to_2geom(new_p1)); + + debug_check("SPCurve::move_endpoints", this); +} + +/** + * returns the number of nodes in a path, used for statusbar text when selecting an spcurve. + * 2GEOMified + */ +guint +SPCurve::nodes_in_path() const +{ + gint r = _end; + gint i = _length - 1; + if (i > r) i = r; // sometimes after switching from node editor length is wrong, e.g. f6 - draw - f2 - tab - f1, this fixes it + for (; i >= 0; i --) + if (_bpath[i].code == NR_MOVETO) + r --; + + guint nr = 0; + for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) { + nr += (*it).size(); + + 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 + } + + debug_check("SPCurve::nodes_in_path", r == (gint)nr); + + return r; } +/** + * Adds p to the last point (and last handle if present) of the last path + */ +void +SPCurve::last_point_additive_move(Geom::Point const & p) +{ + if (is_empty()) { + return; + } + if (_end == 0) { + return; + } + NArtBpath * path = _bpath + _end - 1; + + if (path->code == NR_CURVETO) { + path->x2 += p[Geom::X]; + path->y2 += p[Geom::Y]; + } + path->x3 += p[Geom::X]; + path->y3 += p[Geom::Y]; + + _pathv.back().setFinal( _pathv.back().finalPoint() + p ); + + // Move handle as well when the last segment is a cubic bezier segment: + // TODO: what to do for quadratic beziers? + if ( Geom::CubicBezier const *lastcube = dynamic_cast<Geom::CubicBezier const *>(&_pathv.back().back()) ) { + Geom::CubicBezier newcube( *lastcube ); + newcube.setPoint(2, newcube[2] + p); + _pathv.back().replace( --_pathv.back().end(), newcube ); + } + + debug_check("SPCurve::last_point_additive_move", this); +} /* Local Variables: |
