diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2016-03-01 01:42:13 +0000 |
|---|---|---|
| committer | jabiertxof <info@marker.es> | 2016-03-01 01:42:13 +0000 |
| commit | 145f815859ba21f45e67d315e52f360f029355c7 (patch) | |
| tree | 54448bc60a94415f63f7b6f46296c8d5e2ac58a6 /src | |
| parent | Improve fill rule and add mass option (diff) | |
| parent | type in commit r14664 (diff) | |
| download | inkscape-145f815859ba21f45e67d315e52f360f029355c7.tar.gz inkscape-145f815859ba21f45e67d315e52f360f029355c7.zip | |
update to trunk
(bzr r14648.1.3)
Diffstat (limited to 'src')
37 files changed, 979 insertions, 510 deletions
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp index 553981a72..934a8d3ab 100644 --- a/src/2geom/circle.cpp +++ b/src/2geom/circle.cpp @@ -144,7 +144,7 @@ bool Circle::contains(Circle const &other) const bool Circle::intersects(Line const &l) const { // http://mathworld.wolfram.com/Circle-LineIntersection.html - Coord dr = l.versor().length(); + Coord dr = l.vector().length(); Coord r = _radius; Coord D = cross(l.initialPoint(), l.finalPoint()); Coord delta = r*r * dr*dr - D*D; @@ -163,9 +163,9 @@ bool Circle::intersects(Circle const &other) const std::vector<ShapeIntersection> Circle::intersect(Line const &l) const { // http://mathworld.wolfram.com/Circle-LineIntersection.html - Coord dr = l.versor().length(); - Coord dx = l.versor().x(); - Coord dy = l.versor().y(); + Coord dr = l.vector().length(); + Coord dx = l.vector().x(); + Coord dy = l.vector().y(); Coord D = cross(l.initialPoint() - _center, l.finalPoint() - _center); Coord delta = _radius*_radius * dr*dr - D*D; diff --git a/src/2geom/conicsec.cpp b/src/2geom/conicsec.cpp index 089db71a4..2d396ba30 100644 --- a/src/2geom/conicsec.cpp +++ b/src/2geom/conicsec.cpp @@ -114,8 +114,8 @@ double RatQuad::lambda() const { RatQuad RatQuad::fromPointsTangents(Point P0, Point dP0, Point P, Point P2, Point dP2) { - Line Line0 = Line::from_origin_and_versor(P0, dP0); - Line Line2 = Line::from_origin_and_versor(P2, dP2); + Line Line0 = Line::from_origin_and_vector(P0, dP0); + Line Line2 = Line::from_origin_and_vector(P2, dP2); try { OptCrossing oc = intersection(Line0, Line2); if(!oc) // what to do? @@ -276,8 +276,8 @@ std::vector<Point> decompose_degenerate(xAx const & C1, xAx const & C2, xAx cons n1 = Point(b-d, 1); } - Line L0 = Line::from_origin_and_versor(B0, rot90(n0)); - Line L1 = Line::from_origin_and_versor(B0, rot90(n1)); + Line L0 = Line::from_origin_and_vector(B0, rot90(n0)); + Line L1 = Line::from_origin_and_vector(B0, rot90(n1)); std::vector<double> rts = C1.roots(L0); for(unsigned i = 0; i < rts.size(); i++) { @@ -327,12 +327,12 @@ std::vector<Point> decompose_degenerate(xAx const & C1, xAx const & C2, xAx cons */ assert(L2sq(g) != 0); - Line Lx = Line::from_origin_and_versor(trial_pt, g); // a line along the gradient + Line Lx = Line::from_origin_and_vector(trial_pt, g); // a line along the gradient std::vector<double> rts = xC0.roots(Lx); for(unsigned i = 0; i < rts.size(); i++) { Point P0 = Lx.pointAt(rts[i]); //std::cout << P0 << "\n"; - Line L = Line::from_origin_and_versor(P0, rot90(g)); + Line L = Line::from_origin_and_vector(P0, rot90(g)); std::vector<double> cnrts; // It's very likely that at least one of the conics is degenerate, this will hopefully pick the more generate of the two. if(fabs(C1.hessian().det()) > fabs(C2.hessian().det())) @@ -515,7 +515,7 @@ xAx xAx::operator*(double const &b) const { if(L2sq(dA) <= 1e-10) { // perhaps a single point? return boost::optional<RatQuad> (); } - LineSegment ls = intersection(Line::from_origin_and_versor(A, dA), bnd); + LineSegment ls = intersection(Line::from_origin_and_vector(A, dA), bnd); return RatQuad::fromPointsTangents(A, dA, ls.pointAt(0.5), ls[1], dA); } else if(crs.size() >= 2 && crs.size() < 4) { diff --git a/src/2geom/coord.cpp b/src/2geom/coord.cpp index 8b5e28586..8c7485883 100644 --- a/src/2geom/coord.cpp +++ b/src/2geom/coord.cpp @@ -111,12 +111,6 @@ namespace Geom { namespace { -inline int StrLength(const char* string) { - size_t length = strlen(string); - ASSERT(length == static_cast<size_t>(static_cast<int>(length))); - return static_cast<int>(length); -} - template <typename T> class Vector { public: @@ -1733,8 +1727,6 @@ enum FastDtoaMode { FAST_DTOA_PRECISION }; -static const int kFastDtoaMaximalLength = 17; - bool FastDtoa(double d, FastDtoaMode mode, int requested_digits, @@ -2350,7 +2342,6 @@ bool FastFixedDtoa(double v, return true; } -static const int kMaxExactDoubleIntegerDecimalDigits = 15; static const int kMaxUint64DecimalDigits = 19; static const int kMaxDecimalPower = 309; diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp index afde428b1..f4704460f 100644 --- a/src/2geom/ellipse.cpp +++ b/src/2geom/ellipse.cpp @@ -417,7 +417,7 @@ std::vector<ShapeIntersection> Ellipse::intersect(Line const &line) const // generic case Coord a, b, c; line.coefficients(a, b, c); - Point lv = line.versor(); + Point lv = line.vector(); if (fabs(lv[X]) > fabs(lv[Y])) { // y = -a/b x - c/b diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index ce7945ab6..e7892e2dd 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -32,6 +32,7 @@ #define LIB2GEOM_SEEN_GENERIC_INTERVAL_H #include <cassert> +#include <iostream> #include <boost/none.hpp> #include <boost/optional.hpp> #include <2geom/coord.h> @@ -338,14 +339,12 @@ inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInter return GenericOptInterval<C>(a) & GenericOptInterval<C>(b); } -#ifdef _GLIBCXX_IOSTREAM template <typename C> inline std::ostream &operator<< (std::ostream &os, Geom::GenericInterval<C> const &I) { os << "Interval("<<I.min() << ", "<<I.max() << ")"; return os; } -#endif } // namespace Geom #endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_H diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index afd2cb8a7..fdc7f369b 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -41,6 +41,7 @@ #define LIB2GEOM_SEEN_GENERIC_RECT_H #include <limits> +#include <iostream> #include <boost/optional.hpp> #include <2geom/coord.h> @@ -513,13 +514,11 @@ inline bool GenericRect<C>::contains(OptCRect const &r) const { return !r || contains(*r); } -#ifdef _GLIBCXX_IOSTREAM template <typename C> inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) { - out << "X: " << r[X] << " Y: " << r[Y]; + out << "Rect " << r[X] << " x " << r[Y]; return out; } -#endif } // end namespace Geom diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp index ee2edeba7..a9cc0e251 100644 --- a/src/2geom/line.cpp +++ b/src/2geom/line.cpp @@ -103,7 +103,7 @@ void Line::setCoefficients (Coord a, Coord b, Coord c) void Line::coefficients(Coord &a, Coord &b, Coord &c) const { - Point v = versor().cw(); + Point v = vector().cw(); a = v[X]; b = v[Y]; c = cross(_initial, _final); @@ -139,7 +139,7 @@ std::vector<Coord> Line::roots(Coord v, Dim2 d) const { Coord Line::root(Coord v, Dim2 d) const { assert(d == X || d == Y); - Point vs = versor(); + Point vs = vector(); if (vs[d] != 0) { return (v - _initial[d]) / vs[d]; } else { @@ -149,7 +149,7 @@ Coord Line::root(Coord v, Dim2 d) const boost::optional<LineSegment> Line::clip(Rect const &r) const { - Point v = versor(); + Point v = vector(); // handle horizontal and vertical lines first, // since the root-based code below will break for them for (unsigned i = 0; i < 2; ++i) { @@ -221,7 +221,7 @@ boost::optional<LineSegment> Line::clip(Rect const &r) const * @see timeAtProjection */ Coord Line::timeAt(Point const &p) const { - Point v = versor(); + Point v = vector(); // degenerate case if (v[X] == 0 && v[Y] == 0) { return 0; @@ -243,8 +243,8 @@ Coord Line::timeAt(Point const &p) const Affine Line::transformTo(Line const &other) const { Affine result = Translate(-_initial); - result *= Rotate(angle_between(versor(), other.versor())); - result *= Scale(other.versor().length() / versor().length()); + result *= Rotate(angle_between(vector(), other.vector())); + result *= Scale(other.vector().length() / vector().length()); result *= Translate(other._initial); return result; } @@ -253,8 +253,8 @@ std::vector<ShapeIntersection> Line::intersect(Line const &other) const { std::vector<ShapeIntersection> result; - Point v1 = versor(); - Point v2 = other.versor(); + Point v1 = vector(); + Point v2 = other.vector(); Coord cp = cross(v1, v2); if (cp == 0) return result; @@ -333,8 +333,8 @@ OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i) using std::swap; OptCrossing crossing = - intersection_impl(r1.versor(), r1.origin(), - l2.versor(), l2.origin() ); + intersection_impl(r1.vector(), r1.origin(), + l2.vector(), l2.origin() ); if (crossing) { if (crossing->ta < 0) { @@ -363,7 +363,7 @@ OptCrossing intersection_impl( LineSegment const& ls1, OptCrossing crossing = intersection_impl(ls1.finalPoint() - ls1.initialPoint(), ls1.initialPoint(), - l2.versor(), + l2.vector(), l2.origin() ); if (crossing) { @@ -396,7 +396,7 @@ OptCrossing intersection_impl( LineSegment const& ls1, OptCrossing crossing = intersection_impl( direction, ls1.initialPoint(), - r2.versor(), + r2.vector(), r2.origin() ); if (crossing) { @@ -414,7 +414,7 @@ OptCrossing intersection_impl( LineSegment const& ls1, } if ( are_near(r2.origin(), ls1) ) { - bool eqvs = (dot(direction, r2.versor()) > 0); + bool eqvs = (dot(direction, r2.vector()) > 0); if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs) { crossing->ta = crossing->tb = 0; return crossing; @@ -445,8 +445,8 @@ OptCrossing intersection_impl( LineSegment const& ls1, OptCrossing intersection(Line const& l1, Line const& l2) { OptCrossing c = detail::intersection_impl( - l1.versor(), l1.origin(), - l2.versor(), l2.origin()); + l1.vector(), l1.origin(), + l2.vector(), l2.origin()); if (!c && distance(l1.origin(), l2) == 0) { THROW_INFINITESOLUTIONS(); @@ -457,8 +457,8 @@ OptCrossing intersection(Line const& l1, Line const& l2) OptCrossing intersection(Ray const& r1, Ray const& r2) { OptCrossing crossing = - detail::intersection_impl( r1.versor(), r1.origin(), - r2.versor(), r2.origin() ); + detail::intersection_impl( r1.vector(), r1.origin(), + r2.vector(), r2.origin() ); if (crossing) { @@ -477,7 +477,7 @@ OptCrossing intersection(Ray const& r1, Ray const& r2) if ( are_near(r1.origin(), r2) || are_near(r2.origin(), r1) ) { if ( are_near(r1.origin(), r2.origin()) - && !are_near(r1.versor(), r2.versor()) ) + && !are_near(r1.vector(), r2.vector()) ) { crossing->ta = crossing->tb = 0; return crossing; @@ -582,7 +582,7 @@ Line make_angle_bisector_line(Line const& l1, Line const& l2) } Point O = l1.pointAt(crossing->ta); Point A = l1.pointAt(crossing->ta + 1); - double angle = angle_between(l1.versor(), l2.versor()); + double angle = angle_between(l1.vector(), l2.vector()); Point B = (angle > 0) ? l2.pointAt(crossing->tb + 1) : l2.pointAt(crossing->tb - 1); diff --git a/src/2geom/line.h b/src/2geom/line.h index 8a66d4472..62b3652f1 100644 --- a/src/2geom/line.h +++ b/src/2geom/line.h @@ -98,7 +98,7 @@ public: /// Create a line by extending a ray. explicit Line(Ray const &r) : _initial(r.origin()) - , _final(r.origin() + r.versor()) + , _final(r.origin() + r.vector()) {} /// Create a line normal to a vector at a specified distance from origin. @@ -111,7 +111,7 @@ public: * Note that each line direction has two possible unit vectors. * @param o Point through which the line will pass * @param v Unit vector of the line's direction */ - static Line from_origin_and_versor(Point const &o, Point const &v) { + static Line from_origin_and_vector(Point const &o, Point const &v) { Line l(o, o + v); return l; } @@ -126,9 +126,12 @@ public: /// Get the line's origin point. Point origin() const { return _initial; } - /** @brief Get the line's direction vector. - * Note that the retrieved vector is not normalized to unit length. */ - Point versor() const { return _final - _initial; } + /** @brief Get the line's raw direction vector. + * The retrieved vector is normalized to unit length. */ + Point vector() const { return _final - _initial; } + /** @brief Get the line's normalized direction vector. + * The retrieved vector is normalized to unit length. */ + Point versor() const { return (_final - _initial).normalized(); } /// Angle the line makes with the X axis, in mathematical convention. Coord angle() const { Point d = _final - _initial; @@ -147,7 +150,7 @@ public: } /** @brief Set the speed of the line. * Origin remains unchanged. */ - void setVersor(Point const &v) { + void setVector(Point const &v) { _final = _initial + v; } @@ -239,7 +242,7 @@ public: * @return Time value corresponding to a point closest to @c p. */ Coord timeAtProjection(Point const& p) const { if ( isDegenerate() ) return 0; - Point v = versor(); + Point v = vector(); return dot(p - _initial, v) / dot(v, v); } @@ -284,22 +287,22 @@ public: boost::optional<LineSegment> clip(Rect const &r) const; /** @brief Create a ray starting at the specified time value. - * The created ray will go in the direction of the line's versor (in the direction + * The created ray will go in the direction of the line's vector (in the direction * of increasing time values). * @param t Time value where the ray should start - * @return Ray starting at t and going in the direction of the versor */ + * @return Ray starting at t and going in the direction of the vector */ Ray ray(Coord t) { Ray result; result.setOrigin(pointAt(t)); - result.setVersor(versor()); + result.setVector(vector()); return result; } /** @brief Create a derivative of the line. * The new line will always be degenerate. Its origin will be equal to this - * line's versor. */ + * line's vector. */ Line derivative() const { - Point v = versor(); + Point v = vector(); Line result(v, v); return result; } @@ -314,7 +317,7 @@ public: * If Y grows upwards, then this is the left normal. If Y grows downwards, * then this is the right normal. */ Point normal() const { - return rot90(versor()).normalized(); + return rot90(vector()).normalized(); } // what does this do? @@ -326,7 +329,7 @@ public: /// Compute an affine matrix representing a reflection about the line. Affine reflection() const { - Point v = versor().normalized(); + Point v = versor(); Coord x2 = v[X]*v[X], y2 = v[Y]*v[Y], xy = v[X]*v[Y]; Affine m(x2-y2, 2.*xy, 2.*xy, y2-x2, @@ -344,7 +347,7 @@ public: * lower precision than e.g. a shear transform. * @param d Which coordinate of points on the line should be zero after the transformation */ Affine rotationToZero(Dim2 d) const { - Point v = versor(); + Point v = vector(); if (d == X) { std::swap(v[X], v[Y]); } else { @@ -421,7 +424,7 @@ bool are_near(Point const &p, Line const &line, double eps = EPSILON) inline bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON) { - return are_near(cross(l1.versor(), l2.versor()), 0, eps); + return are_near(cross(l1.vector(), l2.vector()), 0, eps); } /** @brief Test whether two lines are approximately the same. @@ -442,7 +445,7 @@ bool are_same(Line const &l1, Line const &l2, double eps = EPSILON) inline bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON) { - return are_near(dot(l1.versor(), l2.versor()), 0, eps); + return are_near(dot(l1.vector(), l2.vector()), 0, eps); } // evaluate the angle between l1 and l2 rotating l1 in cw direction @@ -451,7 +454,7 @@ bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON) inline double angle_between(Line const& l1, Line const& l2) { - double angle = angle_between(l1.versor(), l2.versor()); + double angle = angle_between(l1.vector(), l2.vector()); if (angle < 0) angle += M_PI; if (angle == M_PI) angle = 0; return angle; @@ -474,7 +477,7 @@ bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON) inline Line make_orthogonal_line(Point const &p, Line const &line) { - Point d = line.versor().cw(); + Point d = line.vector().cw(); Line l(p, p + d); return l; } diff --git a/src/2geom/ray.h b/src/2geom/ray.h index a336e3132..4e60fd814 100644 --- a/src/2geom/ray.h +++ b/src/2geom/ray.h @@ -53,61 +53,62 @@ namespace Geom class Ray { private: Point _origin; - Point _versor; + Point _vector; public: - Ray() : _origin(0,0), _versor(1,0) {} + Ray() : _origin(0,0), _vector(1,0) {} Ray(Point const& origin, Coord angle) : _origin(origin) { - sincos(angle, _versor[Y], _versor[X]); + sincos(angle, _vector[Y], _vector[X]); } Ray(Point const& A, Point const& B) { setPoints(A, B); } Point origin() const { return _origin; } - Point versor() const { return _versor; } + Point vector() const { return _vector; } + Point versor() const { return _vector.normalized(); } void setOrigin(Point const &o) { _origin = o; } - void setVersor(Point const& v) { _versor = v; } - Coord angle() const { return std::atan2(_versor[Y], _versor[X]); } - void setAngle(Coord a) { sincos(a, _versor[Y], _versor[X]); } + void setVector(Point const& v) { _vector = v; } + Coord angle() const { return std::atan2(_vector[Y], _vector[X]); } + void setAngle(Coord a) { sincos(a, _vector[Y], _vector[X]); } void setPoints(Point const &a, Point const &b) { _origin = a; - _versor = b - a; - if (are_near(_versor, Point(0,0)) ) - _versor = Point(0,0); + _vector = b - a; + if (are_near(_vector, Point(0,0)) ) + _vector = Point(0,0); else - _versor.normalize(); + _vector.normalize(); } bool isDegenerate() const { - return ( _versor[X] == 0 && _versor[Y] == 0 ); + return ( _vector[X] == 0 && _vector[Y] == 0 ); } Point pointAt(Coord t) const { - return _origin + _versor * t; + return _origin + _vector * t; } Coord valueAt(Coord t, Dim2 d) const { - return _origin[d] + _versor[d] * t; + return _origin[d] + _vector[d] * t; } std::vector<Coord> roots(Coord v, Dim2 d) const { std::vector<Coord> result; - if ( _versor[d] != 0 ) { - double t = (v - _origin[d]) / _versor[d]; + if ( _vector[d] != 0 ) { + double t = (v - _origin[d]) / _vector[d]; if (t >= 0) result.push_back(t); - } else if (_versor[(d+1)%2] == v) { + } else if (_vector[(d+1)%2] == v) { THROW_INFINITESOLUTIONS(); } return result; } Coord nearestTime(Point const& point) const { if ( isDegenerate() ) return 0; - double t = dot(point - _origin, _versor); + double t = dot(point - _origin, _vector); if (t < 0) t = 0; return t; } Ray reverse() const { Ray result; result.setOrigin(_origin); - result.setVersor(-_versor); + result.setVector(-_vector); return result; } Curve *portion(Coord f, Coord t) const { @@ -117,7 +118,7 @@ public: return LineSegment(pointAt(f), pointAt(t)); } Ray transformed(Affine const& m) const { - return Ray(_origin * m, (_origin + _versor) * m); + return Ray(_origin * m, (_origin + _vector) * m); } }; // end class Ray @@ -134,7 +135,7 @@ bool are_near(Point const& _point, Ray const& _ray, double eps = EPSILON) { inline bool are_same(Ray const& r1, Ray const& r2, double eps = EPSILON) { - return are_near(r1.versor(), r2.versor(), eps) + return are_near(r1.vector(), r2.vector(), eps) && are_near(r1.origin(), r2.origin(), eps); } @@ -142,7 +143,7 @@ bool are_same(Ray const& r1, Ray const& r2, double eps = EPSILON) { // the returned value is an angle in the interval [0, 2PI[ inline double angle_between(Ray const& r1, Ray const& r2, bool cw = true) { - double angle = angle_between(r1.versor(), r2.versor()); + double angle = angle_between(r1.vector(), r2.vector()); if (angle < 0) angle += 2*M_PI; if (!cw) angle = 2*M_PI - angle; return angle; @@ -170,7 +171,7 @@ Ray make_angle_bisector_ray(Ray const& r1, Ray const& r2, bool cw = true) THROW_RANGEERROR("passed rays do not have the same origin"); } - Ray bisector(r1.origin(), r1.origin() + r1.versor() * Rotate(angle_between(r1, r2) / 2.0)); + Ray bisector(r1.origin(), r1.origin() + r1.vector() * Rotate(angle_between(r1, r2) / 2.0)); return (cw ? bisector : bisector.reverse()); } diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index d9a90aace..8a18cfd4a 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -205,11 +205,11 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb) xprime[i] = sb[X][0][1] - sb[X][0][0]; yprime[i] = sb[Y][0][1] - sb[Y][0][0]; } - if (sb[X].size() > 0) { + if (sb[X].size() > 1) { xprime[0] += sb[X][1][0]; xprime[1] -= sb[X][1][1]; } - if (sb[Y].size() > 0) { + if (sb[Y].size() > 1) { yprime[0] += sb[Y][1][0]; yprime[1] -= sb[Y][1][1]; } diff --git a/src/2geom/sweeper.h b/src/2geom/sweeper.h index b3c96455f..469108465 100644 --- a/src/2geom/sweeper.h +++ b/src/2geom/sweeper.h @@ -52,10 +52,10 @@ public: {} std::vector<Item> const &items() { return _items; } - Interval itemBounds(ItemIterator ii) { return Interval(); } + Interval itemBounds(ItemIterator /*ii*/) { return Interval(); } - void addActiveItem(ItemIterator ii) {} - void removeActiveItem(ItemIterator ii) {} + void addActiveItem(ItemIterator /*ii*/) {} + void removeActiveItem(ItemIterator /*ii*/) {} private: std::vector<Item> const &_items; @@ -104,9 +104,9 @@ public: } /** @brief Process entry and exit events. - * This will iterate over all inserted items, calling the virtual protected - * functions _enter() and _leave() according to the order of the boundaries - * of each item. */ + * This will iterate over all inserted items, calling the methods + * addActiveItem and removeActiveItem on the SweepSet passed at construction + * according to the order of the boundaries of each item. */ void process() { if (_set.items().empty()) return; diff --git a/src/2geom/utils.h b/src/2geom/utils.h index 3e0dd4717..01579d81b 100644 --- a/src/2geom/utils.h +++ b/src/2geom/utils.h @@ -68,7 +68,7 @@ struct NullIterator NullIterator() {} template <typename T> - void operator=(T const &v) {} + void operator=(T const &) {} }; /** @brief Get the next iterator in the container with wrap-around. diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index d43d000a7..9190fe633 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -296,14 +296,14 @@ static Avoid::Polygon avoid_item_poly(SPItem const *item) Geom::Line hull_edge(hull[-1], hull[0]); Geom::Line prev_parallel_hull_edge; prev_parallel_hull_edge.setOrigin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); - prev_parallel_hull_edge.setVersor(hull_edge.versor()); + prev_parallel_hull_edge.setVector(hull_edge.versor()); int hull_size = hull.size(); for (int i = 0; i < hull_size; ++i) { hull_edge.setPoints(hull[i], hull[i+1]); Geom::Line parallel_hull_edge; parallel_hull_edge.setOrigin(hull_edge.origin()+hull_edge.versor().ccw()*spacing); - parallel_hull_edge.setVersor(hull_edge.versor()); + parallel_hull_edge.setVector(hull_edge.versor()); // determine the intersection point try { diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 67c0687b7..a81cbdd1f 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -1267,7 +1267,7 @@ objects_query_writing_modes (const std::vector<SPItem*> &objects, SPStyle *style int texts = 0; - for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); i++) { + for (std::vector<SPItem*>::const_iterator i = objects.begin(); i != objects.end(); ++i) { SPObject *obj = *i; if (!isTextualItem(obj)) { diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 22765cb9d..d17271752 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -607,7 +607,7 @@ void sp_canvas_item_raise(SPCanvasItem *item, int positions) g_assert (l != parent->items.end()); for (int i=0; i<=positions && l != parent->items.end(); ++i) - l++; + ++l; parent->items.remove(item); parent->items.insert(l, item); @@ -656,7 +656,7 @@ void sp_canvas_item_lower(SPCanvasItem *item, int positions) g_assert (l != parent->items.end()); for (int i=0; i<positions && l != parent->items.begin(); ++i) - l--; + --l; parent->items.remove(item); parent->items.insert(l, item); diff --git a/src/document.cpp b/src/document.cpp index d4bd29386..ae03b1ba1 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -108,7 +108,8 @@ SPDocument::SPDocument() : oldSignalsConnected(false), current_persp3d(NULL), current_persp3d_impl(NULL), - _parent_document(NULL) + _parent_document(NULL), + _node_cache_valid(false) { // Penalise libavoid for choosing paths with needless extra segments. // This results in much better looking orthogonal connector paths. @@ -130,6 +131,7 @@ SPDocument::SPDocument() : // XXX only for testing! priv->undoStackObservers.add(p->console_output_undo_observer); + _node_cache = std::deque<SPItem*>(); } SPDocument::~SPDocument() { @@ -986,6 +988,7 @@ void SPDocument::_emitModified() { static guint const flags = SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG; root->emitModified(0); priv->modified_signal.emit(flags); + _node_cache_valid=false; } void SPDocument::bindObjectToId(gchar const *id, SPObject *object) { @@ -1332,34 +1335,24 @@ SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *gro /** Turn the SVG DOM into a flat list of nodes that can be searched from top-down. The list can be persisted, which improves "find at multiple points" speed. -Returns true if upto is reached. */ -static bool build_flat_item_list(std::deque<SPItem*> *nodes, unsigned int dkey, SPGroup *group, gboolean into_groups, bool take_insensitive = false, SPItem *upto = NULL) +void SPDocument::build_flat_item_list(unsigned int dkey, SPGroup *group, gboolean into_groups) const { - bool found_upto = false; for ( SPObject *o = group->firstChild() ; o ; o = o->getNext() ) { if (!SP_IS_ITEM(o)) { continue; } - if (upto && SP_ITEM(o) == upto) { - found_upto = true; - break; - } - if (SP_IS_GROUP(o) && (SP_GROUP(o)->effectiveLayerMode(dkey) == SPGroup::LAYER || into_groups)) { - found_upto = build_flat_item_list(nodes, dkey, SP_GROUP(o), into_groups, take_insensitive, upto); - if (found_upto) - break; + build_flat_item_list(dkey, SP_GROUP(o), into_groups); } else { SPItem *child = SP_ITEM(o); - if (take_insensitive || child->isVisibleAndUnlocked(dkey)) { - nodes->push_front(child); + if (child->isVisibleAndUnlocked(dkey)) { + _node_cache.push_front(child); } } } - return found_upto; } /** @@ -1371,15 +1364,21 @@ upwards in z-order and returns what it has found so far (i.e. the found item is guaranteed to be lower than upto). Requires a list of nodes built by build_flat_item_list. */ -static SPItem *find_item_at_point(std::deque<SPItem*> *nodes, unsigned int dkey, Geom::Point const &p) +static SPItem *find_item_at_point(std::deque<SPItem*> *nodes, unsigned int dkey, Geom::Point const &p, SPItem* upto=NULL) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gdouble delta = prefs->getDouble("/options/cursortolerance/value", 1.0); SPItem *seen = NULL; SPItem *child; - for (unsigned long i = 0; i < nodes->size(); ++i) { - child = nodes->at(i); + bool seen_upto = (!upto); + for (std::deque<SPItem*>::const_iterator i = nodes->begin(); i != nodes->end(); ++i) { + child = *i; + if (!seen_upto){ + if(child == upto) + seen_upto = true; + continue; + } Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey); if (arenaitem && arenaitem->pick(p, delta, 1) != NULL) { @@ -1451,7 +1450,7 @@ std::vector<SPItem*> SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom: return find_items_in_area(x, SP_GROUP(this->root), dkey, box, overlaps, false, into_groups); } -std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers, size_t limit) const +std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers, size_t limit) const { std::vector<SPItem*> items; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1463,8 +1462,11 @@ std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vecto prefs->setDouble("/options/cursortolerance/value", 0.25); // Cache a flattened SVG DOM to speed up selection. - std::deque<SPItem*> nodes; - build_flat_item_list(&nodes, key, SP_GROUP(this->root), true, false, NULL); + if(!_node_cache_valid){ + _node_cache.clear(); + build_flat_item_list(key, SP_GROUP(this->root), true); + _node_cache_valid=true; + } SPObject *current_layer = SP_ACTIVE_DESKTOP->currentLayer(); SPDesktop *desktop = SP_ACTIVE_DESKTOP; Inkscape::LayerModel *layer_model = NULL; @@ -1473,7 +1475,7 @@ std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vecto } size_t item_counter = 0; for(int i = points.size()-1;i>=0; i--) { - SPItem *item = find_item_at_point(&nodes, key, points[i]); + SPItem *item = find_item_at_point(&_node_cache, key, points[i]); if (item && items.end()==find(items.begin(),items.end(), item)) if(all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)){ items.push_back(item); @@ -1493,15 +1495,26 @@ std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vecto } SPItem *SPDocument::getItemAtPoint( unsigned const key, Geom::Point const &p, - bool const into_groups, SPItem *upto) const + bool const into_groups, SPItem *upto) const { g_return_val_if_fail(this->priv != NULL, NULL); // Build a flattened SVG DOM for find_item_at_point. - std::deque<SPItem*> nodes; - build_flat_item_list(&nodes, key, SP_GROUP(this->root), into_groups, false, upto); - - return find_item_at_point(&nodes, key, p); + std::deque<SPItem*> bak(_node_cache); + if(!into_groups){ + _node_cache.clear(); + build_flat_item_list(key, SP_GROUP(this->root), into_groups); + } + if(!_node_cache_valid && into_groups){ + _node_cache.clear(); + build_flat_item_list(key, SP_GROUP(this->root), true); + _node_cache_valid=true; + } + + SPItem *res = find_item_at_point(&_node_cache, key, p, upto); + if(!into_groups) + _node_cache = bak; + return res; } SPItem *SPDocument::getGroupAtPoint(unsigned int key, Geom::Point const &p) const diff --git a/src/document.h b/src/document.h index b4a8a8e8e..825049cd5 100644 --- a/src/document.h +++ b/src/document.h @@ -28,6 +28,7 @@ #include <boost/ptr_container/ptr_list.hpp> #include <vector> #include <set> +#include <deque> namespace Avoid { class Router; @@ -263,7 +264,7 @@ public: std::vector<SPItem*> getItemsInBox(unsigned int dkey, Geom::Rect const &box, bool into_groups = false) const; std::vector<SPItem*> getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool into_groups = false) const; SPItem *getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto = NULL) const; - std::vector<SPItem*> getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers = true, size_t limit = 0) const; + std::vector<SPItem*> getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers = true, size_t limit = 0) const ; SPItem *getGroupAtPoint(unsigned int key, Geom::Point const &p) const; void changeUriAndHrefs(char const *uri); @@ -277,6 +278,9 @@ private: void do_change_uri(char const *const filename, bool const rebase); void setupViewport(SPItemCtx *ctx); void importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, Inkscape::XML::Node *target_defs); + void build_flat_item_list(unsigned int dkey, SPGroup *group, gboolean into_groups) const; + mutable std::deque<SPItem*> _node_cache; + mutable bool _node_cache_valid; }; /* diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index f4789a08f..a4e26e224 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -22,6 +22,7 @@ #include <string> #include <cstring> +#include <glibmm/regex.h> #include <libcdr/libcdr.h> @@ -281,7 +282,12 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u } } - SPDocument * doc = SPDocument::createNewDocFromMem(tmpSVGOutput[page_num-1].cstr(), strlen(tmpSVGOutput[page_num-1].cstr()), TRUE); + // remove consecutive closepath commands (see bug 1492153) + Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("(Z(?:\\s+Z)+)(?=[^<]+\")"); + Glib::ustring outString1 = Glib::ustring(tmpSVGOutput[page_num-1].cstr()); + Glib::ustring outString2 = regex->replace(outString1, 0, "Z", Glib::REGEX_MATCH_NEWLINE_ANY); + + SPDocument * doc = SPDocument::createNewDocFromMem(outString2.c_str(), outString2.bytes(), TRUE); // Set viewBox if it doesn't exist if (doc && !doc->getRoot()->viewBox_set) { diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 68b38f5ee..13520c40b 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1617,6 +1617,17 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA uint32_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written U_COLORREF tbkColor = U_RGB(255, 255, 255); // holds proposed change to bkColor + // code for end user debugging + int eDbgRecord=0; + int eDbgComment=0; + int eDbgFinal=0; + char const* eDbgString = getenv( "INKSCAPE_DBG_EMF" ); + if ( eDbgString != NULL ) { + if(strstr(eDbgString,"RECORD")){ eDbgRecord = 1; } + if(strstr(eDbgString,"COMMENT")){ eDbgComment = 1; } + if(strstr(eDbgString,"FINAL")){ eDbgFinal = 1; } + } + /* initialize the tsp for text reassembly */ tsp.string = NULL; tsp.ori = 0.0; /* degrees */ @@ -1659,8 +1670,11 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA lpEMFR = (PU_ENHMETARECORD)(contents + off); -// Uncomment the following to track down toxic records -// std::cout << "record type: " << iType << " name " << U_emr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; +// At run time define environment variable INKSCAPE_DBG_EMF to include string RECORD. +// Users may employ this to track down toxic records + if(eDbgRecord){ + std::cout << "record type: " << iType << " name " << U_emr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; + } off += nSize; SVGOStringStream tmp_outsvg; @@ -3475,14 +3489,20 @@ std::cout << "BEFORE DRAW" dbg_str << "<!-- U_EMR_??? -->\n"; break; } //end of switch -// When testing, uncomment the following to place a comment for each processed EMR record in the SVG -// d->outsvg += dbg_str.str().c_str(); +// At run time define environment variable INKSCAPE_DBG_EMF to include string COMMENT. +// Users may employ this to to place a comment for each processed EMR record in the SVG + if(eDbgComment){ + d->outsvg += dbg_str.str().c_str(); + } d->outsvg += tmp_outsvg.str().c_str(); d->path += tmp_path.str().c_str(); } //end of while -// When testing, uncomment the following to show the final SVG derived from the EMF -// std::cout << d->outsvg << std::endl; +// At run time define environment variable INKSCAPE_DBG_EMF to include string FINAL +// Users may employ this to to show the final SVG derived from the EMF + if(eDbgFinal){ + std::cout << d->outsvg << std::endl; + } (void) emr_properties(U_EMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant return(file_status); diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index d17180d91..b8b0c73de 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -1528,6 +1528,17 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK uint16_t tbkMode = U_TRANSPARENT; // holds proposed change to bkMode, if text is involved saving these to the DC must wait until the text is written U_COLORREF tbkColor = U_RGB(255, 255, 255); // holds proposed change to bkColor + // code for end user debugging + int wDbgRecord=0; + int wDbgComment=0; + int wDbgFinal=0; + char const* wDbgString = getenv( "INKSCAPE_DBG_WMF" ); + if ( wDbgString != NULL ) { + if(strstr(wDbgString,"RECORD")){ wDbgRecord = 1; } + if(strstr(wDbgString,"COMMENT")){ wDbgComment = 1; } + if(strstr(wDbgString,"FINAL")){ wDbgFinal = 1; } + } + /* initialize the tsp for text reassembly */ tsp.string = NULL; tsp.ori = 0.0; /* degrees */ @@ -1692,8 +1703,11 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK file_status = 0; break; } -// Uncomment the following to track down toxic records -// std::cout << "record type: " << (int) iType << " name " << U_wmr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; +// At run time define environment variable INKSCAPE_DBG_WMF to include string RECORD. +// Users may employ this to track down toxic records + if(wDbgRecord){ + std::cout << "record type: " << iType << " name " << U_wmr_names(iType) << " length: " << nSize << " offset: " << off <<std::endl; + } SVGOStringStream tmp_path; SVGOStringStream tmp_str; @@ -3034,8 +3048,11 @@ std::cout << "BEFORE DRAW" dbg_str << "<!-- U_WMR_??? -->\n"; break; } //end of switch -// When testing, uncomment the following to place a comment for each processed WMR record in the SVG -// d->outsvg += dbg_str.str().c_str(); +// At run time define environment variable INKSCAPE_DBG_WMF to include string COMMENT. +// Users may employ this to to place a comment for each processed WMR record in the SVG + if(wDbgComment){ + d->outsvg += dbg_str.str().c_str(); + } d->path += tmp_path.str().c_str(); if(!nSize){ // There was some problem with the processing of this record, it is not safe to continue file_status = 0; @@ -3043,8 +3060,11 @@ std::cout << "BEFORE DRAW" } } //end of while on OK -// When testing, uncomment the following to show the final SVG derived from the WMF -// std::cout << d->outsvg << std::endl; +// At run time define environment variable INKSCAPE_DBG_WMF to include string FINAL +// Users may employ this to to show the final SVG derived from the WMF + if(wDbgFinal){ + std::cout << d->outsvg << std::endl; + } (void) U_wmr_properties(U_WMR_INVALID); // force the release of the lookup table memory, returned value is irrelevant return(file_status); diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index c73a9e9e7..3ef4b15da 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -15,6 +15,7 @@ #include <2geom/sbasis-to-bezier.h> // cubicbezierpath_from_sbasis #include <2geom/path-intersection.h> #include <2geom/circle.h> +#include <math.h> #include "helper/geom-pathstroke.h" @@ -55,6 +56,53 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0 return Geom::Circle(center, fabs(radius)); } + +// Area of triangle given three corner points +static double area( Geom::Point a, Geom::Point b, Geom::Point c ) { + + using Geom::X; + using Geom::Y; + return( 0.5 * fabs( ( a[X]*(b[Y]-c[Y]) + b[X]*(c[Y]-a[Y]) + c[X]*(a[Y]-b[Y]) ) ) ); +} + +// Alternative touching circle routine directly using Beziers. Works only at end points. +static Circle touching_circle( CubicBezier const &curve, bool start ) { + + double k = 0; + Geom::Point p; + Geom::Point normal; + if ( start ) { + double distance = Geom::distance( curve[1], curve[0] ); + k = 4.0/3.0 * area( curve[0], curve[1], curve[2] ) / + (distance * distance * distance); + if( Geom::cross(curve[0]-curve[1], curve[1]-curve[2]) < 0 ) { + k = -k; + } + p = curve[0]; + normal = Geom::Point(curve[1] - curve[0]).cw(); + normal.normalize(); + // std::cout << "Start k: " << k << " d: " << distance << " normal: " << normal << std::endl; + } else { + double distance = Geom::distance( curve[3], curve[2] ); + k = 4.0/3.0 * area( curve[1], curve[2], curve[3] ) / + (distance * distance * distance); + if( Geom::cross(curve[1]-curve[2], curve[2]-curve[3]) < 0 ) { + k = -k; + } + p = curve[3]; + normal = Geom::Point(curve[3] - curve[2]).cw(); + normal.normalize(); + // std::cout << "End k: " << k << " d: " << distance << " normal: " << normal << std::endl; + } + if( k == 0 ) { + return Geom::Circle( Geom::Point(0,std::numeric_limits<float>::infinity()), + std::numeric_limits<float>::infinity()); + } else { + double radius = 1/k; + Geom::Point center = p + normal * radius; + return Geom::Circle( center, fabs(radius) ); + } +} } namespace { @@ -77,7 +125,7 @@ struct join_data { // line parameters double miter; - double width; + double width; // half stroke width }; // Join functions must append the outgoing path @@ -115,7 +163,9 @@ void miter_join_internal(join_data jd, bool clip) if (p.isFinite()) { // check size of miter Point point_on_path = incoming.finalPoint() + rot90(tang1)*width; - satisfied = distance(p, point_on_path) <= miter * 2.0 * width; + // SVG defines miter length as distance between inner intersection and outer intersection, + // which is twice the distance from p to point_on_path but width is half stroke width. + satisfied = distance(p, point_on_path) <= miter * width; if (satisfied) { // miter OK, check to see if we can do a relocation if (inc_ls) { @@ -124,13 +174,16 @@ void miter_join_internal(join_data jd, bool clip) res.appendNew<LineSegment>(p); } } else if (clip) { + // std::cout << " Clipping ------------ " << std::endl; // miter needs clipping, find two points Point bisector_versor = Line(point_on_path, p).versor(); - Point point_limit = point_on_path + miter * 2.0 * width * bisector_versor; - + Point point_limit = point_on_path + miter * width * bisector_versor; + // std::cout << " bisector_versor: " << bisector_versor << std::endl; + // std::cout << " point_limit: " << point_limit << std::endl; Point p1 = intersection_point(incoming.finalPoint(), tang1, point_limit, bisector_versor.cw()); Point p2 = intersection_point(outgoing.initialPoint(), tang2, point_limit, bisector_versor.cw()); - + // std::cout << " p1: " << p1 << std::endl; + // std::cout << " p2: " << p2 << std::endl; if (inc_ls) { res.setFinal(p1); } else { @@ -176,8 +229,132 @@ Geom::Point pick_solution(std::vector<Geom::ShapeIntersection> points, Geom::Poi return sol; } -void extrapolate_join(join_data jd) +// Arcs line join. If two circles don't intersect, expand inner circle. +Geom::Point expand_circle( Geom::Circle &inner_circle, Geom::Circle const &outer_circle, Geom::Point const &start_pt, Geom::Point const &start_tangent ) { + // std::cout << "expand_circle:" << std::endl; + // std::cout << " outer_circle: radius: " << outer_circle.radius() << " center: " << outer_circle.center() << std::endl; + // std::cout << " start: point: " << start_pt << " tangent: " << start_tangent << std::endl; + + if( !(outer_circle.contains(start_pt) ) ) { + // std::cout << " WARNING: Outer circle does not contain starting point!" << std::endl; + return Geom::Point(0,0); + } + + Geom::Line secant1(start_pt, start_pt + start_tangent); + std::vector<Geom::ShapeIntersection> chord1_pts = outer_circle.intersect(secant1); + // std::cout << " chord1: " << chord1_pts[0].point() << ", " << chord1_pts[1].point() << std::endl; + Geom::LineSegment chord1(chord1_pts[0].point(), chord1_pts[1].point()); + + Geom::Line bisector = make_bisector_line( chord1 ); + std::vector<Geom::ShapeIntersection> chord2_pts = outer_circle.intersect(bisector); + // std::cout << " chord2: " << chord2_pts[0].point() << ", " << chord2_pts[1].point() << std::endl; + Geom::LineSegment chord2(chord2_pts[0].point(), chord2_pts[1].point()); + + // Find D, point on chord2 and on circle closest to start point + Geom::Coord d0 = Geom::distance(chord2_pts[0].point(),start_pt); + Geom::Coord d1 = Geom::distance(chord2_pts[1].point(),start_pt); + // std::cout << " d0: " << d0 << " d1: " << d1 << std::endl; + Geom::Point d = (d0 < d1) ? chord2_pts[0].point() : chord2_pts[1].point(); + Geom::Line da(d,start_pt); + + // Chord through start point and point D + std::vector<Geom::ShapeIntersection> chord3_pts = outer_circle.intersect(da); + // std::cout << " chord3: " << chord3_pts[0].point() << ", " << chord3_pts[1].point() << std::endl; + + // Find farthest point on chord3 and on circle (could be more robust) + Geom::Coord d2 = Geom::distance(chord3_pts[0].point(),d); + Geom::Coord d3 = Geom::distance(chord3_pts[1].point(),d); + // std::cout << " d2: " << d2 << " d3: " << d3 << std::endl; + + // Find point P, the intersection of outer circle and new inner circle + Geom::Point p = (d2 > d3) ? chord3_pts[0].point() : chord3_pts[1].point(); + + // Find center of new circle: it is at the intersection of the bisector + // of the chord defined by the start point and point P and a line through + // the start point and parallel to the first bisector. + Geom::LineSegment chord4(start_pt,p); + Geom::Line bisector2 = make_bisector_line( chord4 ); + Geom::Line diameter = make_parallel_line( start_pt, bisector ); + std::vector<Geom::ShapeIntersection> center_new = bisector2.intersect( diameter ); + // std::cout << " center_new: " << center_new[0].point() << std::endl; + Geom::Coord r_new = Geom::distance( center_new[0].point(), start_pt ); + // std::cout << " r_new: " << r_new << std::endl; + + inner_circle.setCenter( center_new[0].point() ); + inner_circle.setRadius( r_new ); + return p; +} + +// Arcs line join. If two circles don't intersect, adjust both circles so they just touch. +// Increase (decrease) the radius of circle 1 and decrease (increase) of circle 2 by the same amount keeping the given points and tangents fixed. +Geom::Point adjust_circles( Geom::Circle &circle1, Geom::Circle &circle2, Geom::Point const &point1, Geom::Point const &point2, Geom::Point const &tan1, Geom::Point const &tan2 ) { + + Geom::Point n1 = (circle1.center() - point1).normalized(); // Always points towards center + Geom::Point n2 = (circle2.center() - point2).normalized(); + Geom::Point sum_n = n1 + n2; + + double r1 = circle1.radius(); + double r2 = circle2.radius(); + double delta_r = r2 - r1; + Geom::Point c1 = circle1.center(); + Geom::Point c2 = circle2.center(); + Geom::Point delta_c = c2 - c1; + + // std::cout << "adjust_circles:" << std::endl; + // std::cout << " norm: " << n1 << "; " << n2 << std::endl; + // std::cout << " sum_n: " << sum_n << std::endl; + // std::cout << " delta_r: " << delta_r << std::endl; + // std::cout << " delta_c: " << delta_c << std::endl; + + // Quadratic equation + double A = 4 - sum_n.length() * sum_n.length(); + double B = 4.0 * delta_r - 2.0 * Geom::dot( delta_c, sum_n ); + double C = delta_r * delta_r - delta_c.length() * delta_c.length(); + + double s1 = 0; + double s2 = 0; + + if( fabs(A) < 0.01 ) { + // std::cout << " A near zero! $$$$$$$$$$$$$$$$$$" << std::endl; + if( B != 0 ) { + s1 = -C/B; + s2 = -s1; + } + } else { + s1 = (-B + sqrt(B*B - 4*A*C))/(2*A); + s2 = (-B - sqrt(B*B - 4*A*C))/(2*A); + } + + double dr = (fabs(s1)<=fabs(s2)?s1:s2); + + // std::cout << " A: " << A << std::endl; + // std::cout << " B: " << B << std::endl; + // std::cout << " C: " << C << std::endl; + // std::cout << " s1: " << s1 << " s2: " << s2 << " dr: " << dr << std::endl; + + circle1 = Geom::Circle( c1 - dr*n1, r1-dr ); + circle2 = Geom::Circle( c2 + dr*n2, r2+dr ); + + // std::cout << " C1: " << circle1 << std::endl; + // std::cout << " C2: " << circle2 << std::endl; + // std::cout << " d': " << Geom::Point( circle1.center() - circle2.center() ).length() << std::endl; + + Geom::Line bisector( circle1.center(), circle2.center() ); + std::vector<Geom::ShapeIntersection> points = circle1.intersect( bisector ); + Geom::Point p0 = points[0].point(); + Geom::Point p1 = points[1].point(); + // std::cout << " points: " << p0 << "; " << p1 << std::endl; + if( abs( Geom::distance( p0, circle2.center() ) - circle2.radius() ) < + abs( Geom::distance( p1, circle2.center() ) - circle2.radius() ) ) { + return p0; + } else { + return p1; + } +} + +void extrapolate_join_internal(join_data jd, int alternative) { + // std::cout << "\nextrapolate_join: entrance: alternative: " << alternative << std::endl; using namespace Geom; Geom::Path &res = jd.res; @@ -187,10 +364,40 @@ void extrapolate_join(join_data jd) Geom::Point endPt = outgoing.initialPoint(); Geom::Point tang1 = jd.in_tang; Geom::Point tang2 = jd.out_tang; + // width is half stroke-width double width = jd.width, miter = jd.miter; - Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.); - Geom::Circle circle2 = Geom::touching_circle(outgoing.toSBasis(), 0); + // std::cout << " startPt: " << startPt << " endPt: " << endPt << std::endl; + // std::cout << " tang1: " << tang1 << " tang2: " << tang2 << std::endl; + // std::cout << " dot product: " << Geom::dot( tang1, tang2 ) << std::endl; + // std::cout << " width: " << width << std::endl; + + // CIRCLE CALCULATION TESTING + Geom::Circle circle1 = touching_circle(Geom::reverse(incoming.toSBasis()), 0.); + Geom::Circle circle2 = touching_circle(outgoing.toSBasis(), 0); + // std::cout << " circle1: " << circle1 << std::endl; + // std::cout << " circle2: " << circle2 << std::endl; + if( Geom::CubicBezier const * in_bezier = dynamic_cast<Geom::CubicBezier const*>(&incoming) ) { + Geom::Circle circle_test1 = touching_circle(*in_bezier, false); + if( !Geom::are_near( circle1, circle_test1, 0.01 ) ) { + // std::cout << " Circle 1 error!!!!!!!!!!!!!!!!!" << std::endl; + // std::cout << " " << circle_test1 << std::endl; + } + } + if( Geom::CubicBezier const * out_bezier = dynamic_cast<Geom::CubicBezier const*>(&outgoing) ) { + Geom::Circle circle_test2 = touching_circle(*out_bezier, true); + if( !Geom::are_near( circle2, circle_test2, 0.01 ) ) { + // std::cout << " Circle 2 error!!!!!!!!!!!!!!!!!" << std::endl; + // std::cout << " " << circle_test2 << std::endl; + } + } + // END TESTING + + Geom::Point center1 = circle1.center(); + Geom::Point center2 = circle2.center(); + double side1 = tang1[Geom::X]*(startPt[Geom::Y]-center1[Geom::Y]) - tang1[Geom::Y]*(startPt[Geom::X]-center1[Geom::X]); + double side2 = tang2[Geom::X]*( endPt[Geom::Y]-center2[Geom::Y]) - tang2[Geom::Y]*( endPt[Geom::X]-center2[Geom::X]); + // std::cout << " side1: " << side1 << " side2: " << side2 << std::endl; bool inc_ls = !circle1.center().isFinite(); bool out_ls = !circle2.center().isFinite(); @@ -199,20 +406,120 @@ void extrapolate_join(join_data jd) Geom::EllipticalArc *arc1 = NULL; Geom::EllipticalArc *arc2 = NULL; + Geom::LineSegment *seg1 = NULL; + Geom::LineSegment *seg2 = NULL; Geom::Point sol; Geom::Point p1; Geom::Point p2; if (!inc_ls && !out_ls) { + // std::cout << " two circles" << std::endl; + + // See if tangent is backwards (radius < width/2 and circle is inside stroke). + Geom::Point node_on_path = startPt + Geom::rot90(tang1)*width; + // std::cout << " node_on_path: " << node_on_path << " -------------- " << std::endl; + bool b1 = false; + bool b2 = false; + if (circle1.radius() < width && distance( circle1.center(), node_on_path ) < width) { + b1 = true; + } + if (circle2.radius() < width && distance( circle2.center(), node_on_path ) < width) { + b2 = true; + } + // std::cout << " b1: " << (b1?"true":"false") + // << " b2: " << (b2?"true":"false") << std::endl; + // Two circles points = circle1.intersect(circle2); + + if (points.size() != 2) { + // std::cout << " Circles do not intersect, do backup" << std::endl; + switch (alternative) { + case 1: + { + // Fallback to round if one path has radius smaller than half line width. + if(b1 || b2) { + // std::cout << "At one least path has radius smaller than half line width." << std::endl; + return( round_join(jd) ); + } + + Point p; + if( circle2.contains( startPt ) && !circle1.contains( endPt ) ) { + // std::cout << "Expand circle1" << std::endl; + p = expand_circle( circle1, circle2, startPt, tang1 ); + points.push_back( ShapeIntersection( 0, 0, p) ); + points.push_back( ShapeIntersection( 0, 0, p) ); + } else if( circle1.contains( endPt ) && !circle2.contains( startPt ) ) { + // std::cout << "Expand circle2" << std::endl; + p = expand_circle( circle2, circle1, endPt, tang2 ); + points.push_back( ShapeIntersection( 0, 0, p) ); + points.push_back( ShapeIntersection( 0, 0, p) ); + } else { + // std::cout << "Either both points inside or both outside" << std::endl; + return( miter_clip_join(jd) ); + } + break; + + } + case 2: + { + // Fallback to round if one path has radius smaller than half line width. + if(b1 || b2) { + // std::cout << "At one least path has radius smaller than half line width." << std::endl; + return( round_join(jd) ); + } + + if( ( circle2.contains( startPt ) && !circle1.contains( endPt ) ) || + ( circle1.contains( endPt ) && !circle2.contains( startPt ) ) ) { + + Geom::Point apex = adjust_circles( circle1, circle2, startPt, endPt, tang1, tang2 ); + points.push_back( ShapeIntersection( 0, 0, apex) ); + points.push_back( ShapeIntersection( 0, 0, apex) ); + } else { + // std::cout << "Either both points inside or both outside" << std::endl; + return( miter_clip_join(jd) ); + } + + break; + } + case 3: + if( side1 > 0 ) { + Geom::Line secant(startPt, startPt + tang1); + points = circle2.intersect(secant); + circle1.setRadius(std::numeric_limits<float>::infinity()); + circle1.setCenter(Geom::Point(0,std::numeric_limits<float>::infinity())); + } else { + Geom::Line secant(endPt, endPt + tang2); + points = circle1.intersect(secant); + circle2.setRadius(std::numeric_limits<float>::infinity()); + circle2.setCenter(Geom::Point(0,std::numeric_limits<float>::infinity())); + } + break; + + + case 0: + default: + // Do nothing + break; + } + } + if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); - arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol); - arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt); + if( circle1.radius() != std::numeric_limits<float>::infinity() ) { + arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol); + } else { + seg1 = new Geom::LineSegment(startPt, sol); + } + if( circle2.radius() != std::numeric_limits<float>::infinity() ) { + arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt); + } else { + seg2 = new Geom::LineSegment(sol, endPt); + } } } else if (inc_ls && !out_ls) { // Line and circle + // std::cout << " line circle" << std::endl; points = circle2.intersect(Line(incoming.initialPoint(), incoming.finalPoint())); if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); @@ -220,16 +527,18 @@ void extrapolate_join(join_data jd) } } else if (!inc_ls && out_ls) { // Circle and line + // std::cout << " circle line" << std::endl; points = circle1.intersect(Line(outgoing.initialPoint(), outgoing.finalPoint())); if (points.size() == 2) { sol = pick_solution(points, tang2, endPt); arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol); } } - - if (points.size() != 2) + if (points.size() != 2) { + // std::cout << " no solutions" << std::endl; // no solutions available, fall back to miter - return miter_clip_join(jd); + return miter_join(jd); + } // We have a solution, thus sol is defined. p1 = sol; @@ -250,12 +559,13 @@ void extrapolate_join(join_data jd) Geom::Line limit_line; double miter_limit = 2.0 * width * miter; bool clipped = false; - + if (are_parallel(bisector_chord, ortho)) { // No intersection (can happen if curvatures are equal but opposite) if (Geom::distance(point_on_path, sol) > miter_limit) { clipped = true; - Geom::Point limit_point = point_on_path + miter_limit * bisector.versor(); + Geom::Point temp = bisector.versor(); + Geom::Point limit_point = point_on_path + miter_limit * temp; limit_line = make_parallel_line( limit_point, ortho ); } } else { @@ -311,6 +621,8 @@ void extrapolate_join(join_data jd) // Add initial if (arc1) { res.append(*arc1); + } else if (seg1 ) { + res.append(*seg1); } else { // Straight line segment: move last point res.setFinal(p1); @@ -324,6 +636,9 @@ void extrapolate_join(join_data jd) if (arc2) { res.append(*arc2); res.append(outgoing); + } else if (seg2 ) { + res.append(*seg2); + res.append(outgoing); } else { // Straight line segment: res.appendNew<Geom::LineSegment>(outgoing.finalPoint()); @@ -334,8 +649,16 @@ void extrapolate_join(join_data jd) delete arc1; delete arc2; + delete seg1; + delete seg2; } +void extrapolate_join( join_data jd) { extrapolate_join_internal(jd, 0); } +void extrapolate_join_alt1(join_data jd) { extrapolate_join_internal(jd, 1); } +void extrapolate_join_alt2(join_data jd) { extrapolate_join_internal(jd, 2); } +void extrapolate_join_alt3(join_data jd) { extrapolate_join_internal(jd, 3); } + + void join_inside(join_data jd) { Geom::Path &res = jd.res; @@ -716,6 +1039,15 @@ void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, case Inkscape::JOIN_EXTRAPOLATE: jf = &extrapolate_join; break; + case Inkscape::JOIN_EXTRAPOLATE1: + jf = &extrapolate_join_alt1; + break; + case Inkscape::JOIN_EXTRAPOLATE2: + jf = &extrapolate_join_alt2; + break; + case Inkscape::JOIN_EXTRAPOLATE3: + jf = &extrapolate_join_alt3; + break; case Inkscape::JOIN_MITER_CLIP: jf = &miter_clip_join; break; diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index 6697273cf..6a753a6fd 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -21,6 +21,9 @@ enum LineJoinType { JOIN_MITER, JOIN_MITER_CLIP, JOIN_EXTRAPOLATE, + JOIN_EXTRAPOLATE1, + JOIN_EXTRAPOLATE2, + JOIN_EXTRAPOLATE3, }; enum LineCapType { diff --git a/src/inkview.cpp b/src/inkview.cpp index aab4b4a20..3fcd9ed8a 100644 --- a/src/inkview.cpp +++ b/src/inkview.cpp @@ -30,10 +30,6 @@ # include "config.h" #endif -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif - #include <cstring> #include <sys/stat.h> #include <locale.h> @@ -106,66 +102,67 @@ static GtkWidget *ctrlwin = NULL; int sp_main_gui (int, char const**) { return 0; } int sp_main_console (int, char const**) { return 0; } -static int -sp_svgview_main_delete (GtkWidget */*widget*/, GdkEvent */*event*/, struct SPSlideShow */*ss*/) +static int sp_svgview_main_delete (GtkWidget */*widget*/, + GdkEvent */*event*/, + struct SPSlideShow */*ss*/) { gtk_main_quit (); return FALSE; } -static int -sp_svgview_main_key_press (GtkWidget */*widget*/, GdkEventKey *event, struct SPSlideShow *ss) +static int sp_svgview_main_key_press (GtkWidget */*widget*/, + GdkEventKey *event, + struct SPSlideShow *ss) { switch (event->keyval) { - case GDK_KEY_Up: - case GDK_KEY_Home: - sp_svgview_goto_first(ss); - break; - case GDK_KEY_Down: - case GDK_KEY_End: - sp_svgview_goto_last(ss); - break; - case GDK_KEY_F11: - if (ss->fullscreen) { - gtk_window_unfullscreen (GTK_WINDOW(ss->window)); - ss->fullscreen = false; - } else { - gtk_window_fullscreen (GTK_WINDOW(ss->window)); - ss->fullscreen = true; - } - break; - case GDK_KEY_Return: - sp_svgview_control_show (ss); - break; - case GDK_KEY_KP_Page_Down: - case GDK_KEY_Page_Down: - case GDK_KEY_Right: - case GDK_KEY_space: - sp_svgview_show_next (ss); - break; - case GDK_KEY_KP_Page_Up: - case GDK_KEY_Page_Up: - case GDK_KEY_Left: - case GDK_KEY_BackSpace: - sp_svgview_show_prev (ss); - break; - case GDK_KEY_Escape: - case GDK_KEY_q: - case GDK_KEY_Q: - gtk_main_quit(); - break; - default: - break; + case GDK_KEY_Up: + case GDK_KEY_Home: + sp_svgview_goto_first(ss); + break; + case GDK_KEY_Down: + case GDK_KEY_End: + sp_svgview_goto_last(ss); + break; + case GDK_KEY_F11: + if (ss->fullscreen) { + gtk_window_unfullscreen (GTK_WINDOW(ss->window)); + ss->fullscreen = false; + } else { + gtk_window_fullscreen (GTK_WINDOW(ss->window)); + ss->fullscreen = true; + } + break; + case GDK_KEY_Return: + sp_svgview_control_show (ss); + break; + case GDK_KEY_KP_Page_Down: + case GDK_KEY_Page_Down: + case GDK_KEY_Right: + case GDK_KEY_space: + sp_svgview_show_next (ss); + break; + case GDK_KEY_KP_Page_Up: + case GDK_KEY_Page_Up: + case GDK_KEY_Left: + case GDK_KEY_BackSpace: + sp_svgview_show_prev (ss); + break; + case GDK_KEY_Escape: + case GDK_KEY_q: + case GDK_KEY_Q: + gtk_main_quit(); + break; + default: + break; } gtk_window_set_title(GTK_WINDOW(ss->window), ss->doc->getName()); return TRUE; } -int -main (int argc, const char **argv) +int main (int argc, const char **argv) { if (argc == 1) { - usage(); + usage(); } // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example) @@ -175,21 +172,25 @@ main (int argc, const char **argv) struct SPSlideShow ss; - int option, - num_parsed_options = 0; + int num_parsed_options = 0; // the list of arguments is in the net line - while ((option = getopt(argc, (char* const* )argv, "t:")) != -1) - { - switch(option) { - case 't': // for timer - // fprintf(stderr, "set timer arg %s\n", optarg ); - ss.timer = atoi(optarg); - num_parsed_options += 2; // 2 because of flag + option + for (int i = 1; i < argc; i++) { + if ((argv[i][0] == '-')) { + if (!strcmp(argv[i], "--")) { break; - case '?': - default: - usage(); + } + else if ((!strcmp(argv[i], "-t"))) { + if (i + 1 >= argc) { + usage(); + } + ss.timer = atoi(argv[i+1]); + num_parsed_options = i+1; + i++; + } + else { + usage(); + } } } @@ -232,82 +233,84 @@ main (int argc, const char **argv) // starting at where the commandline options stopped parsing because // we want all the files to be in the list for (i = num_parsed_options + 1 ; i < argc; i++) { - struct stat st; - if (stat (argv[i], &st) - || !S_ISREG (st.st_mode) - || (st.st_size < 64)) { - fprintf(stderr, "could not open file %s\n", argv[i]); - } else { - -#ifdef WITH_INKJAR - if (is_jar(argv[i])) { - Inkjar::JarFileReader jar_file_reader(argv[i]); - for (;;) { - GByteArray *gba = jar_file_reader.get_next_file(); - if (gba == NULL) { - char *c_ptr; - gchar *last_filename = jar_file_reader.get_last_filename(); - if (last_filename == NULL) - break; - if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) { - if (*(++c_ptr) == '\0') { - g_free(last_filename); - continue; - } - } - } else if (gba->len > 0) { - //::write(1, gba->data, gba->len); - /* Append to list */ - if (ss.length >= ss.size) { - /* Expand */ - ss.size <<= 1; - ss.slides = g_renew (char *, ss.slides, ss.size); - } - - ss.doc = SPDocument::createNewDocFromMem ((const gchar *)gba->data, - gba->len, - TRUE); - gchar *last_filename = jar_file_reader.get_last_filename(); - if (ss.doc) { - ss.slides[ss.length++] = strdup (last_filename); - (ss.doc)->setUri (last_filename); - } - g_byte_array_free(gba, TRUE); - g_free(last_filename); - } else - break; - } - } else { -#endif /* WITH_INKJAR */ - /* Append to list */ - if (ss.length >= ss.size) { - /* Expand */ - ss.size <<= 1; - ss.slides = g_renew (char *, ss.slides, ss.size); - - } - - ss.slides[ss.length++] = strdup (argv[i]); - + struct stat st; + if (stat (argv[i], &st) + || !S_ISREG (st.st_mode) + || (st.st_size < 64)) { + fprintf(stderr, "could not open file %s\n", argv[i]); + } else { + + #ifdef WITH_INKJAR + if (is_jar(argv[i])) { + Inkjar::JarFileReader jar_file_reader(argv[i]); + for (;;) { + GByteArray *gba = jar_file_reader.get_next_file(); + if (gba == NULL) { + char *c_ptr; + gchar *last_filename = jar_file_reader.get_last_filename(); + if (last_filename == NULL) + break; + if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) { + if (*(++c_ptr) == '\0') { + g_free(last_filename); + continue; + } + } + } else if (gba->len > 0) { + //::write(1, gba->data, gba->len); + /* Append to list */ + if (ss.length >= ss.size) { + /* Expand */ + ss.size <<= 1; + ss.slides = g_renew (char *, ss.slides, ss.size); + } + + ss.doc = SPDocument::createNewDocFromMem ((const gchar *)gba->data, + gba->len, + TRUE); + gchar *last_filename = jar_file_reader.get_last_filename(); + if (ss.doc) { + ss.slides[ss.length++] = strdup (last_filename); + (ss.doc)->setUri (last_filename); + } + g_byte_array_free(gba, TRUE); + g_free(last_filename); + } else { + break; + } + } + } else { + #endif /* WITH_INKJAR */ + /* Append to list */ + if (ss.length >= ss.size) { + /* Expand */ + ss.size <<= 1; + ss.slides = g_renew (char *, ss.slides, ss.size); + } + + ss.slides[ss.length++] = strdup (argv[i]); + + if (!ss.doc) { + ss.doc = SPDocument::createNewDoc (ss.slides[ss.current], TRUE, false); if (!ss.doc) { - ss.doc = SPDocument::createNewDoc (ss.slides[ss.current], TRUE, false); - if (!ss.doc) - ++ss.current; - } -#ifdef WITH_INKJAR - } -#endif - } + ++ss.current; + } + } + #ifdef WITH_INKJAR + } + #endif + } } - if(!ss.doc) + if(!ss.doc) { return 1; /* none of the slides loadable */ - + } + w = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title( GTK_WINDOW(w), ss.doc->getName() ); gtk_window_set_default_size (GTK_WINDOW (w), - MIN ((int)(ss.doc)->getWidth().value("px"), (int)gdk_screen_width() - 64), - MIN ((int)(ss.doc)->getHeight().value("px"), (int)gdk_screen_height() - 64)); + MIN ((int)(ss.doc)->getWidth().value("px"), (int)gdk_screen_width() - 64), + MIN ((int)(ss.doc)->getHeight().value("px"), (int)gdk_screen_height() - 64)); ss.window = w; g_signal_connect (G_OBJECT (w), "delete_event", (GCallback) sp_svgview_main_delete, &ss); @@ -327,8 +330,9 @@ main (int argc, const char **argv) return 0; } -static int -sp_svgview_ctrlwin_delete (GtkWidget */*widget*/, GdkEvent */*event*/, void */*data*/) +static int sp_svgview_ctrlwin_delete (GtkWidget */*widget*/, + GdkEvent */*event*/, + void */*data*/) { ctrlwin = NULL; return FALSE; @@ -337,97 +341,92 @@ sp_svgview_ctrlwin_delete (GtkWidget */*widget*/, GdkEvent */*event*/, void */*d static GtkWidget* sp_svgview_control_show(struct SPSlideShow *ss) { if (!ctrlwin) { - ctrlwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + ctrlwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_resizable(GTK_WINDOW(ctrlwin), FALSE); gtk_window_set_transient_for(GTK_WINDOW(ctrlwin), GTK_WINDOW(ss->window)); g_signal_connect(G_OBJECT (ctrlwin), "key_press_event", (GCallback) sp_svgview_main_key_press, ss); g_signal_connect(G_OBJECT (ctrlwin), "delete_event", (GCallback) sp_svgview_ctrlwin_delete, NULL); #if GTK_CHECK_VERSION(3,0,0) - GtkWidget *t = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + GtkWidget *t = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); #else - GtkWidget *t = gtk_hbutton_box_new(); + GtkWidget *t = gtk_hbutton_box_new(); #endif - gtk_container_add(GTK_CONTAINER(ctrlwin), t); + gtk_container_add(GTK_CONTAINER(ctrlwin), t); #if GTK_CHECK_VERSION(3,10,0) GtkWidget *b = gtk_button_new_from_icon_name(INKSCAPE_ICON("go-first"), GTK_ICON_SIZE_BUTTON); #else - GtkWidget *b = gtk_button_new(); + GtkWidget *b = gtk_button_new(); GtkWidget *img = gtk_image_new_from_icon_name(INKSCAPE_ICON("go-first"), GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(b), img); #endif - gtk_container_add(GTK_CONTAINER(t), b); + gtk_container_add(GTK_CONTAINER(t), b); - g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_goto_first_cb, ss); + g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_goto_first_cb, ss); #if GTK_CHECK_VERSION(3,10,0) b = gtk_button_new_from_icon_name(INKSCAPE_ICON("go-previous"), GTK_ICON_SIZE_BUTTON); #else - b = gtk_button_new(); + b = gtk_button_new(); img = gtk_image_new_from_icon_name(INKSCAPE_ICON("go-previous"), GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(b), img); #endif - gtk_container_add(GTK_CONTAINER(t), b); + gtk_container_add(GTK_CONTAINER(t), b); - g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_show_prev_cb, ss); + g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_show_prev_cb, ss); #if GTK_CHECK_VERSION(3,10,0) b = gtk_button_new_from_icon_name(INKSCAPE_ICON("go-next"), GTK_ICON_SIZE_BUTTON); #else - b = gtk_button_new(); + b = gtk_button_new(); img = gtk_image_new_from_icon_name(INKSCAPE_ICON("go-next"), GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(b), img); #endif - gtk_container_add(GTK_CONTAINER(t), b); + gtk_container_add(GTK_CONTAINER(t), b); - g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_show_next_cb, ss); + g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_show_next_cb, ss); #if GTK_CHECK_VERSION(3,10,0) b = gtk_button_new_from_icon_name(INKSCAPE_ICON("go-last"), GTK_ICON_SIZE_BUTTON); #else - b = gtk_button_new(); + b = gtk_button_new(); img = gtk_image_new_from_icon_name(INKSCAPE_ICON("go-last"), GTK_ICON_SIZE_BUTTON); gtk_button_set_image(GTK_BUTTON(b), img); #endif - gtk_container_add(GTK_CONTAINER(t), b); - g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_goto_last_cb, ss); - gtk_widget_show_all(ctrlwin); + gtk_container_add(GTK_CONTAINER(t), b); + g_signal_connect(G_OBJECT(b), "clicked", (GCallback) sp_svgview_goto_last_cb, ss); + gtk_widget_show_all(ctrlwin); } else { - gtk_window_present(GTK_WINDOW(ctrlwin)); + gtk_window_present(GTK_WINDOW(ctrlwin)); } return NULL; } -static int -sp_svgview_show_next_cb (GtkWidget */*widget*/, void *data) +static int sp_svgview_show_next_cb (GtkWidget */*widget*/, void *data) { sp_svgview_show_next(static_cast<struct SPSlideShow *>(data)); return FALSE; } -static int -sp_svgview_show_prev_cb (GtkWidget */*widget*/, void *data) +static int sp_svgview_show_prev_cb (GtkWidget */*widget*/, void *data) { sp_svgview_show_prev(static_cast<struct SPSlideShow *>(data)); return FALSE; } -static int -sp_svgview_goto_first_cb (GtkWidget */*widget*/, void *data) +static int sp_svgview_goto_first_cb (GtkWidget */*widget*/, void *data) { sp_svgview_goto_first(static_cast<struct SPSlideShow *>(data)); return FALSE; } -static int -sp_svgview_goto_last_cb (GtkWidget */*widget*/, void *data) +static int sp_svgview_goto_last_cb (GtkWidget */*widget*/, void *data) { sp_svgview_goto_last(static_cast<struct SPSlideShow *>(data)); return FALSE; } -static void -sp_svgview_waiting_cursor(struct SPSlideShow *ss) +static void sp_svgview_waiting_cursor(struct SPSlideShow *ss) { GdkCursor *waiting = gdk_cursor_new(GDK_WATCH); gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(ss->window)), waiting); @@ -445,12 +444,12 @@ sp_svgview_waiting_cursor(struct SPSlideShow *ss) gdk_cursor_unref(waiting); #endif } - while(gtk_events_pending()) + while(gtk_events_pending()) { gtk_main_iteration(); + } } -static void -sp_svgview_normal_cursor(struct SPSlideShow *ss) +static void sp_svgview_normal_cursor(struct SPSlideShow *ss) { gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(ss->window)), NULL); if (ctrlwin) { @@ -458,8 +457,9 @@ sp_svgview_normal_cursor(struct SPSlideShow *ss) } } -static void -sp_svgview_set_document(struct SPSlideShow *ss, SPDocument *doc, int current) +static void sp_svgview_set_document(struct SPSlideShow *ss, + SPDocument *doc, + int current) { if (doc && doc != ss->doc) { doc->ensureUpToDate(); @@ -469,8 +469,7 @@ sp_svgview_set_document(struct SPSlideShow *ss, SPDocument *doc, int current) } } -static void -sp_svgview_show_next (struct SPSlideShow *ss) +static void sp_svgview_show_next (struct SPSlideShow *ss) { sp_svgview_waiting_cursor(ss); @@ -485,8 +484,7 @@ sp_svgview_show_next (struct SPSlideShow *ss) sp_svgview_normal_cursor(ss); } -static void -sp_svgview_show_prev (struct SPSlideShow *ss) +static void sp_svgview_show_prev (struct SPSlideShow *ss) { sp_svgview_waiting_cursor(ss); @@ -501,16 +499,16 @@ sp_svgview_show_prev (struct SPSlideShow *ss) sp_svgview_normal_cursor(ss); } -static void -sp_svgview_goto_first (struct SPSlideShow *ss) +static void sp_svgview_goto_first (struct SPSlideShow *ss) { sp_svgview_waiting_cursor(ss); SPDocument *doc = NULL; int current = 0; while ( !doc && (current < ss->length - 1)) { - if (current == ss->current) + if (current == ss->current) { break; + } doc = SPDocument::createNewDoc (ss->slides[current++], TRUE, false); } @@ -519,16 +517,16 @@ sp_svgview_goto_first (struct SPSlideShow *ss) sp_svgview_normal_cursor(ss); } -static void -sp_svgview_goto_last (struct SPSlideShow *ss) +static void sp_svgview_goto_last (struct SPSlideShow *ss) { sp_svgview_waiting_cursor(ss); SPDocument *doc = NULL; int current = ss->length - 1; while (!doc && (current >= 0)) { - if (current == ss->current) + if (current == ss->current) { break; + } doc = SPDocument::createNewDoc (ss->slides[current--], TRUE, false); } @@ -538,8 +536,7 @@ sp_svgview_goto_last (struct SPSlideShow *ss) } #ifdef WITH_INKJAR -static bool -is_jar(char const *filename) +static bool is_jar(char const *filename) { /* fixme: Check MIME type or something. /usr/share/misc/file/magic suggests that checking for initial string "PK\003\004" in content should suffice. */ @@ -556,12 +553,12 @@ is_jar(char const *filename) static void usage() { fprintf(stderr, - "Usage: inkview [OPTIONS...] [FILES ...]\n" - "\twhere FILES are SVG (.svg or .svgz)" + "Usage: inkview [OPTIONS...] [FILES ...]\n" + "\twhere FILES are SVG (.svg or .svgz)" #ifdef WITH_INKJAR - " or archives of SVGs (.sxw, .jar)" + " or archives of SVGs (.sxw, .jar)" #endif - "\n"); + "\n"); exit(1); } diff --git a/src/io/inkjar.cpp b/src/io/inkjar.cpp index fb1fedf55..345455c4a 100644 --- a/src/io/inkjar.cpp +++ b/src/io/inkjar.cpp @@ -103,14 +103,19 @@ bool JarFile::init_inflation() bool JarFile::open() { + if (_file != NULL) { + fclose(_file); + } if ((_file = fopen(_filename, "r")) == NULL) { - fprintf(stderr, "open failed.\n"); - return false; + fprintf(stderr, "open failed.\n"); + return false; + } + if (!init_inflation()) { + return false; + } + else { + return true; } - if (!init_inflation()) - return false; - - return true; } bool JarFile::close() diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 28f99eb94..2eef315dd 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -33,6 +33,9 @@ static const Util::EnumData<unsigned> JoinTypeData[] = { {JOIN_MITER, N_("Miter"), "miter"}, {JOIN_MITER_CLIP, N_("Miter Clip"), "miter-clip"}, {JOIN_EXTRAPOLATE, N_("Extrapolated arc"), "extrp_arc"}, + {JOIN_EXTRAPOLATE1, N_("Extrapolated arc Alt1"), "extrp_arc1"}, + {JOIN_EXTRAPOLATE2, N_("Extrapolated arc Alt2"), "extrp_arc2"}, + {JOIN_EXTRAPOLATE3, N_("Extrapolated arc Alt3"), "extrp_arc3"}, }; static const Util::EnumData<unsigned> CapTypeData[] = { diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 5b7874c61..3e559ee7a 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -641,7 +641,6 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(IntermSnapResults &isr, bool strict_snapping = _snapmanager->snapprefs.getStrictSnapping(); // Find all intersections of the constrained path with the snap target candidates - std::vector<Geom::Point> intersections; for (std::vector<SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); ++k) { if (k->path_vector && _allowSourceToSnapToTarget(p.getSourceType(), (*k).target_type, strict_snapping)) { // Do the intersection math diff --git a/src/sp-marker.cpp b/src/sp-marker.cpp index 88dfbe04e..3505e2fe8 100644 --- a/src/sp-marker.cpp +++ b/src/sp-marker.cpp @@ -394,7 +394,8 @@ sp_marker_show_instance ( SPMarker *marker, Inkscape::DrawingItem *parent, if (marker->orient_mode == MARKER_ORIENT_AUTO) { m = base; } else if (marker->orient_mode == MARKER_ORIENT_AUTO_START_REVERSE) { - m = Geom::Rotate::from_degrees( 180.0 ) * base; + // m = Geom::Rotate::from_degrees( 180.0 ) * base; + // Rotating is done at rendering time if necessary m = base; } else { /* fixme: Orient units (Lauris) */ diff --git a/src/sp-mask.cpp b/src/sp-mask.cpp index 7b9ab11c3..3537c7bac 100644 --- a/src/sp-mask.cpp +++ b/src/sp-mask.cpp @@ -139,12 +139,12 @@ void SPMask::update(SPCtx* ctx, unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector<SPObject *> children = this->childList(false); - for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();child++) { + for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();++child) { sp_object_ref(*child); } - for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();child++) { + for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();++child) { if (flags || ((*child)->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { (*child)->updateDisplay(ctx, flags); } @@ -173,11 +173,11 @@ void SPMask::modified(unsigned int flags) { flags &= SP_OBJECT_MODIFIED_CASCADE; std::vector<SPObject *> children = this->childList(false); - for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();child++) { + for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();++child) { sp_object_ref(*child); } - for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();child++) { + for (std::vector<SPObject *>::const_iterator child = children.begin();child != children.end();++child) { if (flags || ((*child)->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { (*child)->emitModified(flags); } diff --git a/src/sp-text.cpp b/src/sp-text.cpp index c7dfdd694..afbe0b147 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -367,8 +367,11 @@ gchar* SPText::description() const { char *n = xml_quote_strdup( style->font_family.value ); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int unit = prefs->getInt("/options/font/unitType", SP_CSS_UNIT_PT); Inkscape::Util::Quantity q = Inkscape::Util::Quantity(style->font_size.computed, "px"); - GString *xs = g_string_new(q.string(SP_ACTIVE_DESKTOP->getNamedView()->display_units).c_str()); + q.quantity *= this->i2doc_affine().descrim(); + GString *xs = g_string_new(q.string(sp_style_get_css_unit_string(unit)).c_str()); char const *trunc = ""; Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) this); diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 7cd48e62e..c2c5c5005 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -448,13 +448,13 @@ void DocumentProperties::populate_available_profiles(){ */ static void sanitizeName( Glib::ustring& str ) { - if (str.size() > 1) { + if (str.size() > 0) { char val = str.at(0); if (((val < 'A') || (val > 'Z')) && ((val < 'a') || (val > 'z')) && (val != '_') && (val != ':')) { - str.replace(0, 1, "-"); + str.insert(0, "_"); } for (Glib::ustring::size_type i = 1; i < str.size(); i++) { char val = str.at(i); diff --git a/src/ui/dialog/text-edit.cpp b/src/ui/dialog/text-edit.cpp index 05cf3a388..c01da8864 100644 --- a/src/ui/dialog/text-edit.cpp +++ b/src/ui/dialog/text-edit.cpp @@ -127,8 +127,8 @@ TextEdit::TextEdit() gtk_combo_box_text_append_text((GtkComboBoxText *) spacing_combo, spacings[i]); } - gtk_widget_set_tooltip_text (px, _("Spacing between lines (percent of font size)")); - gtk_widget_set_tooltip_text (spacing_combo, _("Spacing between lines (percent of font size)")); + gtk_widget_set_tooltip_text (px, _("Spacing between baselines (percent of font size)")); + gtk_widget_set_tooltip_text (spacing_combo, _("Spacing between baselines (percent of font size)")); layout_hbox.pack_start(*Gtk::manage(Glib::wrap(spacing_combo)), false, false); layout_frame.set_padding(4,4,4,4); layout_frame.add(layout_hbox); diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index 7db4e09d8..47927667c 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -458,7 +458,7 @@ bool MeshTool::root_handler(GdkEvent* event) { SPCtrlCurve *line = NULL; if (! drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); l++) { + for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) { line = (SPCtrlCurve*) (*l); over_line |= sp_mesh_context_is_over_line (this, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y)); } @@ -594,7 +594,7 @@ bool MeshTool::root_handler(GdkEvent* event) { bool over_line = false; if (!drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() ; l++) { + for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() ; ++l) { over_line |= sp_mesh_context_is_over_line (this, (SPItem*)(*l), Geom::Point(event->motion.x, event->motion.y)); } } @@ -625,7 +625,7 @@ bool MeshTool::root_handler(GdkEvent* event) { SPCtrlLine *line = NULL; if (!drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); l++) { + for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) { line = (SPCtrlLine*)(*l); over_line = sp_mesh_context_is_over_line (this, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y)); diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 2b85216d2..905e38f2b 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -405,13 +405,15 @@ void SelectTool::sp_select_context_cycle_through_items(Inkscape::Selection *sele // Find next item and activate it std::vector<SPItem *>::iterator next = this->cycling_cur_item; if (scroll_event->direction == GDK_SCROLL_UP) { - next++; - if (next == this->cycling_items.end() && this->cycling_wrap) + ++next; + if (next == this->cycling_items.end() && this->cycling_wrap) { next = this->cycling_items.begin(); + } } else { - if(next == this->cycling_items.begin()) + if(next == this->cycling_items.begin()) { next = this->cycling_items.end(); - next--; + } + --next; } if (next!=this->cycling_items.end()) { diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index 9ce60fce7..32ccbad93 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -269,15 +269,14 @@ VanishingPoint::selectedBoxes(Inkscape::Selection *sel) { return sel_boxes; } -VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp) +VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp) : + parent(parent), + knot(NULL), + point(p), + point_original(p), + dragging_started(false), + vps() { - this->parent = parent; - - this->point = p; - this->point_original = p; - - this->dragging_started = false; - if (vp.is_finite()) { // create the knot this->knot = new SPKnot(SP_ACTIVE_DESKTOP, NULL); diff --git a/src/vanishing-point.h b/src/vanishing-point.h index 28da8e7fa..ae2a88d6e 100644 --- a/src/vanishing-point.h +++ b/src/vanishing-point.h @@ -46,6 +46,7 @@ public: inline VanishingPoint &operator=(VanishingPoint const &rhs) { _persp = rhs._persp; _axis = rhs._axis; + my_counter = rhs.my_counter; return *this; } inline bool operator==(VanishingPoint const &rhs) const { diff --git a/src/widgets/ruler.cpp b/src/widgets/ruler.cpp index ab486eeeb..3a5e76277 100644 --- a/src/widgets/ruler.cpp +++ b/src/widgets/ruler.cpp @@ -41,8 +41,9 @@ #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB -#define DEFAULT_RULER_FONT_SCALE PANGO_SCALE_X_SMALL -#define MINIMUM_INCR 5 +#define DEFAULT_RULER_FONT_SCALE PANGO_SCALE_X_SMALL +#define MINIMUM_INCR 5 +#define IMMEDIATE_REDRAW_THRESHOLD 20 using Inkscape::Util::unit_table; @@ -71,11 +72,11 @@ typedef struct GdkWindow *input_window; cairo_surface_t *backing_store; + gboolean backing_store_valid; + GdkRectangle last_pos_rect; + guint pos_redraw_idle_id; PangoLayout *layout; gdouble font_scale; - - gint xsrc; - gint ysrc; GList *track_widgets; } SPRulerPrivate; @@ -132,18 +133,22 @@ static void sp_ruler_style_updated (GtkWidget *widget); static void sp_ruler_size_request (GtkWidget *widget, GtkRequisition *requisition); static void sp_ruler_style_set (GtkWidget *widget, - GtkStyle *prev_style); + GtkStyle *prev_style); #endif static gboolean sp_ruler_motion_notify (GtkWidget *widget, GdkEventMotion *event); static gboolean sp_ruler_draw (GtkWidget *widget, - cairo_t *cr); + cairo_t *cr); #if !GTK_CHECK_VERSION(3,0,0) static gboolean sp_ruler_expose (GtkWidget *widget, GdkEventExpose *event); #endif static void sp_ruler_draw_ticks (SPRuler *ruler); +static GdkRectangle sp_ruler_get_pos_rect (SPRuler *ruler, + gdouble position); +static gboolean sp_ruler_idle_queue_pos_redraw(gpointer data); +static void sp_ruler_queue_pos_redraw (SPRuler *ruler); static void sp_ruler_draw_pos (SPRuler *ruler, cairo_t *cr); static void sp_ruler_make_pixmap (SPRuler *ruler); @@ -260,14 +265,23 @@ sp_ruler_init (SPRuler *ruler) gtk_widget_set_has_window (GTK_WIDGET (ruler), FALSE); - priv->orientation = GTK_ORIENTATION_HORIZONTAL; - priv->unit = unit_table.getUnit("px"); - priv->lower = 0; - priv->upper = 0; - priv->position = 0; - priv->max_size = 0; - priv->backing_store = NULL; - priv->font_scale = DEFAULT_RULER_FONT_SCALE; + priv->orientation = GTK_ORIENTATION_HORIZONTAL; + priv->unit = unit_table.getUnit("px"); + priv->lower = 0; + priv->upper = 0; + priv->position = 0; + priv->max_size = 0; + + priv->backing_store = NULL; + priv->backing_store_valid = FALSE; + + priv->last_pos_rect.x = 0; + priv->last_pos_rect.y = 0; + priv->last_pos_rect.width = 0; + priv->last_pos_rect.height = 0; + priv->pos_redraw_idle_id = 0; + + priv->font_scale = DEFAULT_RULER_FONT_SCALE; #if GTK_CHECK_VERSION(3,0,0) #if GTK_CHECK_VERSION(3,8,0) @@ -299,6 +313,12 @@ sp_ruler_dispose (GObject *object) while (priv->track_widgets) sp_ruler_remove_track_widget (ruler, GTK_WIDGET(priv->track_widgets->data)); + if (priv->pos_redraw_idle_id) + { + g_source_remove (priv->pos_redraw_idle_id); + priv->pos_redraw_idle_id = 0; + } + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -343,6 +363,7 @@ sp_ruler_set_range (SPRuler *ruler, } g_object_thaw_notify (G_OBJECT (ruler)); + priv->backing_store_valid = FALSE; gtk_widget_queue_draw (GTK_WIDGET (ruler)); } @@ -513,7 +534,9 @@ sp_ruler_unrealize(GtkWidget *widget) cairo_surface_destroy (priv->backing_store); priv->backing_store = NULL; } - + + priv->backing_store_valid = FALSE; + if (priv->layout) { g_object_unref (priv->layout); @@ -689,7 +712,6 @@ sp_ruler_expose (GtkWidget *widget, cairo_clip (cr); gtk_widget_get_allocation (widget, &allocation); - cairo_translate (cr, allocation.x, allocation.y); gboolean result = sp_ruler_draw (widget, cr); @@ -733,6 +755,8 @@ sp_ruler_make_pixmap (SPRuler *ruler) CAIRO_CONTENT_COLOR, allocation.width, allocation.height); + + priv->backing_store_valid = FALSE; } static void @@ -743,118 +767,22 @@ sp_ruler_draw_pos (SPRuler *ruler, #if GTK_CHECK_VERSION(3,0,0) GtkStyleContext *context = gtk_widget_get_style_context (widget); - GtkBorder border; GdkRGBA color; #else GtkStyle *style = gtk_widget_get_style (widget); GtkStateType state = gtk_widget_get_state (widget); - gint xthickness; - gint ythickness; #endif SPRulerPrivate *priv = SP_RULER_GET_PRIVATE (ruler); - GtkAllocation allocation; - gint x, y; - gint width, height; - gint bs_width, bs_height; + GdkRectangle pos_rect; if (! gtk_widget_is_drawable (widget)) return; - gtk_widget_get_allocation(widget, &allocation); - -#if GTK_CHECK_VERSION(3,0,0) - gtk_style_context_get_border (context, static_cast<GtkStateFlags>(0), &border); -#else - xthickness = style->xthickness; - ythickness = style->ythickness; -#endif - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - width = allocation.width; -#if GTK_CHECK_VERSION(3,0,0) - height = allocation.height - (border.top + border.bottom); -#else - height = allocation.height - ythickness * 2; -#endif - - bs_width = height / 2 + 2; - bs_width |= 1; /* make sure it's odd */ - bs_height = bs_width / 2 + 1; - } - else - { -#if GTK_CHECK_VERSION(3,0,0) - width = allocation.width - (border.left + border.right); -#else - width = allocation.width - xthickness * 2; -#endif - height = allocation.height; - - bs_height = width / 2 + 2; - bs_height |= 1; /* make sure it's odd */ - bs_width = bs_height / 2 + 1; - } + pos_rect = sp_ruler_get_pos_rect (ruler, sp_ruler_get_position (ruler)); - if ((bs_width > 0) && (bs_height > 0)) + if ((pos_rect.width > 0) && (pos_rect.height > 0)) { - gdouble lower; - gdouble upper; - gdouble position; - gdouble increment; - - if (! cr) - { - cr = gdk_cairo_create (gtk_widget_get_window (widget)); - cairo_translate (cr, allocation.x, allocation.y); - cairo_rectangle (cr, allocation.x, allocation.y, allocation.width, allocation.height); - cairo_clip (cr); - - cairo_translate (cr, allocation.x, allocation.y); - - /* If a backing store exists, restore the ruler */ - if (priv->backing_store) - { - cairo_set_source_surface (cr, priv->backing_store, 0, 0); - cairo_rectangle (cr, priv->xsrc, priv->ysrc, bs_width, bs_height); - cairo_fill (cr); - } - } - else - { - cairo_reference (cr); - } - - position = sp_ruler_get_position (ruler); - - sp_ruler_get_range (ruler, &lower, &upper, NULL); - - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - { - increment = (gdouble) width / (upper - lower); - -#if GTK_CHECK_VERSION(3,0,0) - x = ROUND ((position - lower) * increment) + (border.left - bs_width) / 2 - 1; - y = (height + bs_height) / 2 + border.top; -#else - x = ROUND ((position - lower) * increment) + (xthickness - bs_width) / 2 - 1; - y = (height + bs_height) / 2 + ythickness; -#endif - } - else - { - increment = (gdouble) height / (upper - lower); - -#if GTK_CHECK_VERSION(3,0,0) - x = (width + bs_width) / 2 + border.left; - y = ROUND ((position - lower) * increment) + (border.top - bs_height) / 2 - 1; -#else - x = (width + bs_width) / 2 + xthickness; - y = ROUND ((position - lower) * increment) + (ythickness - bs_height) / 2 - 1; -#endif - } - #if GTK_CHECK_VERSION(3,0,0) gtk_style_context_get_color (context, gtk_widget_get_state_flags (widget), &color); @@ -863,26 +791,28 @@ sp_ruler_draw_pos (SPRuler *ruler, gdk_cairo_set_source_color (cr, &style->fg[state]); #endif - cairo_move_to (cr, x, y); + cairo_move_to (cr, pos_rect.x, pos_rect.y); if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { - cairo_line_to (cr, x + bs_width / 2.0, y + bs_height); - cairo_line_to (cr, x + bs_width, y); + cairo_line_to (cr, pos_rect.x + pos_rect.width / 2.0, + pos_rect.y + pos_rect.height); + cairo_line_to (cr, pos_rect.x + pos_rect.width, + pos_rect.y); } else { - cairo_line_to (cr, x + bs_width, y + bs_height / 2.0); - cairo_line_to (cr, x, y + bs_height); + cairo_line_to (cr, pos_rect.x + pos_rect.width, + + pos_rect.y + pos_rect.height / 2.0); + cairo_line_to (cr, pos_rect.x, + pos_rect.y + pos_rect.height); } cairo_fill (cr); - - cairo_destroy (cr); - - priv->xsrc = x; - priv->ysrc = y; } + + priv->last_pos_rect = pos_rect; } /** @@ -1096,6 +1026,7 @@ sp_ruler_set_unit (SPRuler *ruler, priv->unit = unit; g_object_notify(G_OBJECT(ruler), "unit"); + priv->backing_store_valid = FALSE; gtk_widget_queue_draw (GTK_WIDGET (ruler)); } } @@ -1131,10 +1062,39 @@ sp_ruler_set_position (SPRuler *ruler, if (priv->position != position) { - priv->position = position; - g_object_notify (G_OBJECT (ruler), "position"); - - sp_ruler_draw_pos (ruler, NULL); + GdkRectangle rect; + gint xdiff, ydiff; + + priv->position = position; + g_object_notify (G_OBJECT (ruler), "position"); + + rect = sp_ruler_get_pos_rect (ruler, priv->position); + + xdiff = rect.x - priv->last_pos_rect.x; + ydiff = rect.y - priv->last_pos_rect.y; + + /* + * If the position has changed far enough, queue a redraw immediately. + * Otherwise, we only queue a redraw in a low priority idle handler, to + * allow for other things (like updating the canvas) to run. + * + * TODO: This might not be necessary any more in GTK3 with the frame + * clock. Investigate this more after the port to GTK3. + */ + if (priv->last_pos_rect.width != 0 && + priv->last_pos_rect.height != 0 && + (ABS (xdiff) > IMMEDIATE_REDRAW_THRESHOLD || + ABS (ydiff) > IMMEDIATE_REDRAW_THRESHOLD)) + { + sp_ruler_queue_pos_redraw (ruler); + } + else if (! priv->pos_redraw_idle_id) + { + priv->pos_redraw_idle_id = + g_idle_add_full (G_PRIORITY_LOW, + sp_ruler_idle_queue_pos_redraw, + ruler, NULL); + } } } @@ -1154,7 +1114,7 @@ sp_ruler_get_position (SPRuler *ruler) static gboolean sp_ruler_motion_notify (GtkWidget *widget, - GdkEventMotion *event) + GdkEventMotion *event) { SPRuler *ruler = SP_RULER(widget); @@ -1347,7 +1307,7 @@ sp_ruler_draw_ticks (SPRuler *ruler) if (ideal_length > ++length) length = ideal_length; - if (lower < upper) + if (lower < upper) { start = floor (lower / subd_incr) * subd_incr; end = ceil (upper / subd_incr) * subd_incr; @@ -1413,16 +1373,16 @@ sp_ruler_draw_ticks (SPRuler *ruler) pango_layout_get_extents (layout, &logical_rect, NULL); #if GTK_CHECK_VERSION(3,0,0) - cairo_move_to (cr, + cairo_move_to (cr, pos + 2, border.top + PANGO_PIXELS (logical_rect.y - digit_offset)); #else - cairo_move_to (cr, + cairo_move_to (cr, pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset)); #endif - pango_cairo_show_layout(cr, layout); + pango_cairo_show_layout(cr, layout); } else { @@ -1435,15 +1395,15 @@ sp_ruler_draw_ticks (SPRuler *ruler) pango_layout_get_extents (layout, NULL, &logical_rect); #if GTK_CHECK_VERSION(3,0,0) - cairo_move_to (cr, + cairo_move_to (cr, border.left + 1, pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset)); #else - cairo_move_to (cr, + cairo_move_to (cr, xthickness + 1, pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset)); #endif - pango_cairo_show_layout (cr, layout); + pango_cairo_show_layout (cr, layout); } } } @@ -1454,10 +1414,118 @@ sp_ruler_draw_ticks (SPRuler *ruler) cairo_fill (cr); + priv->backing_store_valid = TRUE; + out: cairo_destroy (cr); } +static GdkRectangle +sp_ruler_get_pos_rect (SPRuler *ruler, + gdouble position) +{ + GtkWidget *widget = GTK_WIDGET (ruler); + GtkStyle *style = gtk_widget_get_style (widget); + SPRulerPrivate *priv = SP_RULER_GET_PRIVATE (ruler); + GtkAllocation allocation; + gint width, height; + gint xthickness; + gint ythickness; + gdouble upper, lower; + gdouble increment; + GdkRectangle rect = { 0, 0, 0, 0 }; + + if (! gtk_widget_is_drawable (widget)) + return rect; + + gtk_widget_get_allocation (widget, &allocation); + + xthickness = style->xthickness; + ythickness = style->ythickness; + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + width = allocation.width; + height = allocation.height - ythickness * 2; + + rect.width = height / 2 + 2; + rect.width |= 1; /* make sure it's odd */ + rect.height = rect.width / 2 + 1; + } + else + { + width = allocation.width - xthickness * 2; + height = allocation.height; + + rect.height = width / 2 + 2; + rect.height |= 1; /* make sure it's odd */ + rect.width = rect.height / 2 + 1; + } + + sp_ruler_get_range (ruler, &lower, &upper, NULL); + + if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) + { + increment = (gdouble) width / (upper - lower); + + rect.x = ROUND ((position - lower) * increment) + (xthickness - rect.width) / 2 - 1; + rect.y = (height + rect.height) / 2 + ythickness; + } + else + { + increment = (gdouble) height / (upper - lower); + + rect.x = (width + rect.width) / 2 + xthickness; + rect.y = ROUND ((position - lower) * increment) + (ythickness - rect.height) / 2 - 1; + } + + rect.x += allocation.x; + rect.y += allocation.y; + + return rect; +} + +static gboolean +sp_ruler_idle_queue_pos_redraw (gpointer data) +{ + SPRuler *ruler = (SPRuler *)data; + SPRulerPrivate *priv = SP_RULER_GET_PRIVATE (ruler); + + sp_ruler_queue_pos_redraw (ruler); + + gboolean ret = g_source_remove(priv->pos_redraw_idle_id); + priv->pos_redraw_idle_id = 0; + + return ret; +} + +static void +sp_ruler_queue_pos_redraw (SPRuler *ruler) +{ + SPRulerPrivate *priv = SP_RULER_GET_PRIVATE (ruler); + const GdkRectangle rect = sp_ruler_get_pos_rect (ruler, priv->position); + + gtk_widget_queue_draw_area (GTK_WIDGET(ruler), + rect.x, + rect.y, + rect.width, + rect.height); + + if (priv->last_pos_rect.width != 0 || priv->last_pos_rect.height != 0) + { + gtk_widget_queue_draw_area (GTK_WIDGET(ruler), + priv->last_pos_rect.x, + priv->last_pos_rect.y, + priv->last_pos_rect.width, + priv->last_pos_rect.height); + + priv->last_pos_rect.x = 0; + priv->last_pos_rect.y = 0; + priv->last_pos_rect.width = 0; + priv->last_pos_rect.height = 0; + } +} + static PangoLayout* sp_ruler_create_layout (GtkWidget *widget, const gchar *text) diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index c6cc7dc45..5ca92b4c0 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -1592,7 +1592,7 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje "TextLineHeightAction", /* name */ _("Line Height"), /* label */ _("Line:"), /* short label */ - _("Spacing between lines (times font size)"), /* tooltip */ + _("Spacing between baselines (times font size)"), /* tooltip */ "/tools/text/lineheight", /* preferences path */ 0.0, /* default */ GTK_WIDGET(desktop->canvas), /* focusTarget */ |
