From: jaspervdg Date: Tue, 20 May 2008 18:25:46 +0000 (+0000) Subject: PathString now makes sure that relative coordinates are based on rounded absolute... X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=a51d6261b417a0e5f541cf0d55e8938549c51e80;p=inkscape.git PathString now makes sure that relative coordinates are based on rounded absolute coordinates (which allows for greater path data size reductions and makes sure that two identical coordinates will result in the same rounded coordinates). --- diff --git a/src/svg/path-string.cpp b/src/svg/path-string.cpp index 300650811..44b474dd2 100644 --- a/src/svg/path-string.cpp +++ b/src/svg/path-string.cpp @@ -13,7 +13,9 @@ */ #include "svg/path-string.h" +#include "svg/stringstream.h" #include "prefs-utils.h" +#include Inkscape::SVG::PathString::PathString() : allow_relative_coordinates(0 != prefs_get_int_attribute("options.svgoutput", "allowrelativecoordinates", 1)), @@ -25,7 +27,7 @@ void Inkscape::SVG::PathString::_appendOp(char abs_op, char rel_op) { bool rel_op_repeated = _rel_state.prevop == rel_op && !force_repeat_commands; unsigned int const abs_added_size = abs_op_repeated ? 0 : 2; unsigned int const rel_added_size = rel_op_repeated ? 0 : 2; - if ( _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size && allow_relative_coordinates ) { + if ( false && _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size && allow_relative_coordinates ) { // Copy rel to abs _abs_state = _rel_state; _abs_state.switches++; @@ -34,7 +36,7 @@ void Inkscape::SVG::PathString::_appendOp(char abs_op, char rel_op) { // _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size // _rel_state.str.size()+rel_added_size < _abs_state.str.size()+2 // _abs_state.str.size()+2 > _rel_state.str.size()+rel_added_size - } else if ( _abs_state.str.size()+2 < _rel_state.str.size()+rel_added_size ) { + } else if ( false && _abs_state.str.size()+2 < _rel_state.str.size()+rel_added_size ) { // Copy abs to rel _rel_state = _abs_state; _abs_state.switches++; @@ -44,6 +46,102 @@ void Inkscape::SVG::PathString::_appendOp(char abs_op, char rel_op) { if ( !rel_op_repeated ) _rel_state.appendOp(rel_op); } +void Inkscape::SVG::PathString::State::append(NR::Coord v) { + SVGOStringStream os; + os << ' ' << v; + str.append(os.str()); +} + +void Inkscape::SVG::PathString::State::append(NR::Point p) { + SVGOStringStream os; + os << ' ' << p[NR::X] << ',' << p[NR::Y]; + str.append(os.str()); +} + +void Inkscape::SVG::PathString::State::append(NR::Coord v, NR::Coord &rv) { + SVGOStringStream os; + os << ' ' << v; + str.append(os.str()); + double c; + sscanf(os.str().c_str(), " %lf", &c); + rv = c; +} + +void Inkscape::SVG::PathString::State::append(NR::Point p, NR::Point &rp) { + SVGOStringStream os; + os << ' ' << p[NR::X] << ',' << p[NR::Y]; + str.append(os.str()); + double x, y; + sscanf(os.str().c_str(), " %lf,%lf", &x, &y); + rp[NR::X] = x; + rp[NR::Y] = y; +} + +// NOTE: The following two appendRelative methods will not be exact if the total number of digits needed +// to represent the difference exceeds the precision of a double. This is not very likely though, and if +// it does happen the imprecise value is not likely to be chosen (because it will probably be a lot longer +// than the absolute value). + +void Inkscape::SVG::PathString::State::appendRelative(NR::Coord v, NR::Coord r) { + SVGOStringStream os; + int precision = (int)os.precision(); + int digitsBegin = (int)floor(log10(fabs(v-r))); // Position of first digit of difference + int digitsEnd = (int)floor(log10(std::min(fabs(v),fabs(r)))) - precision; // Position just beyond the last significant digit of the smallest (in absolute sense) number + os << ' '; + if (r == 0) { + os.precision(precision); + os << v; + } else if (v == 0) { + os.precision(precision); + os << -r; + } else if (digitsBegin>digitsEnd) { + os.precision(digitsBegin-digitsEnd); + os << (v-r); + } else { + // This assumes the input numbers are already rounded to 'precision' digits + os << '0'; + } + str.append(os.str()); +} + +void Inkscape::SVG::PathString::State::appendRelative(NR::Point p, NR::Point r) { + SVGOStringStream os; + int precision = (int)os.precision(); + int digitsBeginX = (int)floor(log10(fabs(p[NR::X]-r[NR::X]))); // Position of first digit of difference + int digitsEndX = (int)floor(log10(std::min(fabs(p[NR::X]),fabs(r[NR::X])))) - precision; // Position just beyond the last significant digit of the smallest (in absolute sense) number + int digitsBeginY = (int)floor(log10(fabs(p[NR::Y]-r[NR::Y]))); // Position of first digit of difference + int digitsEndY = (int)floor(log10(std::min(fabs(p[NR::Y]),fabs(r[NR::Y])))) - precision; // Position just beyond the last significant digit of the smallest (in absolute sense) number + os << ' '; + if (r[NR::X] == 0) { + os.precision(precision); + os << p[NR::X]; + } else if (p[NR::X] == 0) { + os.precision(precision); + os << -r[NR::X]; + } else if (digitsBeginX>digitsEndX) { + os.precision(digitsBeginX-digitsEndX); + os << (p[NR::X]-r[NR::X]); + } else { + // This assumes the input numbers are already rounded to 'precision' digits + os << '0'; + } + os << ','; + if (r[NR::Y] == 0) { + os.precision(precision); + os << p[NR::Y]; + } else if (p[NR::Y] == 0) { + os.precision(precision); + os << -r[NR::Y]; + } else if (digitsBeginY>digitsEndY) { + os.precision(digitsBeginY-digitsEndY); + os << (p[NR::Y]-r[NR::Y]); + } else { + // This assumes the input numbers are already rounded to 'precision' digits + os << '0'; + } + str.append(os.str()); +} + /* Local Variables: mode:c++ diff --git a/src/svg/path-string.h b/src/svg/path-string.h index bc0628a5a..9f06facd3 100644 --- a/src/svg/path-string.h +++ b/src/svg/path-string.h @@ -19,7 +19,6 @@ #include #include "libnr/nr-point.h" #include "libnr/nr-point-ops.h" -#include "svg/stringstream.h" namespace Inkscape { @@ -50,9 +49,9 @@ public: PathString &moveTo(NR::Point p) { _appendOp('M','m'); - _appendPoint(p); + _appendPoint(p, true); - _current_point = _initial_point = p; + _initial_point = _current_point; return *this; } @@ -62,9 +61,7 @@ public: PathString &lineTo(NR::Point p) { _appendOp('L','l'); - _appendPoint(p); - - _current_point = p; + _appendPoint(p, true); return *this; } @@ -74,10 +71,8 @@ public: PathString &quadTo(NR::Point c, NR::Point p) { _appendOp('Q','q'); - _appendPoint(c); - _appendPoint(p); - - _current_point = p; + _appendPoint(c, false); + _appendPoint(p, true); return *this; } @@ -90,11 +85,9 @@ public: PathString &curveTo(NR::Point c0, NR::Point c1, NR::Point p) { _appendOp('C','c'); - _appendPoint(c0); - _appendPoint(c1); - _appendPoint(p); - - _current_point = p; + _appendPoint(c0, false); + _appendPoint(c1, false); + _appendPoint(p, true); return *this; } @@ -107,9 +100,7 @@ public: _appendValue(rot); _appendFlag(large_arc); _appendFlag(sweep); - _appendPoint(p); - - _current_point = p; + _appendPoint(p, true); return *this; } @@ -139,19 +130,25 @@ private: _rel_state.append(p); } - void _appendX(NR::Coord x) { - _abs_state.append(x); - _rel_state.append(x-_current_point[NR::X]); + void _appendX(NR::Coord x, bool sc) { + double rx; + _abs_state.append(x, rx); + _rel_state.appendRelative(rx, _current_point[NR::X]); + if (sc) _current_point[NR::X] = rx; } - void _appendY(NR::Coord y) { - _abs_state.append(y); - _rel_state.append(y-_current_point[NR::Y]); + void _appendY(NR::Coord y, bool sc) { + double ry; + _abs_state.append(y, ry); + _rel_state.appendRelative(ry, _current_point[NR::Y]); + if (sc) _current_point[NR::Y] = ry; } - void _appendPoint(NR::Point p) { - _abs_state.append(p); - _rel_state.append(p-_current_point); + void _appendPoint(NR::Point p, bool sc) { + NR::Point rp; + _abs_state.append(p, rp); + _rel_state.appendRelative(rp, _current_point); + if (sc) _current_point = rp; } struct State { @@ -168,17 +165,12 @@ private: str.append(1, ( flag ? '1' : '0' )); } - void append(NR::Coord v) { - SVGOStringStream os; - os << ' ' << v; - str.append(os.str()); - } - - void append(NR::Point p) { - SVGOStringStream os; - os << ' ' << p[NR::X] << ',' << p[NR::Y]; - str.append(os.str()); - } + void append(NR::Coord v); + void append(NR::Point v); + void append(NR::Coord v, NR::Coord& rv); + void append(NR::Point p, NR::Point& rp); + void appendRelative(NR::Coord v, NR::Coord r); + void appendRelative(NR::Point p, NR::Point r); bool operator<=(const State& s) const { if ( str.size() < s.str.size() ) return true;