From 328fad57dbfb65e3bd31062021d5cc3081e68515 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Fri, 22 Jul 2011 04:09:27 +0200 Subject: Replace direct use of Cairo contexts and surfaces in the rendering tree with wrappers which keep some extra information about the surface, amd NRRect and NRRectL use with Geom::Rect and Geom::IntRect. Should simplify implementing filter primitive subregions. (bzr r10347.1.17) --- src/2geom/affine.h | 4 +- src/2geom/bezier-curve.cpp | 5 ++- src/2geom/coord.h | 6 --- src/2geom/forward.h | 1 + src/2geom/generic-interval.h | 4 +- src/2geom/generic-rect.h | 26 +++++++++---- src/2geom/int-point.h | 5 +++ src/2geom/interval.h | 15 +++++++- src/2geom/path-intersection.cpp | 2 + src/2geom/point.h | 15 ++++---- src/2geom/rect.h | 30 +++------------ src/2geom/transforms.h | 85 ++++++++++++++++++++++++++++++++--------- 12 files changed, 129 insertions(+), 69 deletions(-) (limited to 'src/2geom') diff --git a/src/2geom/affine.h b/src/2geom/affine.h index b07fba0f7..d7a7a0692 100644 --- a/src/2geom/affine.h +++ b/src/2geom/affine.h @@ -65,7 +65,8 @@ class Affine , MultipliableNoncommutative< Affine, Rotate , MultipliableNoncommutative< Affine, HShear , MultipliableNoncommutative< Affine, VShear - > > > > > > > + , MultipliableNoncommutative< Affine, Zoom + > > > > > > > > { Coord _c[6]; public: @@ -113,6 +114,7 @@ public: Affine &operator*=(Rotate const &r); Affine &operator*=(HShear const &h); Affine &operator*=(VShear const &v); + Affine &operator*=(Zoom const &); /// @} bool operator==(Affine const &o) const { diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp index 46aff8b49..8c40e5e42 100644 --- a/src/2geom/bezier-curve.cpp +++ b/src/2geom/bezier-curve.cpp @@ -106,10 +106,11 @@ namespace Geom BezierCurve::BezierCurve(std::vector const &pts) { - inner = D2(Bezier::Order(pts.size()-1), Bezier::Order(pts.size()-1)); + inner = D2(Bezier::Order(pts.size() - 1), Bezier::Order(pts.size() - 1)); for (unsigned d = 0; d < 2; ++d) { - for(unsigned i = 0; i <= pts.size(); i++) + for(unsigned i = 0; i < pts.size(); i++) { inner[d][i] = pts[i][d]; + } } } diff --git a/src/2geom/coord.h b/src/2geom/coord.h index c7bbcdcd4..f7bf2c5d0 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -69,9 +69,6 @@ struct CoordTraits { typedef OptIntInterval OptIntervalType; typedef IntRect RectType; typedef OptIntRect OptRectType; - inline static bool contains(IntCoord low, IntCoord high, IntCoord testlow, IntCoord testhigh) { - return low <= testlow && testhigh < high; - } }; template<> @@ -81,9 +78,6 @@ struct CoordTraits { typedef OptInterval OptIntervalType; typedef Rect RectType; typedef OptRect OptRectType; - inline static bool contains(Coord low, Coord high, Coord testlow, Coord testhigh) { - return low <= testlow && testhigh <= high; - } }; } // end namespace Geom diff --git a/src/2geom/forward.h b/src/2geom/forward.h index b1cad6f1f..0dbd9fa94 100644 --- a/src/2geom/forward.h +++ b/src/2geom/forward.h @@ -97,6 +97,7 @@ class Rotate; class Scale; class HShear; class VShear; +class Zoom; // templates template class D2; diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index d719c16c8..a32e97d4b 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -106,11 +106,11 @@ public: /// @{ /** @brief Check whether the interval includes this number. */ bool contains(C val) const { - return CoordTraits::contains(min(), max(), val, val); + return min() <= val && val <= max(); } /** @brief Check whether the interval includes the given interval. */ bool contains(Self const &val) const { - return CoordTraits::contains(min(), max(), val.min(), val.max()); + return min() <= val.min() && val.max() <= max(); } /** @brief Check whether the intervals have any common elements. */ bool intersects(Self const &val) const { diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index d60c4bb0f..6dc57b169 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -40,6 +40,7 @@ #ifndef LIB2GEOM_SEEN_GENERIC_RECT_H #define LIB2GEOM_SEEN_GENERIC_RECT_H +#include #include namespace Geom { @@ -93,30 +94,37 @@ public: * @param end End of the range * @return Rectangle that contains all points from [start, end). */ template - static GenericRect from_range(InputIterator start, InputIterator end) { + static CRect from_range(InputIterator start, InputIterator end) { assert(start != end); CPoint p1 = *start++; - GenericRect result(p1, p1); + CRect result(p1, p1); for (; start != end; ++start) { result.expandTo(*start); } return result; } /** @brief Create a rectangle from a C-style array of points it should contain. */ - static GenericRect from_array(CPoint const *c, unsigned n) { - GenericRect result = GenericRect::from_range(c, c+n); + static CRect from_array(CPoint const *c, unsigned n) { + CRect result = GenericRect::from_range(c, c+n); return result; } /** @brief Create rectangle from origin and dimensions. */ - static GenericRect from_xywh(C x, C y, C w, C h) { + static CRect from_xywh(C x, C y, C w, C h) { CPoint xy(x, y); CPoint wh(w, h); - GenericRect result(xy, xy + wh); + CRect result(xy, xy + wh); return result; } /** @brief Create rectangle from origin and dimensions. */ - static GenericRect from_xywh(CPoint const &xy, CPoint const &wh) { - GenericRect result(xy, xy + wh); + static CRect from_xywh(CPoint const &xy, CPoint const &wh) { + CRect result(xy, xy + wh); + return result; + } + /// Create infinite rectangle. + static CRect infinite() { + CPoint p0(std::numeric_limits::min(), std::numeric_limits::min()); + CPoint p1(std::numeric_limits::max(), std::numeric_limits::max()); + CRect result(p0, p1); return result; } /// @} @@ -155,6 +163,8 @@ public: C width() const { return f[X].extent(); } /** @brief Get the vertical extent of the rectangle. */ C height() const { return f[Y].extent(); } + /** @brief Get the ratio of width to height of the rectangle. */ + Coord aspectRatio() const { return Coord(width()) / Coord(height()); } /** @brief Get rectangle's width and height as a point. * @return Point with X coordinate corresponding to the width and the Y coordinate diff --git a/src/2geom/int-point.h b/src/2geom/int-point.h index cf2fe720f..1a16ecb7a 100644 --- a/src/2geom/int-point.h +++ b/src/2geom/int-point.h @@ -83,6 +83,11 @@ public: } IntCoord operator[](Dim2 d) const { return _pt[d]; } IntCoord &operator[](Dim2 d) { return _pt[d]; } + + IntCoord x() const throw() { return _pt[X]; } + IntCoord &x() throw() { return _pt[X]; } + IntCoord y() const throw() { return _pt[Y]; } + IntCoord &y() throw() { return _pt[Y]; } /// @} /// @name Vector-like arithmetic operations diff --git a/src/2geom/interval.h b/src/2geom/interval.h index ee6d674d2..e95da4811 100644 --- a/src/2geom/interval.h +++ b/src/2geom/interval.h @@ -64,7 +64,7 @@ typedef GenericOptInterval OptInterval; class Interval : public GenericInterval , boost::multipliable< Interval - , boost::multipliable< Interval, Coord + , boost::multiplicative< Interval, Coord > > { typedef GenericInterval Base; @@ -180,7 +180,20 @@ public: /// @} }; +// functions required for Python bindings +inline Interval unify(Interval const &a, Interval const &b) +{ + Interval r = a | b; + return r; +} +inline OptInterval intersect(Interval const &a, Interval const &b) +{ + OptInterval r = a & b; + return r; } + +} // end namespace Geom + #endif //SEEN_INTERVAL_H /* diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp index 7aa662abb..be3e3b7cc 100644 --- a/src/2geom/path-intersection.cpp +++ b/src/2geom/path-intersection.cpp @@ -271,6 +271,8 @@ intersect_polish_root (Curve const &A, double &s, } #ifdef HAVE_GSL + int status; + size_t iter = 0; if(0) { // the GSL version is more accurate, but taints this with GPL const size_t n = 2; struct rparams p = {A, B}; diff --git a/src/2geom/point.h b/src/2geom/point.h index 69da8a4ae..0eb771874 100644 --- a/src/2geom/point.h +++ b/src/2geom/point.h @@ -58,7 +58,8 @@ class Point , MultipliableNoncommutative< Point, Scale , MultipliableNoncommutative< Point, HShear , MultipliableNoncommutative< Point, VShear - > > > > > > > > > // this uses chaining so it looks weird, but works + , MultipliableNoncommutative< Point, Zoom + > > > > > > > > > > // this uses chaining so it looks weird, but works { Coord _pt[2]; public: @@ -111,6 +112,11 @@ public: Coord operator[](Dim2 d) const throw() { return _pt[d]; } Coord &operator[](Dim2 d) throw() { return _pt[d]; } + + Coord x() const throw() { return _pt[X]; } + Coord &x() throw() { return _pt[X]; } + Coord y() const throw() { return _pt[Y]; } + Coord &y() throw() { return _pt[Y]; } /// @} /// @name Vector operations @@ -172,12 +178,7 @@ public: Point &operator*=(Rotate const &r); Point &operator*=(HShear const &s); Point &operator*=(VShear const &s); - /** @brief Transform the point by the inverse of the specified matrix. */ - template - Point &operator/=(T const &m) { - *this *= m.inverse(); - return *this; - } + Point &operator*=(Zoom const &z); /// @} /// @name Conversion to integer points diff --git a/src/2geom/rect.h b/src/2geom/rect.h index e9f6cbeb7..f7d331523 100644 --- a/src/2geom/rect.h +++ b/src/2geom/rect.h @@ -73,31 +73,7 @@ public: Rect(Point const &a, Point const &b) : Base(a,b) {} Rect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {} Rect(Base const &b) : Base(b) {} - /** @brief Create a rectangle from a range of points. - * The resulting rectangle will contain all ponts from the range. - * The return type of iterators must be convertible to Point. - * The range must not be empty. For possibly empty ranges, see OptRect. - * @param start Beginning of the range - * @param end End of the range - * @return Rectangle that contains all points from [start, end). */ - template - static Rect from_range(InputIterator start, InputIterator end) { - Rect result = Base::from_range(start, end); - return result; - } - /** @brief Create a rectangle from a C-style array of points it should contain. */ - static Rect from_array(Point const *c, unsigned n) { - Rect result = Rect::from_range(c, c+n); - return result; - } - static Rect from_xywh(Coord x, Coord y, Coord w, Coord h) { - Rect result = Base::from_xywh(x, y, w, h); - return result; - } - static Rect from_xywh(Point const &o, Point const &dim) { - Rect result = Base::from_xywh(o, dim); - return result; - } + Rect(IntRect const &ir) : Base(ir.min(), ir.max()) {} /// @} /// @name Inspect dimensions. @@ -114,6 +90,10 @@ public: bool interiorIntersects(Rect const &r) const { return f[X].interiorIntersects(r[X]) && f[Y].interiorIntersects(r[Y]); } + /** @brief Check whether the interior includes the given point. */ + bool interiorContains(Point const &p) const { + return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]); + } /** @brief Check whether the interior includes all points in the given rectangle. * Interior of the rectangle is the entire rectangle without its borders. */ bool interiorContains(Rect const &r) const { diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h index 9623bed26..5627e8b6f 100644 --- a/src/2geom/transforms.h +++ b/src/2geom/transforms.h @@ -106,13 +106,14 @@ T pow(T const &t, int n) { class Translate : public TransformOperations< Translate > { - Translate() : vec(0, 0) {} Point vec; public: - /** @brief Construct a translation from its vector. */ - explicit Translate(Point const &p) : vec(p) {} - /** @brief Construct a translation from its coordinates. */ - explicit Translate(Coord x, Coord y) : vec(x, y) {} + /// Create a translation that doesn't do anything. + Translate() : vec(0, 0) {} + /// Construct a translation from its vector. + Translate(Point const &p) : vec(p) {} + /// Construct a translation from its coordinates. + Translate(Coord x, Coord y) : vec(x, y) {} operator Affine() const { Affine ret(1, 0, 0, 1, vec[X], vec[Y]); return ret; } Coord operator[](Dim2 dim) const { return vec[dim]; } @@ -120,9 +121,10 @@ public: Translate &operator*=(Translate const &o) { vec += o.vec; return *this; } bool operator==(Translate const &o) const { return vec == o.vec; } - /** @brief Get the inverse translation. */ + Point vector() const { return vec; } + /// Get the inverse translation. Translate inverse() const { return Translate(-vec); } - /** @brief Get a translation that doesn't do anything. */ + /// Get a translation that doesn't do anything. static Translate identity() { Translate ret; return ret; } friend class Point; @@ -136,10 +138,14 @@ class Scale : public TransformOperations< Scale > { Point vec; - Scale() : vec(1, 1) {} public: + /// Create a scaling that doesn't do anything. + Scale() : vec(1, 1) {} + /// Create a scaling from two scaling factors given as coordinates of a point. explicit Scale(Point const &p) : vec(p) {} + /// Create a scaling from two scaling factors. Scale(Coord x, Coord y) : vec(x, y) {} + /// Create an uniform scaling from a single scaling factor. explicit Scale(Coord s) : vec(s, s) {} inline operator Affine() const { Affine ret(vec[X], 0, 0, vec[Y], 0, 0); return ret; } @@ -150,6 +156,8 @@ public: Coord &operator[](unsigned d) { return vec[d]; } Scale &operator*=(Scale const &b) { vec[X] *= b[X]; vec[Y] *= b[Y]; return *this; } bool operator==(Scale const &o) const { return vec == o.vec; } + + Point vector() const { return vec; } Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); } static Scale identity() { Scale ret; return ret; } @@ -162,15 +170,16 @@ public: class Rotate : public TransformOperations< Rotate > { - Rotate() : vec(1, 0) {} - Point vec; + Point vec; ///< @todo Convert to storing the angle, as it's more space-efficient. public: + /// Construct a zero-degree rotation. + Rotate() : vec(1, 0) {} /** @brief Construct a rotation from its angle in radians. * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */ explicit Rotate(Coord theta) : vec(Point::polar(theta)) {} - /** @brief Construct a rotation from its characteristic vector. */ + /// Construct a rotation from its characteristic vector. explicit Rotate(Point const &p) : vec(unit_vector(p)) {} - /** @brief Construct a rotation from the coordinates of its characteristic vector. */ + /// Construct a rotation from the coordinates of its characteristic vector. explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); } operator Affine() const { Affine ret(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); return ret; } @@ -186,10 +195,10 @@ public: r.vec = Point(vec[X], -vec[Y]); return r; } - /** @brief Get a 0-degree rotation. */ + /// @brief Get a zero-degree rotation. static Rotate identity() { Rotate ret; return ret; } /** @brief Construct a rotation from its angle in degrees. - * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */ + * Positive arguments correspond to clockwise rotations if Y grows downwards. */ static Rotate from_degrees(Coord deg) { Coord rad = (deg / 180.0) * M_PI; return Rotate(rad); @@ -213,8 +222,8 @@ public: void setFactor(Coord nf) { f = nf; } S &operator*=(S const &s) { f += s.f; return static_cast(*this); } bool operator==(S const &s) const { return f == s.f; } - S inverse() const { return S(-f); } - static S identity() { return S(0); } + S inverse() const { S ret(-f); return ret; } + static S identity() { S ret(0); return ret; } friend class Point; friend class Affine; @@ -244,6 +253,48 @@ public: operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; } }; +/** @brief Combination of a translation and uniform scale. + * The translation part is applied first, then the result is scaled from the new origin. + * This way when the class is used to accumulate a zoom transform, trans always points + * to the new origin in original coordinates. + * @ingroup Transform */ +class Zoom + : public TransformOperations< Zoom > +{ + Coord _scale; + Point _trans; + Zoom() : _scale(1), _trans() {} +public: + /// Construct a zoom from a scaling factor. + explicit Zoom(Coord s) : _scale(s), _trans() {} + /// Construct a zoom from a translation. + explicit Zoom(Translate const &t) : _scale(1), _trans(t.vector()) {} + /// Construct a zoom from a scaling factor and a translation. + Zoom(Coord s, Translate const &t) : _scale(s), _trans(t.vector()) {} + + operator Affine() const { + Affine ret(_scale, 0, 0, _scale, _trans[X] * _scale, _trans[Y] * _scale); + return ret; + } + Zoom &operator*=(Zoom const &z) { + _trans += z._trans / _scale; + _scale *= z._scale; + return *this; + } + bool operator==(Zoom const &z) const { return _scale == z._scale && _trans == z._trans; } + + Coord scale() const { return _scale; } + void setScale(Coord s) { _scale = s; } + Point translation() const { return _trans; } + void setTranslation(Point const &p) { _trans = p; } + Zoom inverse() const { Zoom ret(1/_scale, Translate(-_trans*_scale)); return ret; } + static Zoom identity() { Zoom ret(1.0); return ret; } + static Zoom map_rect(Rect const &old_r, Rect const &new_r); + + friend class Point; + friend class Affine; +}; + /** @brief Specialization of exponentiation for Scale. * @relates Scale */ template<> @@ -259,7 +310,7 @@ inline Translate pow(Translate const &t, int n) { return ret; } -//TODO: matrix to trans/scale/rotate +//TODO: decomposition of Affine into some finite combination of the above classes } // end namespace Geom -- cgit v1.2.3 From 4dd33aa4d5c57706c7f64f63391174954160a308 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosi??ski Date: Sat, 6 Aug 2011 14:18:32 +0200 Subject: Rewrite NRArenaItem hierarchy into C++ (bzr r10347.1.21) --- src/2geom/affine.cpp | 12 +++++ src/2geom/affine.h | 10 ++--- src/2geom/coord.h | 38 ++++++++++++++++ src/2geom/generic-interval.h | 8 +--- src/2geom/generic-rect.h | 105 +++++++++++++++++++++++++++++++++++-------- src/2geom/interval.h | 3 -- src/2geom/linear.h | 2 +- src/2geom/rect.h | 1 - src/2geom/transforms.h | 30 ++++++++++++- 9 files changed, 173 insertions(+), 36 deletions(-) (limited to 'src/2geom') diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp index 2a1f18d77..c31b9ba90 100644 --- a/src/2geom/affine.cpp +++ b/src/2geom/affine.cpp @@ -410,6 +410,9 @@ Affine &Affine::operator*=(Affine const &o) { } //TODO: What's this!?! +/** Given a matrix m such that unit_circle = m*x, this returns the + * quadratic form x*A*x = 1. + * @relates Affine */ Affine elliptic_quadratic_form(Affine const &m) { double od = m[0] * m[1] + m[2] * m[3]; Affine ret (m[0]*m[0] + m[1]*m[1], od, @@ -469,6 +472,15 @@ Eigen::Eigen(double m[2][2]) { vectors[i] = Point(0,0); } +/** @brief Nearness predicate for affine transforms + * @returns True if all entries of matrices are within eps of each other */ +bool are_near(Affine const &a, Affine const &b, Coord eps) +{ + return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) && + are_near(a[2], b[2], eps) && are_near(a[3], b[3], eps) && + are_near(a[4], b[4], eps) && are_near(a[5], b[5], eps); +} + } //namespace Geom /* diff --git a/src/2geom/affine.h b/src/2geom/affine.h index d7a7a0692..22f8bd9f5 100644 --- a/src/2geom/affine.h +++ b/src/2geom/affine.h @@ -200,9 +200,8 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::Affine &m) return out_file; } -/** Given a matrix m such that unit_circle = m*x, this returns the - * quadratic form x*A*x = 1. - * @relates Affine */ +// Affine factories +Affine from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0)); Affine elliptic_quadratic_form(Affine const &m); /** Given a matrix (ignoring the translation) this returns the eigen @@ -215,9 +214,6 @@ public: Eigen(double M[2][2]); }; -// Affine factories -Affine from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0)); - /** @brief Create an identity matrix. * This is a convenience function identical to Affine::identity(). */ inline Affine identity() { @@ -239,6 +235,8 @@ inline Affine Affine::identity() { return ret; // allow NRVO } +bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON); + } // end namespace Geom #endif // LIB2GEOM_SEEN_AFFINE_H diff --git a/src/2geom/coord.h b/src/2geom/coord.h index f7bf2c5d0..90e776665 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -34,6 +34,7 @@ #include #include +#include #include <2geom/forward.h> namespace Geom { @@ -62,6 +63,9 @@ inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= template struct CoordTraits {}; +// NOTE: operator helpers for Rect and Interval are defined here. +// This is to avoid increasing their size through multiple inheritance. + template<> struct CoordTraits { typedef IntPoint PointType; @@ -69,6 +73,22 @@ struct CoordTraits { typedef OptIntInterval OptIntervalType; typedef IntRect RectType; typedef OptIntRect OptRectType; + + typedef + boost::equality_comparable< IntervalType + , boost::additive< IntervalType + , boost::additive< IntervalType, IntCoord + , boost::orable< IntervalType + > > > > + IntervalOps; + + typedef + boost::equality_comparable< RectType + , boost::orable< RectType + , boost::orable< RectType, OptRectType + , boost::additive< RectType, PointType + > > > > + RectOps; }; template<> @@ -78,6 +98,24 @@ struct CoordTraits { typedef OptInterval OptIntervalType; typedef Rect RectType; typedef OptRect OptRectType; + + typedef + boost::equality_comparable< IntervalType + , boost::additive< IntervalType + , boost::multipliable< IntervalType + , boost::orable< IntervalType + , boost::arithmetic< IntervalType, Coord + > > > > > + IntervalOps; + + typedef + boost::equality_comparable< RectType + , boost::orable< RectType + , boost::orable< RectType, OptRectType + , boost::additive< RectType, PointType + , boost::multipliable< RectType, Affine + > > > > > + RectOps; }; } // end namespace Geom diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index a32e97d4b..0212da676 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include <2geom/coord.h> namespace Geom { @@ -47,11 +47,7 @@ class GenericOptInterval; */ template class GenericInterval - : boost::equality_comparable< GenericInterval - , boost::additive< GenericInterval - , boost::additive< GenericInterval, C - , boost::orable< GenericInterval - > > > > + : CoordTraits::IntervalOps { typedef GenericInterval Self; protected: diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index 2db30dfa9..efe499809 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -42,6 +42,7 @@ #include #include +#include <2geom/coord.h> namespace Geom { @@ -54,11 +55,7 @@ class GenericOptRect; */ template class GenericRect - : boost::additive< typename CoordTraits::RectType, typename CoordTraits::PointType - , boost::equality_comparable< typename CoordTraits::RectType - , boost::orable< typename CoordTraits::RectType - , boost::orable< typename CoordTraits::RectType, typename CoordTraits::OptRectType - > > > > + : CoordTraits::RectOps { typedef typename CoordTraits::IntervalType CInterval; typedef typename CoordTraits::PointType CPoint; @@ -131,15 +128,22 @@ public: /// @name Inspect dimensions. /// @{ - CInterval &operator[](unsigned i) { return f[i]; } + CInterval &operator[](unsigned i) { return f[i]; } CInterval const &operator[](unsigned i) const { return f[i]; } + CInterval &operator[](Dim2 d) { return f[d]; } + CInterval const &operator[](Dim2 d) const { return f[d]; } + /** @brief Get the corner of the rectangle with smallest coordinate values. + * In 2Geom standard coordinate system, this means upper left. */ CPoint min() const { return CPoint(f[X].min(), f[Y].min()); } + /** @brief Get the corner of the rectangle with largest coordinate values. + * In 2Geom standard coordinate system, this means lower right. */ CPoint max() const { return CPoint(f[X].max(), f[Y].max()); } /** @brief Return the n-th corner of the rectangle. - * If the Y axis grows upwards, this returns corners in clockwise order - * starting from the lower left. If Y grows downwards, it returns the corners - * in counter-clockwise order starting from the upper left. */ + * Returns corners in the direction of growing angles, starting from + * the one given by min(). For the standard coordinate system used + * in 2Geom (+Y downwards), this means clockwise starting from + * the upper left. */ CPoint corner(unsigned i) const { switch(i % 4) { case 0: return CPoint(f[X].min(), f[Y].min()); @@ -196,10 +200,10 @@ public: } /** @brief Check whether the rectangles have any common points. - * A non-empty rectangle will not intersect empty rectangles. */ + * Empty rectangles will not intersect with any other rectangle. */ inline bool intersects(OptCRect const &r) const; /** @brief Check whether the rectangle includes all points in the given rectangle. - * A non-empty rectangle will contain any empty rectangle. */ + * Empty rectangles will be contained in any non-empty rectangle. */ inline bool contains(OptCRect const &r) const; /** @brief Check whether the given point is within the rectangle. */ @@ -224,11 +228,11 @@ public: void expandTo(CPoint const &p) { f[X].expandTo(p[X]); f[Y].expandTo(p[Y]); } - /** @brief Enlarge the rectangle to contain the given rectangle. */ + /** @brief Enlarge the rectangle to contain the argument. */ void unionWith(CRect const &b) { f[X].unionWith(b[X]); f[Y].unionWith(b[Y]); } - /** @brief Enlarge the rectangle to contain the given rectangle. + /** @brief Enlarge the rectangle to contain the argument. * Unioning with an empty rectangle results in no changes. */ void unionWith(OptCRect const &b); @@ -244,7 +248,8 @@ public: * This will expand the width by the X coordinate of the point in both directions * and the height by Y coordinate of the point. Negative coordinate values will * shrink the rectangle. If -p[X] is larger than half of the width, - * the X interval will contain only the X coordinate of the midpoint; same for height. */ + * the X interval will contain only the X coordinate of the midpoint; + * same for height. */ void expandBy(CPoint const &p) { f[X].expandBy(p[X]); f[Y].expandBy(p[Y]); } @@ -297,30 +302,66 @@ class GenericOptRect typedef typename CoordTraits::OptRectType OptCRect; typedef boost::optional Base; public: + /// @name Create potentially empty rectangles. + /// @{ GenericOptRect() : Base() {} GenericOptRect(GenericRect const &a) : Base(CRect(a)) {} GenericOptRect(CPoint const &a, CPoint const &b) : Base(CRect(a, b)) {} - /** - * Creates an empty OptRect when one of the argument intervals is empty. - */ + /// Creates an empty OptRect when one of the argument intervals is empty. GenericOptRect(OptCInterval const &x_int, OptCInterval const &y_int) { if (x_int && y_int) { *this = CRect(*x_int, *y_int); } // else, stay empty. } + /** @brief Create a rectangle from a range of points. + * The resulting rectangle will contain all ponts from the range. + * If the range contains no points, the result will be an empty rectangle. + * The return type of iterators must be convertible to the corresponding + * point type (Point or IntPoint). + * @param start Beginning of the range + * @param end End of the range + * @return Rectangle that contains all points from [start, end). */ + template + static OptCRect from_range(InputIterator start, InputIterator end) { + OptCRect result; + for (; start != end; ++start) { + result.expandTo(*start); + } + return result; + } + /// @} + /// @name Check other rectangles and points for inclusion. + /// @{ /** @brief Check for emptiness. */ inline bool isEmpty() const { return !*this; }; - + /** @brief Check whether the rectangles have any common points. + * Empty rectangles will not intersect with any other rectangle. */ bool intersects(CRect const &r) const { return r.intersects(*this); } + /** @brief Check whether the rectangle includes all points in the given rectangle. + * Empty rectangles will be contained in any non-empty rectangle. */ bool contains(CRect const &r) const { return *this && (*this)->contains(r); } + /** @brief Check whether the rectangles have any common points. + * Empty rectangles will not intersect with any other rectangle. + * Two empty rectangles will not intersect each other. */ bool intersects(OptCRect const &r) const { return *this && (*this)->intersects(r); } + /** @brief Check whether the rectangle includes all points in the given rectangle. + * Empty rectangles will be contained in any non-empty rectangle. + * An empty rectangle will not contain other empty rectangles. */ bool contains(OptCRect const &r) const { return *this && (*this)->contains(r); } + /** @brief Check whether the given point is within the rectangle. + * An empty rectangle will not contain any points. */ bool contains(CPoint const &p) const { return *this && (*this)->contains(p); } + /// @} + /// @name Modify the potentially empty rectangle. + /// @{ + /** @brief Enlarge the rectangle to contain the argument. + * If this rectangle is empty, after callng this method it will + * be equal to the argument. */ void unionWith(CRect const &b) { if (*this) { (*this)->unionWith(b); @@ -328,9 +369,16 @@ public: *this = b; } } + /** @brief Enlarge the rectangle to contain the argument. + * Unioning with an empty rectangle results in no changes. + * If this rectangle is empty, after calling this method it will + * be equal to the argument. */ void unionWith(OptCRect const &b) { if (b) unionWith(*b); } + /** @brief Leave only the area overlapping with the argument. + * If the rectangles do not have any points in common, after calling + * this method the rectangle will be empty. */ void intersectWith(CRect const &b) { if (!*this) return; OptCInterval x = (**this)[X] & b[X], y = (**this)[Y] & b[Y]; @@ -340,6 +388,9 @@ public: *(static_cast(this)) = boost::none; } } + /** @brief Leave only the area overlapping with the argument. + * If the argument is empty or the rectangles do not have any points + * in common, after calling this method the rectangle will be empty. */ void intersectWith(OptCRect const &b) { if (b) { intersectWith(*b); @@ -347,18 +398,36 @@ public: *(static_cast(this)) = boost::none; } } + /** @brief Create or enlarge the rectangle to contain the given point. + * If the rectangle is empty, after calling this method it will be non-empty + * and it will contain only the given point. */ + void expandTo(CPoint const &p) { + if (*this) { + (*this).expandTo(p); + } else { + *this = CRect(p, p); + } + } + /// @} + + /// @name Operators + /// @{ + /** @brief Union with @a b */ GenericOptRect &operator|=(OptCRect const &b) { unionWith(b); return *this; } + /** @brief Intersect with @a b */ GenericOptRect &operator&=(CRect const &b) { intersectWith(b); return *this; } + /** @brief Intersect with @a b */ GenericOptRect &operator&=(OptCRect const &b) { intersectWith(b); return *this; } + /// @} }; template diff --git a/src/2geom/interval.h b/src/2geom/interval.h index e95da4811..711eaa5e2 100644 --- a/src/2geom/interval.h +++ b/src/2geom/interval.h @@ -63,9 +63,6 @@ typedef GenericOptInterval OptInterval; */ class Interval : public GenericInterval - , boost::multipliable< Interval - , boost::multiplicative< Interval, Coord - > > { typedef GenericInterval Base; public: diff --git a/src/2geom/linear.h b/src/2geom/linear.h index df6dd9904..448ab3bb7 100644 --- a/src/2geom/linear.h +++ b/src/2geom/linear.h @@ -55,7 +55,7 @@ class SBasis; class Linear{ public: double a[2]; - Linear() {} + Linear() { a[0] = 0; a[1] = 0; } Linear(double aa, double b) {a[0] = aa; a[1] = b;} Linear(double aa) {a[0] = aa; a[1] = aa;} diff --git a/src/2geom/rect.h b/src/2geom/rect.h index f7d331523..b79a0a04f 100644 --- a/src/2geom/rect.h +++ b/src/2geom/rect.h @@ -59,7 +59,6 @@ typedef GenericOptRect OptRect; */ class Rect : public GenericRect - , boost::multipliable< Rect, Affine > { typedef GenericRect Base; public: diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h index 5627e8b6f..eaf869056 100644 --- a/src/2geom/transforms.h +++ b/src/2geom/transforms.h @@ -45,10 +45,11 @@ namespace Geom { * @ingroup Concepts */ template struct TransformConcept { - T t; + T t, t2; Affine m; Point p; bool bool_; + Coord epsilon; void constraints() { m = t; //implicit conversion m *= t; @@ -63,6 +64,8 @@ struct TransformConcept { bool_ = (t != t); t = T::identity(); t = t.inverse(); + bool_ = are_near(t, t2); + bool_ = are_near(t, t2, epsilon); } }; @@ -130,6 +133,10 @@ public: friend class Point; }; +inline bool are_near(Translate const &a, Translate const &b, Coord eps=EPSILON) { + return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps); +} + /** @brief Scaling from the origin. * During scaling, the point (0,0) will not move. To obtain a scale with a different * invariant point, combine with translation to the origin and back. @@ -164,6 +171,10 @@ public: friend class Point; }; +inline bool are_near(Scale const &a, Scale const &b, Coord eps=EPSILON) { + return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps); +} + /** @brief Rotation around the origin. * Combine with translations to the origin and back to get a rotation around a different point. * @ingroup Transforms */ @@ -207,6 +218,10 @@ public: friend class Point; }; +inline bool are_near(Rotate const &a, Rotate const &b, Coord eps=EPSILON) { + return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps); +} + /** @brief Common base for shearing transforms. * This class is an implementation detail and should not be used directly. * @ingroup Transforms */ @@ -241,6 +256,10 @@ public: operator Affine() const { Affine ret(1, 0, f, 1, 0, 0); return ret; } }; +inline bool are_near(HShear const &a, HShear const &b, Coord eps=EPSILON) { + return are_near(a.factor(), b.factor(), eps); +} + /** @brief Vertical shearing. * Points on the Y axis will not move. Combine with translations to get a shear * with a different invariant line. @@ -253,6 +272,10 @@ public: operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; } }; +inline bool are_near(VShear const &a, VShear const &b, Coord eps=EPSILON) { + return are_near(a.factor(), b.factor(), eps); +} + /** @brief Combination of a translation and uniform scale. * The translation part is applied first, then the result is scaled from the new origin. * This way when the class is used to accumulate a zoom transform, trans always points @@ -295,6 +318,11 @@ public: friend class Affine; }; +inline bool are_near(Zoom const &a, Zoom const &b, Coord eps=EPSILON) { + return are_near(a.scale(), b.scale(), eps) && + are_near(a.translation(), b.translation(), eps); +} + /** @brief Specialization of exponentiation for Scale. * @relates Scale */ template<> -- cgit v1.2.3