diff options
Diffstat (limited to 'src/2geom')
| -rw-r--r-- | src/2geom/affine.cpp | 60 | ||||
| -rw-r--r-- | src/2geom/coord.h | 42 | ||||
| -rw-r--r-- | src/2geom/forward.h | 4 | ||||
| -rw-r--r-- | src/2geom/generic-interval.h | 31 | ||||
| -rw-r--r-- | src/2geom/generic-rect.h | 36 | ||||
| -rw-r--r-- | src/2geom/interval.h | 43 | ||||
| -rw-r--r-- | src/2geom/linear.h | 2 | ||||
| -rw-r--r-- | src/2geom/rect.h | 42 |
8 files changed, 182 insertions, 78 deletions
diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp index c31b9ba90..1be5d9fe8 100644 --- a/src/2geom/affine.cpp +++ b/src/2geom/affine.cpp @@ -144,6 +144,7 @@ bool Affine::isNonzeroTranslation(Coord eps) const { 0 & b & 0 \\ 0 & 0 & 1 \end{array}\right]\f$. */ bool Affine::isScale(Coord eps) const { + if (isSingular(eps)) return false; return are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps); } @@ -156,6 +157,7 @@ bool Affine::isScale(Coord eps) const { 0 & b & 0 \\ 0 & 0 & 1 \end{array}\right]\f$ and \f$a, b \neq 1\f$. */ bool Affine::isNonzeroScale(Coord eps) const { + if (isSingular(eps)) return false; return (!are_near(_c[0], 1.0, eps) || !are_near(_c[3], 1.0, eps)) && //NOTE: these are the diags, and the next line opposite diags are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps); @@ -165,11 +167,12 @@ bool Affine::isNonzeroScale(Coord eps) const { * @param eps Numerical tolerance * @return True iff the matrix is of the form * \f$\left[\begin{array}{ccc} - a & 0 & 0 \\ - 0 & a & 0 \\ - 0 & 0 & 1 \end{array}\right]\f$. */ + a_1 & 0 & 0 \\ + 0 & a_2 & 0 \\ + 0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$. */ bool Affine::isUniformScale(Coord eps) const { - return are_near(_c[0], _c[3], eps) && + if (isSingular(eps)) return false; + return are_near(fabs(_c[0]), fabs(_c[3]), eps) && are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps); } @@ -178,11 +181,16 @@ bool Affine::isUniformScale(Coord eps) const { * @param eps Numerical tolerance * @return True iff the matrix is of the form * \f$\left[\begin{array}{ccc} - a & 0 & 0 \\ - 0 & a & 0 \\ - 0 & 0 & 1 \end{array}\right]\f$ and \f$a \neq 1\f$. */ + a_1 & 0 & 0 \\ + 0 & a_2 & 0 \\ + 0 & 0 & 1 \end{array}\right]\f$ where \f$|a_1| = |a_2|\f$ + * and \f$a_1, a_2 \neq 1\f$. */ bool Affine::isNonzeroUniformScale(Coord eps) const { - return !are_near(_c[0], 1.0, eps) && are_near(_c[0], _c[3], eps) && + if (isSingular(eps)) return false; + // we need to test both c0 and c3 to handle the case of flips, + // which should be treated as nonzero uniform scales + return !(are_near(_c[0], 1.0, eps) && are_near(_c[3], 1.0, eps)) && + are_near(fabs(_c[0]), fabs(_c[3]), eps) && are_near(_c[1], 0.0, eps) && are_near(_c[2], 0.0, eps) && are_near(_c[4], 0.0, eps) && are_near(_c[5], 0.0, eps); } @@ -266,15 +274,17 @@ bool Affine::isNonzeroVShear(Coord eps) const { } /** @brief Check whether this matrix represents zooming. - * Zooming is any combination of translation and uniform scaling. It preserves angles, ratios - * of distances between arbitrary points and unit vectors of line segments. + * Zooming is any combination of translation and uniform non-flipping scaling. + * It preserves angles, ratios of distances between arbitrary points + * and unit vectors of line segments. * @param eps Numerical tolerance - * @return True iff the matrix is of the form + * @return True iff the matrix is invertible and of the form * \f$\left[\begin{array}{ccc} a & 0 & 0 \\ 0 & a & 0 \\ b & c & 1 \end{array}\right]\f$. */ bool Affine::isZoom(Coord eps) const { + if (isSingular(eps)) return false; return are_near(_c[0], _c[3], eps) && are_near(_c[1], 0, eps) && are_near(_c[2], 0, eps); } @@ -290,30 +300,42 @@ bool Affine::preservesArea(Coord eps) const } /** @brief Check whether the transformation preserves angles between lines. - * This means that the transformation can be any combination of translation, uniform scaling - * and rotation. + * This means that the transformation can be any combination of translation, uniform scaling, + * rotation and flipping. * @param eps Numerical tolerance * @return True iff the matrix is of the form * \f$\left[\begin{array}{ccc} - a & b & 0 \\ - -b & a & 0 \\ - c & d & 1 \end{array}\right]\f$. */ + a & b & 0 \\ + -b & a & 0 \\ + c & d & 1 \end{array}\right]\f$ or + \f$\left[\begin{array}{ccc} + -a & b & 0 \\ + b & a & 0 \\ + c & d & 1 \end{array}\right]\f$. */ bool Affine::preservesAngles(Coord eps) const { - return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps); + if (isSingular(eps)) return false; + return (are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) || + (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps)); } /** @brief Check whether the transformation preserves distances between points. - * This means that the transformation can be any combination of translation and rotation. + * This means that the transformation can be any combination of translation, + * rotation and flipping. * @param eps Numerical tolerance * @return True iff the matrix is of the form * \f$\left[\begin{array}{ccc} a & b & 0 \\ -b & a & 0 \\ + c & d & 1 \end{array}\right]\f$ or + \f$\left[\begin{array}{ccc} + -a & b & 0 \\ + b & a & 0 \\ c & d & 1 \end{array}\right]\f$ and \f$a^2 + b^2 = 1\f$. */ bool Affine::preservesDistances(Coord eps) const { - return are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) && + return ((are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps)) || + (are_near(_c[0], -_c[3], eps) && are_near(_c[1], _c[2], eps))) && are_near(_c[0] * _c[0] + _c[1] * _c[1], 1.0, eps); } diff --git a/src/2geom/coord.h b/src/2geom/coord.h index 90e776665..78a852f32 100644 --- a/src/2geom/coord.h +++ b/src/2geom/coord.h @@ -75,18 +75,18 @@ struct CoordTraits<IntCoord> { typedef OptIntRect OptRectType; typedef - boost::equality_comparable< IntervalType - , boost::additive< IntervalType - , boost::additive< IntervalType, IntCoord - , boost::orable< IntervalType + boost::equality_comparable< IntInterval + , boost::additive< IntInterval + , boost::additive< IntInterval, IntCoord + , boost::orable< IntInterval > > > > IntervalOps; typedef - boost::equality_comparable< RectType - , boost::orable< RectType - , boost::orable< RectType, OptRectType - , boost::additive< RectType, PointType + boost::equality_comparable< IntRect + , boost::orable< IntRect + , boost::orable< IntRect, OptIntRect + , boost::additive< IntRect, IntPoint > > > > RectOps; }; @@ -100,21 +100,23 @@ struct CoordTraits<Coord> { typedef OptRect OptRectType; typedef - boost::equality_comparable< IntervalType - , boost::additive< IntervalType - , boost::multipliable< IntervalType - , boost::orable< IntervalType - , boost::arithmetic< IntervalType, Coord - > > > > > + boost::equality_comparable< Interval + , boost::equality_comparable< Interval, IntInterval + , boost::additive< Interval + , boost::multipliable< Interval + , boost::orable< Interval + , boost::arithmetic< Interval, Coord + > > > > > > IntervalOps; typedef - boost::equality_comparable< RectType - , boost::orable< RectType - , boost::orable< RectType, OptRectType - , boost::additive< RectType, PointType - , boost::multipliable< RectType, Affine - > > > > > + boost::equality_comparable< Rect + , boost::equality_comparable< Rect, IntRect + , boost::orable< Rect + , boost::orable< Rect, OptRect + , boost::additive< Rect, Point + , boost::multipliable< Rect, Affine + > > > > > > RectOps; }; diff --git a/src/2geom/forward.h b/src/2geom/forward.h index 0dbd9fa94..70cac1f7d 100644 --- a/src/2geom/forward.h +++ b/src/2geom/forward.h @@ -49,13 +49,13 @@ class Ray; template <typename> class GenericInterval; template <typename> class GenericOptInterval; class Interval; -typedef GenericOptInterval<Coord> OptInterval; +class OptInterval; typedef GenericInterval<IntCoord> IntInterval; typedef GenericOptInterval<IntCoord> OptIntInterval; template <typename> class GenericRect; template <typename> class GenericOptRect; class Rect; -typedef GenericOptRect<Coord> OptRect; +class OptRect; typedef GenericRect<IntCoord> IntRect; typedef GenericOptRect<IntCoord> OptIntRect; diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h index 0212da676..87d3be2c1 100644 --- a/src/2geom/generic-interval.h +++ b/src/2geom/generic-interval.h @@ -49,6 +49,7 @@ template <typename C> class GenericInterval : CoordTraits<C>::IntervalOps { + typedef typename CoordTraits<C>::IntervalType CInterval; typedef GenericInterval<C> Self; protected: C _b[2]; @@ -76,15 +77,15 @@ public: * @param end End of the range * @return Interval that contains all values from [start, end). */ template <typename InputIterator> - static Self from_range(InputIterator start, InputIterator end) { + static CInterval from_range(InputIterator start, InputIterator end) { assert(start != end); - Self result(*start++); + CInterval result(*start++); for (; start != end; ++start) result.expandTo(*start); return result; } /** @brief Create an interval from a C-style array of values it should contain. */ - static Self from_array(C const *c, unsigned n) { - Self result = from_range(c, c+n); + static CInterval from_array(C const *c, unsigned n) { + CInterval result = from_range(c, c+n); return result; } /// @} @@ -94,7 +95,7 @@ public: C min() const { return _b[0]; } C max() const { return _b[1]; } C extent() const { return max() - min(); } - C middle() const { return (max() + min()) * 0.5; } + C middle() const { return (max() + min()) / 2; } bool isSingular() const { return min() == max(); } /// @} @@ -105,11 +106,11 @@ public: return min() <= val && val <= max(); } /** @brief Check whether the interval includes the given interval. */ - bool contains(Self const &val) const { + bool contains(CInterval const &val) const { return min() <= val.min() && val.max() <= max(); } /** @brief Check whether the intervals have any common elements. */ - bool intersects(Self const &val) const { + bool intersects(CInterval const &val) const { return contains(val.min()) || contains(val.max()) || val.contains(*this); } /// @} @@ -159,7 +160,7 @@ public: * The resulting interval will contain all points of both intervals. * It might also contain some points which didn't belong to either - this happens * when the intervals did not have any common elements. */ - void unionWith(Self const &a) { + void unionWith(CInterval const &a) { if(a._b[0] < _b[0]) _b[0] = a._b[0]; if(a._b[1] > _b[1]) _b[1] = a._b[1]; } @@ -187,7 +188,7 @@ public: /** @brief Add two intervals. * Sum is defined as the set of points that can be obtained by adding any two values * from both operands: \f$S = \{x \in A, y \in B: x + y\}\f$ */ - Self &operator+=(Self const &o) { + Self &operator+=(CInterval const &o) { _b[0] += o._b[0]; _b[1] += o._b[1]; return *this; @@ -196,7 +197,7 @@ public: * Difference is defined as the set of points that can be obtained by subtracting * any value from the second operand from any value from the first operand: * \f$S = \{x \in A, y \in B: x - y\}\f$ */ - Self &operator-=(Self const &o) { + Self &operator-=(CInterval const &o) { // equal to *this += -o _b[0] -= o._b[1]; _b[1] -= o._b[0]; @@ -205,12 +206,12 @@ public: /** @brief Union two intervals. * Note that the intersection-and-assignment operator is not defined, * because the result of an intersection can be empty, while Interval cannot. */ - Self &operator|=(Self const &o) { + Self &operator|=(CInterval const &o) { unionWith(o); return *this; } /** @brief Test for interval equality. */ - bool operator==(Self const &other) const { + bool operator==(CInterval const &other) const { return min() == other.min() && max() == other.max(); } /// @} @@ -230,15 +231,15 @@ inline GenericInterval<C> unify(GenericInterval<C> const &a, GenericInterval<C> template <typename C> class GenericOptInterval : public boost::optional<typename CoordTraits<C>::IntervalType> - , boost::orable< GenericOptInterval<C>, typename CoordTraits<C>::OptIntervalType - , boost::andable< GenericOptInterval<C>, typename CoordTraits<C>::OptIntervalType + , boost::orable< GenericOptInterval<C> + , boost::andable< GenericOptInterval<C> > > { typedef typename CoordTraits<C>::IntervalType CInterval; typedef typename CoordTraits<C>::OptIntervalType OptCInterval; typedef boost::optional<CInterval> Base; public: - /// @name Create optionally empty intervals of integers. + /// @name Create optionally empty intervals. /// @{ /** @brief Create an empty interval. */ GenericOptInterval() : Base() {} diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index efe499809..719b37385 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -135,10 +135,10 @@ public: /** @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()); } + CPoint min() const { CPoint p(f[X].min(), f[Y].min()); return p; } /** @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()); } + CPoint max() const { CPoint p(f[X].max(), f[Y].max()); return p; } /** @brief Return the n-th corner of the rectangle. * Returns corners in the direction of growing angles, starting from * the one given by min(). For the standard coordinate system used @@ -242,7 +242,15 @@ public: * half of the width, the X interval will contain only the X coordinate * of the midpoint; same for height. */ void expandBy(C amount) { - f[X].expandBy(amount); f[Y].expandBy(amount); + expandBy(amount, amount); + } + /** @brief Expand the rectangle in both directions. + * Note that this is different from scaling. Negative values wil shrink the + * rectangle. If <code>-x</code> is larger than + * half of the width, the X interval will contain only the X coordinate + * of the midpoint; same for height. */ + void expandBy(C x, C y) { + f[X].expandBy(x); f[Y].expandBy(y); } /** @brief Expand the rectangle by the coordinates of the given point. * This will expand the width by the X coordinate of the point in both directions @@ -250,8 +258,8 @@ public: * shrink the rectangle. If <code>-p[X]</code> is larger than half of the width, * 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]); + void expandBy(CPoint const &p) { + expandBy(p[X], p[Y]); } /// @} @@ -279,7 +287,7 @@ public: return *this; } /** @brief Test for equality of rectangles. */ - bool operator==(GenericRect<C> const &o) const { return f[X] == o[X] && f[Y] == o[Y]; } + bool operator==(CRect const &o) const { return f[X] == o[X] && f[Y] == o[Y]; } /// @} }; @@ -290,10 +298,12 @@ public: template <typename C> class GenericOptRect : public boost::optional<typename CoordTraits<C>::RectType> + , boost::equality_comparable< typename CoordTraits<C>::OptRectType + , boost::equality_comparable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType , boost::orable< typename CoordTraits<C>::OptRectType , boost::andable< typename CoordTraits<C>::OptRectType , boost::andable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType - > > > + > > > > > { typedef typename CoordTraits<C>::IntervalType CInterval; typedef typename CoordTraits<C>::OptIntervalType OptCInterval; @@ -307,6 +317,7 @@ public: GenericOptRect() : Base() {} GenericOptRect(GenericRect<C> const &a) : Base(CRect(a)) {} GenericOptRect(CPoint const &a, CPoint const &b) : Base(CRect(a, b)) {} + GenericOptRect(C x0, C y0, C x1, C y1) : Base(CRect(x0, y0, x1, y1)) {} /// 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) { @@ -314,6 +325,7 @@ public: } // 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. @@ -427,6 +439,16 @@ public: intersectWith(b); return *this; } + /** @brief Test for equality. + * All empty rectangles are equal. */ + bool operator==(OptCRect const &other) const { + if (!*this != !other) return false; + return *this ? (**this == *other) : true; + } + bool operator==(CRect const &other) const { + if (!*this) return false; + return **this == other; + } /// @} }; diff --git a/src/2geom/interval.h b/src/2geom/interval.h index 711eaa5e2..b1fac04d9 100644 --- a/src/2geom/interval.h +++ b/src/2geom/interval.h @@ -47,12 +47,6 @@ namespace Geom { -/** - * @brief Range of real numbers that can be empty. - * @ingroup Primitives - */ -typedef GenericOptInterval<Coord> OptInterval; - /** * @brief Range of real numbers that is never empty. * @@ -128,8 +122,6 @@ public: /// @name Operators /// @{ - inline operator OptInterval() { return OptInterval(*this); } - // IMPL: ScalableConcept /** @brief Scale an interval */ Interval &operator*=(Coord s) { @@ -158,6 +150,12 @@ public: expandTo(mx * o.max()); return *this; } + bool operator==(IntInterval const &ii) const { + return min() == Coord(ii.min()) && max() == Coord(ii.max()); + } + bool operator==(Interval const &other) const { + return Base::operator==(other); + } /// @} /// @name Rounding to integer values @@ -177,6 +175,35 @@ public: /// @} }; +/** + * @brief Range of real numbers that can be empty. + * @ingroup Primitives + */ +class OptInterval + : public GenericOptInterval<Coord> +{ + typedef GenericOptInterval<Coord> Base; +public: + /// @name Create optionally empty intervals. + /// @{ + /** @brief Create an empty interval. */ + OptInterval() : Base() {} + /** @brief Wrap an existing interval. */ + OptInterval(Interval const &a) : Base(a) {} + /** @brief Create an interval containing a single point. */ + OptInterval(Coord u) : Base(u) {} + /** @brief Create an interval containing a range of numbers. */ + OptInterval(Coord u, Coord v) : Base(u,v) {} + OptInterval(Base const &b) : Base(b) {} + + /** @brief Promote from IntInterval. */ + OptInterval(IntInterval const &i) : Base(Interval(i)) {} + /** @brief Promote from OptIntInterval. */ + OptInterval(OptIntInterval const &i) : Base() { + if (i) *this = Interval(*i); + } +}; + // functions required for Python bindings inline Interval unify(Interval const &a, Interval const &b) { diff --git a/src/2geom/linear.h b/src/2geom/linear.h index 448ab3bb7..df6dd9904 100644 --- a/src/2geom/linear.h +++ b/src/2geom/linear.h @@ -55,7 +55,7 @@ class SBasis; class Linear{ public: double a[2]; - Linear() { a[0] = 0; a[1] = 0; } + Linear() {} 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 b79a0a04f..2516bcfa6 100644 --- a/src/2geom/rect.h +++ b/src/2geom/rect.h @@ -48,12 +48,6 @@ namespace Geom { /** - * @brief Axis-aligned rectangle that can be empty. - * @ingroup Primitives - */ -typedef GenericOptRect<Coord> OptRect; - -/** * @brief Axis aligned, non-empty rectangle. * @ingroup Primitives */ @@ -118,9 +112,45 @@ public: /// @name Operators /// @{ Rect &operator*=(Affine const &m); + bool operator==(IntRect const &ir) const { + return f[X] == ir[X] && f[Y] == ir[Y]; + } + bool operator==(Rect const &other) const { + return Base::operator==(other); + } /// @} }; +/** + * @brief Axis-aligned rectangle that can be empty. + * @ingroup Primitives + */ +class OptRect + : public GenericOptRect<Coord> +{ + typedef GenericOptRect<Coord> Base; +public: + OptRect() : Base() {} + OptRect(Rect const &a) : Base(a) {} + OptRect(Point const &a, Point const &b) : Base(a, b) {} + OptRect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {} + OptRect(OptInterval const &x_int, OptInterval const &y_int) : Base(x_int, y_int) {} + OptRect(Base const &b) : Base(b) {} + + OptRect(IntRect const &r) : Base(Rect(r)) {} + OptRect(OptIntRect const &r) : Base() { + if (r) *this = Rect(*r); + } + // actually, the only reason we have this class, instead of typedefing + // to GenericOptRect<Coord>, are the above constructors + bool operator==(OptRect const &other) const { + return Base::operator==(other); + } + bool operator==(Rect const &other) const { + return Base::operator==(other); + } +}; + Coord distanceSq(Point const &p, Rect const &rect); Coord distance(Point const &p, Rect const &rect); |
