summaryrefslogtreecommitdiffstats
path: root/src/2geom/rect.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/2geom/rect.h')
-rw-r--r--src/2geom/rect.h315
1 files changed, 70 insertions, 245 deletions
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
index 65bb1bb76..e9f6cbeb7 100644
--- a/src/2geom/rect.h
+++ b/src/2geom/rect.h
@@ -2,7 +2,10 @@
* \file
* \brief Axis-aligned rectangle
*//*
- * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ * Authors:
+ * Michael Sloan <mgsloan@gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ * Copyright 2007-2011 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -34,48 +37,42 @@
* MenTaLguY <mental@rydia.net>
*/
-#include <2geom/d2.h>
-
-#ifndef LIB2GEOM_RECT_H
-#define LIB2GEOM_RECT_H
+#ifndef LIB2GEOM_SEEN_RECT_H
+#define LIB2GEOM_SEEN_RECT_H
+#include <boost/optional.hpp>
#include <2geom/affine.h>
-#include <boost/optional/optional.hpp>
+#include <2geom/interval.h>
+#include <2geom/int-rect.h>
namespace Geom {
/**
- * @brief Axis-aligned, non-empty rectangle - convenience typedef
+ * @brief Axis-aligned rectangle that can be empty.
* @ingroup Primitives
*/
-typedef D2<Interval> Rect;
-class OptRect;
-
-inline Rect unify(Rect const &, Rect const &);
+typedef GenericOptRect<Coord> OptRect;
/**
* @brief Axis aligned, non-empty rectangle.
* @ingroup Primitives
*/
-template<>
-class D2<Interval> {
-private:
- Interval f[2];
+class Rect
+ : public GenericRect<Coord>
+ , boost::multipliable< Rect, Affine >
+{
+ typedef GenericRect<Coord> Base;
public:
/// @name Create rectangles.
/// @{
/** @brief Create a rectangle that contains only the point at (0,0). */
- D2<Interval>() { f[X] = f[Y] = Interval(); }
+ Rect() {}
/** @brief Create a rectangle from X and Y intervals. */
- D2<Interval>(Interval const &a, Interval const &b) {
- f[X] = a;
- f[Y] = b;
- }
+ Rect(Interval const &a, Interval const &b) : Base(a,b) {}
/** @brief Create a rectangle from two points. */
- D2<Interval>(Point const & a, Point const & b) {
- f[X] = Interval(a[X], b[X]);
- f[Y] = Interval(a[Y], b[Y]);
- }
+ 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.
@@ -85,12 +82,7 @@ public:
* @return Rectangle that contains all points from [start, end). */
template <typename InputIterator>
static Rect from_range(InputIterator start, InputIterator end) {
- assert(start != end);
- Point p1 = *start++;
- Rect result(p1, p1);
- for (; start != end; ++start) {
- result.expandTo(*start);
- }
+ Rect result = Base::from_range(start, end);
return result;
}
/** @brief Create a rectangle from a C-style array of points it should contain. */
@@ -98,264 +90,97 @@ public:
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;
+ }
/// @}
/// @name Inspect dimensions.
/// @{
- Interval& operator[](unsigned i) { return f[i]; }
- Interval const & operator[](unsigned i) const { return f[i]; }
-
- Point min() const { return Point(f[X].min(), f[Y].min()); }
- Point max() const { return Point(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. */
- Point corner(unsigned i) const {
- switch(i % 4) {
- case 0: return Point(f[X].min(), f[Y].min());
- case 1: return Point(f[X].max(), f[Y].min());
- case 2: return Point(f[X].max(), f[Y].max());
- default: return Point(f[X].min(), f[Y].max());
- }
- }
-
- //We should probably remove these - they're coord sys gnostic
- /** @brief Return top coordinate of the rectangle (+Y is downwards). */
- Coord top() const { return f[Y].min(); }
- /** @brief Return bottom coordinate of the rectangle (+Y is downwards). */
- Coord bottom() const { return f[Y].max(); }
- /** @brief Return leftmost coordinate of the rectangle (+X is to the right). */
- Coord left() const { return f[X].min(); }
- /** @brief Return rightmost coordinate of the rectangle (+X is to the right). */
- Coord right() const { return f[X].max(); }
-
- Coord width() const { return f[X].extent(); }
- Coord height() const { return f[Y].extent(); }
-
- /** @brief Get rectangle's width and height as a point.
- * @return Point with X coordinate corresponding to the width and the Y coordinate
- * corresponding to the height of the rectangle. */
- Point dimensions() const { return Point(f[X].extent(), f[Y].extent()); }
- Point midpoint() const { return Point(f[X].middle(), f[Y].middle()); }
-
-/**
- * \brief Compute the area of this rectangle.
- *
- * Note that a zero area rectangle is not empty - just as the interval [0,0] contains one point, the rectangle [0,0] x [0,0] contains 1 point and no area.
- * \retval For a valid return value, the rect must be tested for emptyness first.
- */
- /** @brief Compute rectangle's area. */
- Coord area() const { return f[X].extent() * f[Y].extent(); }
/** @brief Check whether the rectangle has zero area up to specified tolerance.
* @param eps Maximum value of the area to consider empty
* @return True if rectangle has an area smaller than tolerance, false otherwise */
- bool hasZeroArea(double eps = EPSILON) const { return (area() <= eps); }
-
- /** @brief Get the larger extent (width or height) of the rectangle. */
- Coord maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
- /** @brief Get the smaller extent (width or height) of the rectangle. */
- Coord minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+ bool hasZeroArea(Coord eps = EPSILON) const { return (area() <= eps); }
/// @}
/// @name Test other rectangles and points for inclusion.
/// @{
- /** @brief Check whether the rectangles have any common points. */
- bool intersects(Rect const &r) const {
- return f[X].intersects(r[X]) && f[Y].intersects(r[Y]);
- }
/** @brief Check whether the interiors of the rectangles have any common points. */
bool interiorIntersects(Rect const &r) const {
return f[X].interiorIntersects(r[X]) && f[Y].interiorIntersects(r[Y]);
}
- /** @brief Check whether the rectangle includes all points in the given rectangle. */
- bool contains(Rect const &r) const {
- return f[X].contains(r[X]) && f[Y].contains(r[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 {
return f[X].interiorContains(r[X]) && f[Y].interiorContains(r[Y]);
}
-
- /** @brief Check whether the rectangles have any common points.
- * A non-empty rectangle will not intersect empty rectangles. */
- inline bool intersects(OptRect const &r) const;
- /** @brief Check whether the rectangle includes all points in the given rectangle.
- * A non-empty rectangle will contain any empty rectangle. */
- inline bool contains(OptRect const &r) const;
- /** @brief Check whether the interior includes all points in the given rectangle.
- * The interior of a non-empty rectangle will contain any empty rectangle. */
inline bool interiorContains(OptRect const &r) const;
+ /// @}
- /** @brief Check whether the given point is within the rectangle. */
- bool contains(Point const &p) const {
- return f[X].contains(p[X]) && f[Y].contains(p[Y]);
+ /// @name Rounding to integer coordinates
+ /// @{
+ /** @brief Return the smallest integer rectangle which contains this one. */
+ IntRect roundOutwards() const {
+ IntRect ir(f[X].roundOutwards(), f[Y].roundOutwards());
+ return ir;
}
- /** @brief Check whether the given point is in the rectangle's interior.
- * This means the point must lie within the rectangle but not on its border. */
- bool interiorContains(Point const &p) const {
- return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]);
+ /** @brief Return the largest integer rectangle which is contained in this one. */
+ OptIntRect roundInwards() const {
+ OptIntRect oir(f[X].roundInwards(), f[Y].roundInwards());
+ return oir;
}
/// @}
- /// @name Modify the rectangle.
+ /// @name Operators
/// @{
- /** @brief Enlarge the rectangle to contain the given point. */
- void expandTo(Point p) {
- f[X].expandTo(p[X]); f[Y].expandTo(p[Y]);
- }
- /** @brief Enlarge the rectangle to contain the given rectangle. */
- void unionWith(Rect const &b) {
- f[X].unionWith(b[X]); f[Y].unionWith(b[Y]);
- }
- /** @brief Enlarge the rectangle to contain the given rectangle.
- * Unioning with an empty rectangle results in no changes. */
- void unionWith(OptRect const &b);
-
- //TODO: figure out how these work with negative values and OptRect
- /** @brief Expand the rectangle in both directions by the specified amount.
- * Note that this is different from scaling. Negative values wil shrink the
- * rectangle. If <code>-amount</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(Coord amount) {
- f[X].expandBy(amount); f[Y].expandBy(amount);
- }
- /** @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
- * and the height by Y coordinate of the point. Negative coordinate values will
- * 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(Point const p) {
- f[X].expandBy(p[X]); f[Y].expandBy(p[Y]);
- }
+ Rect &operator*=(Affine const &m);
/// @}
};
-inline Rect unify(Rect const & a, Rect const & b) {
- return Rect(unify(a[X], b[X]), unify(a[Y], b[Y]));
+Coord distanceSq(Point const &p, Rect const &rect);
+Coord distance(Point const &p, Rect const &rect);
+
+inline bool Rect::interiorContains(OptRect const &r) const {
+ return !r || interiorContains(static_cast<Rect const &>(*r));
}
-inline Rect union_list(std::vector<Rect> const &r) {
- if(r.empty()) return Rect(Interval(0,0), Interval(0,0));
- Rect ret = r[0];
- for(unsigned i = 1; i < r.size(); i++)
- ret.unionWith(r[i]);
+// the functions below do not work when defined generically
+inline OptRect operator&(Rect const &a, Rect const &b) {
+ OptRect ret(a);
+ ret.intersectWith(b);
return ret;
}
-
-inline
-Coord distanceSq( Point const& p, Rect const& rect )
-{
- double dx = 0, dy = 0;
- if ( p[X] < rect.left() )
- {
- dx = p[X] - rect.left();
- }
- else if ( p[X] > rect.right() )
- {
- dx = rect.right() - p[X];
- }
- if ( p[Y] < rect.top() )
- {
- dy = rect.top() - p[Y];
- }
- else if ( p[Y] > rect.bottom() )
- {
- dy = p[Y] - rect.bottom();
- }
- return dx*dx + dy*dy;
+inline OptRect intersect(Rect const &a, Rect const &b) {
+ return a & b;
}
-
-/**
- * Returns the smallest distance between p and rect.
- */
-inline
-Coord distance( Point const& p, Rect const& rect )
-{
- return std::sqrt(distanceSq(p, rect));
+inline OptRect intersect(OptRect const &a, OptRect const &b) {
+ return a & b;
}
-
-/**
- * @brief Axis-aligned rectangle that can be empty.
- * @ingroup Primitives
- */
-class OptRect : public boost::optional<Rect> {
-public:
- OptRect() : boost::optional<Rect>() {};
- OptRect(Rect const &a) : boost::optional<Rect>(a) {};
-
- /**
- * Creates an empty OptRect when one of the argument intervals is empty.
- */
- OptRect(OptInterval const &x_int, OptInterval const &y_int) {
- if (x_int && y_int) {
- *this = Rect(*x_int, *y_int);
- }
- // else, stay empty.
- }
-
- /** @brief Check for emptiness. */
- inline bool isEmpty() const { return (*this == false); };
-
- bool intersects(Rect const &r) const { return r.intersects(*this); }
- bool contains(Rect const &r) const { return *this && (*this)->contains(r); }
- bool interiorContains(Rect const &r) const { return *this && (*this)->interiorContains(r); }
-
- bool intersects(OptRect const &r) const { return *this && (*this)->intersects(r); }
- bool contains(OptRect const &r) const { return *this && (*this)->contains(r); }
- bool interiorContains(OptRect const &r) const { return *this && (*this)->interiorContains(r); }
-
- bool contains(Point const &p) const { return *this && (*this)->contains(p); }
- bool interiorContains(Point const &p) const { return *this && (*this)->contains(p); }
-
- inline void unionWith(OptRect const &b) {
- if (*this) { // check that we are not empty
- (*this)->unionWith(b);
- } else {
- *this = b;
- }
- }
-};
-
-
-/**
- * Returns the smallest rectangle that encloses both rectangles.
- * An empty argument is assumed to be an empty rectangle
- */
-inline OptRect unify(OptRect const & a, OptRect const & b) {
- if (!a) {
- return b;
- } else if (!b) {
- return a;
- } else {
- return unify(*a, *b);
- }
+inline Rect unify(Rect const &a, Rect const &b) {
+ return a | b;
}
-
-inline OptRect intersect(Rect const & a, Rect const & b) {
- return OptRect(intersect(a[X], b[X]), intersect(a[Y], b[Y]));
+inline OptRect unify(OptRect const &a, OptRect const &b) {
+ return a | b;
}
-inline void Rect::unionWith(OptRect const &b) {
- if (b) {
- unionWith(*b);
- }
-}
-inline bool Rect::intersects(OptRect const &r) const {
- return r && intersects(*r);
-}
-inline bool Rect::contains(OptRect const &r) const {
- return !r || contains(*r);
-}
-inline bool Rect::interiorContains(OptRect const &r) const {
- return !r || interiorContains(*r);
+/** @brief Union a list of rectangles
+ * @deprecated Use OptRect::from_range instead */
+inline Rect union_list(std::vector<Rect> const &r) {
+ if(r.empty()) return Rect(Interval(0,0), Interval(0,0));
+ Rect ret = r[0];
+ for(unsigned i = 1; i < r.size(); i++)
+ ret.unionWith(r[i]);
+ return ret;
}
} // end namespace Geom
-#endif //_2GEOM_RECT
+#endif // LIB2GEOM_SEEN_RECT_H
/*
Local Variables: