summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2015-05-08 06:29:58 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2015-05-08 06:29:58 +0000
commite110371c4d69ea0a407d7500b156cf373109787b (patch)
tree7c575b0576389731bd7758766986dc56f3aeccf6 /src
parentUpdate 2Geom to r2347 (diff)
downloadinkscape-e110371c4d69ea0a407d7500b156cf373109787b.tar.gz
inkscape-e110371c4d69ea0a407d7500b156cf373109787b.zip
Fix node editing problems
(bzr r14059.2.4)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/path-sink.h4
-rw-r--r--src/2geom/pathvector.h3
-rw-r--r--src/2geom/svg-path-parser.cpp105
-rw-r--r--src/2geom/svg-path-parser.h16
-rw-r--r--src/svg/svg-path.cpp11
-rw-r--r--src/ui/tool/path-manipulator.cpp10
6 files changed, 95 insertions, 54 deletions
diff --git a/src/2geom/path-sink.h b/src/2geom/path-sink.h
index 5913593d2..d6806031d 100644
--- a/src/2geom/path-sink.h
+++ b/src/2geom/path-sink.h
@@ -212,7 +212,11 @@ class PathBuilder : public PathIteratorSink<SubpathInserter> {
private:
PathVector _pathset;
public:
+ /// Create a builder that outputs to an internal pathvector.
PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
+ /// Create a builder that outputs to pathvector given by reference.
+ PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {}
+
/// Retrieve the path
PathVector const &peek() const {return _pathset;}
/// Clear the stored path vector
diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h
index 375c4f0a0..955cd1d37 100644
--- a/src/2geom/pathvector.h
+++ b/src/2geom/pathvector.h
@@ -112,9 +112,8 @@ class PathVector
, MultipliableNoncommutative< PathVector, HShear
, MultipliableNoncommutative< PathVector, VShear
, MultipliableNoncommutative< PathVector, Zoom
- , boost::addable< PathVector
, boost::equality_comparable< PathVector
- > > > > > > > > >
+ > > > > > > > >
{
typedef std::vector<Path> Sequence;
public:
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
index 89a912695..2778795b3 100644
--- a/src/2geom/svg-path-parser.cpp
+++ b/src/2geom/svg-path-parser.cpp
@@ -1,5 +1,5 @@
-#line 1 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 1 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
/**
* \file
* \brief parse SVG path specifications
@@ -44,7 +44,7 @@
namespace Geom {
-#line 48 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+#line 48 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
static const char _svg_path_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
3, 1, 4, 1, 5, 1, 15, 2,
@@ -1075,29 +1075,38 @@ static const int svg_path_first_final = 232;
static const int svg_path_en_main = 232;
-#line 47 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 47 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
SVGPathParser::SVGPathParser(PathSink &sink)
: _absolute(false)
, _sink(sink)
+ , _z_snap_threshold(0)
+ , _curve(NULL)
{
reset();
}
+SVGPathParser::~SVGPathParser()
+{
+ delete _curve;
+}
+
void SVGPathParser::reset() {
_absolute = false;
_current = _initial = Point(0, 0);
_quad_tangent = _cubic_tangent = Point(0, 0);
_params.clear();
+ delete _curve;
+ _curve = NULL;
-#line 1096 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+#line 1105 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
{
cs = svg_path_start;
}
-#line 64 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 73 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
}
@@ -1162,23 +1171,24 @@ Point SVGPathParser::_pop_point() {
}
void SVGPathParser::_moveTo(Point const &p) {
+ _pushCurve(NULL); // flush
_sink.moveTo(p);
_quad_tangent = _cubic_tangent = _current = _initial = p;
}
void SVGPathParser::_lineTo(Point const &p) {
- _sink.lineTo(p);
+ _pushCurve(new LineSegment(_current, p));
_quad_tangent = _cubic_tangent = _current = p;
}
void SVGPathParser::_curveTo(Point const &c0, Point const &c1, Point const &p) {
- _sink.curveTo(c0, c1, p);
+ _pushCurve(new CubicBezier(_current, c0, c1, p));
_quad_tangent = _current = p;
_cubic_tangent = p + ( p - c1 );
}
void SVGPathParser::_quadTo(Point const &c, Point const &p) {
- _sink.quadTo(c, p);
+ _pushCurve(new QuadraticBezier(_current, c, p));
_cubic_tangent = _current = p;
_quad_tangent = p + ( p - c );
}
@@ -1186,19 +1196,31 @@ void SVGPathParser::_quadTo(Point const &c, Point const &p) {
void SVGPathParser::_arcTo(Coord rx, Coord ry, Coord angle,
bool large_arc, bool sweep, Point const &p)
{
- if (are_near(_current, p)) {
+ if (_current == p) {
return; // ignore invalid (ambiguous) arc segments where start and end point are the same (per SVG spec)
}
- _sink.arcTo(rx, ry, angle, large_arc, sweep, p);
+ _pushCurve(new SVGEllipticalArc(_current, rx, ry, angle, large_arc, sweep, p));
_quad_tangent = _cubic_tangent = _current = p;
}
void SVGPathParser::_closePath() {
+ if (!_absolute && _curve && are_near(_initial, _current, _z_snap_threshold)) {
+ _curve->setFinal(_initial);
+ }
+ _pushCurve(NULL); // flush
_sink.closePath();
_quad_tangent = _cubic_tangent = _current = _initial;
}
+void SVGPathParser::_pushCurve(Curve *c) {
+ if (_curve) {
+ _sink.feed(*_curve, false);
+ delete _curve;
+ }
+ _curve = c;
+}
+
void SVGPathParser::_parse(char const *str, char const *strend, bool finish)
{
char const *p = str;
@@ -1207,7 +1229,7 @@ void SVGPathParser::_parse(char const *str, char const *strend, bool finish)
char const *start = NULL;
-#line 1211 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+#line 1233 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
{
int _klen;
unsigned int _trans;
@@ -1282,13 +1304,13 @@ _match:
switch ( *_acts++ )
{
case 0:
-#line 173 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 195 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
start = p;
}
break;
case 1:
-#line 177 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 199 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
if (start) {
std::string buf(start, p);
@@ -1302,55 +1324,55 @@ _match:
}
break;
case 2:
-#line 189 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 211 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(1.0);
}
break;
case 3:
-#line 193 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 215 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(0.0);
}
break;
case 4:
-#line 197 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 219 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = true;
}
break;
case 5:
-#line 201 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 223 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = false;
}
break;
case 6:
-#line 205 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 227 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_moveTo(_pop_point());
}
break;
case 7:
-#line 209 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 231 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
-#line 213 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 235 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
-#line 217 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 239 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
-#line 221 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 243 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1359,7 +1381,7 @@ _match:
}
break;
case 11:
-#line 228 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1367,7 +1389,7 @@ _match:
}
break;
case 12:
-#line 234 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 256 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
@@ -1375,14 +1397,14 @@ _match:
}
break;
case 13:
-#line 240 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 262 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
-#line 245 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 267 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
@@ -1395,12 +1417,12 @@ _match:
}
break;
case 15:
-#line 256 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 278 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
-#line 1404 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+#line 1426 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
}
}
@@ -1417,7 +1439,7 @@ _again:
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 1:
-#line 177 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 199 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
if (start) {
std::string buf(start, p);
@@ -1431,31 +1453,31 @@ _again:
}
break;
case 6:
-#line 205 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 227 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_moveTo(_pop_point());
}
break;
case 7:
-#line 209 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 231 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
-#line 213 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 235 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
-#line 217 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 239 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
-#line 221 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 243 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1464,7 +1486,7 @@ _again:
}
break;
case 11:
-#line 228 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1472,7 +1494,7 @@ _again:
}
break;
case 12:
-#line 234 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 256 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
@@ -1480,14 +1502,14 @@ _again:
}
break;
case 13:
-#line 240 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 262 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
-#line 245 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 267 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
@@ -1500,12 +1522,12 @@ _again:
}
break;
case 15:
-#line 256 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 278 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
-#line 1509 "D:/lib2geom/trunk/src/2geom/svg-path-parser.cpp"
+#line 1531 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
}
}
}
@@ -1513,7 +1535,7 @@ _again:
_out: {}
}
-#line 398 "D:/lib2geom/trunk/src/2geom/svg-path-parser.rl"
+#line 420 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
if (finish) {
@@ -1525,6 +1547,7 @@ _again:
}
if (finish) {
+ _pushCurve(NULL);
_sink.flush();
reset();
}
diff --git a/src/2geom/svg-path-parser.h b/src/2geom/svg-path-parser.h
index 6478ba468..094ecd4da 100644
--- a/src/2geom/svg-path-parser.h
+++ b/src/2geom/svg-path-parser.h
@@ -40,6 +40,7 @@
#include <2geom/exception.h>
#include <2geom/point.h>
#include <2geom/path-sink.h>
+#include <2geom/forward.h>
namespace Geom {
@@ -60,6 +61,7 @@ namespace Geom {
class SVGPathParser {
public:
SVGPathParser(PathSink &sink);
+ ~SVGPathParser();
/** @brief Reset internal state.
* Discards the internal state associated with partially parsed data,
@@ -94,6 +96,17 @@ public:
* You should not call this after parse(). */
void finish();
+ /** @brief Set the threshold for considering the closing segment degenerate.
+ * When the current point was reached by a relative command, is closer
+ * to the initial point of the path than the specified threshold
+ * and a 'z' is encountered, the last segment will be adjusted instead so that
+ * the closing segment has exactly zero length. This is useful when reading
+ * SVG 1.1 paths that have non-linear final segments written in relative
+ * coordinates, which always suffer from some loss of precision. SVG 2
+ * allows alternate placement of 'z' which does not have this problem. */
+ void setZSnapThreshold(Coord threshold) { _z_snap_threshold = threshold; }
+ Coord zSnapThreshold() const { return _z_snap_threshold; }
+
private:
bool _absolute;
Point _current;
@@ -102,6 +115,8 @@ private:
Point _quad_tangent;
std::vector<Coord> _params;
PathSink &_sink;
+ Coord _z_snap_threshold;
+ Curve *_curve;
int cs;
std::string _number_part;
@@ -118,6 +133,7 @@ private:
void _arcTo(double rx, double ry, double angle,
bool large_arc, bool sweep, Point const &p);
void _closePath();
+ void _pushCurve(Curve *c);
void _parse(char const *str, char const *strend, bool finish);
};
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
index 8f839eca5..ab51a5537 100644
--- a/src/svg/svg-path.cpp
+++ b/src/svg/svg-path.cpp
@@ -43,16 +43,15 @@ Geom::PathVector sp_svg_read_pathv(char const * str)
if (!str)
return pathv; // return empty pathvector when str == NULL
-
- typedef std::back_insert_iterator<Geom::PathVector> Inserter;
- Inserter iter(pathv);
- Geom::PathIteratorSink<Inserter> generator(iter);
+ Geom::PathBuilder builder(pathv);
+ Geom::SVGPathParser parser(builder);
+ parser.setZSnapThreshold(Geom::EPSILON);
try {
- Geom::parse_svg_path(str, generator);
+ parser.parse(str);
}
catch (Geom::SVGPathParseError &e) {
- generator.flush();
+ builder.flush();
// This warning is extremely annoying when testing
//g_warning("Malformed SVG path, truncated path up to where error was found.\n Input path=\"%s\"\n Parsed path=\"%s\"", str, sp_svg_write_path(pathv));
}
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index e54764216..bbedb8263 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -1148,15 +1148,15 @@ void PathManipulator::_createControlPointsFromGeometry()
Node *previous_node = new Node(_multi_path_manipulator._path_data.node_data, pit->initialPoint());
subpath->push_back(previous_node);
- Geom::Curve const &cseg = pit->back_closed();
- bool fuse_ends = pit->closed()
- && Geom::are_near(cseg.initialPoint(), cseg.finalPoint());
- for (Geom::Path::iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
+
+ bool closed = pit->closed();
+
+ for (Geom::Path::iterator cit = pit->begin(); cit != pit->end(); ++cit) {
Geom::Point pos = cit->finalPoint();
Node *current_node;
// if the closing segment is degenerate and the path is closed, we need to move
// the handle of the first node instead of creating a new one
- if (fuse_ends && cit == --(pit->end_open())) {
+ if (closed && cit == --(pit->end())) {
current_node = subpath->begin().get_pointer();
} else {
/* regardless of segment type, create a new node at the end