| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * \file | ||
| 3 | * \brief Axis-aligned rectangle | ||
| 4 | *//* | ||
| 5 | * Authors: | ||
| 6 | * Michael Sloan <mgsloan@gmail.com> | ||
| 7 | * Krzysztof KosiĆski <tweenk.pl@gmail.com> | ||
| 8 | * Copyright 2007-2011 Authors | ||
| 9 | * | ||
| 10 | * This library is free software; you can redistribute it and/or | ||
| 11 | * modify it either under the terms of the GNU Lesser General Public | ||
| 12 | * License version 2.1 as published by the Free Software Foundation | ||
| 13 | * (the "LGPL") or, at your option, under the terms of the Mozilla | ||
| 14 | * Public License Version 1.1 (the "MPL"). If you do not alter this | ||
| 15 | * notice, a recipient may use your version of this file under either | ||
| 16 | * the MPL or the LGPL. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the LGPL along with this library | ||
| 19 | * in the file COPYING-LGPL-2.1; if not, output to the Free Software | ||
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 21 | * You should have received a copy of the MPL along with this library | ||
| 22 | * in the file COPYING-MPL-1.1 | ||
| 23 | * | ||
| 24 | * The contents of this file are subject to the Mozilla Public License | ||
| 25 | * Version 1.1 (the "License"); you may not use this file except in | ||
| 26 | * compliance with the License. You may obtain a copy of the License at | ||
| 27 | * http://www.mozilla.org/MPL/ | ||
| 28 | * | ||
| 29 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY | ||
| 30 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for | ||
| 31 | * the specific language governing rights and limitations. | ||
| 32 | * | ||
| 33 | * Authors of original rect class: | ||
| 34 | * Lauris Kaplinski <lauris@kaplinski.com> | ||
| 35 | * Nathan Hurst <njh@mail.csse.monash.edu.au> | ||
| 36 | * bulia byak <buliabyak@users.sf.net> | ||
| 37 | * MenTaLguY <mental@rydia.net> | ||
| 38 | */ | ||
| 39 | |||
| 40 | #ifndef LIB2GEOM_SEEN_GENERIC_RECT_H | ||
| 41 | #define LIB2GEOM_SEEN_GENERIC_RECT_H | ||
| 42 | |||
| 43 | #include <limits> | ||
| 44 | #include <iostream> | ||
| 45 | #include <optional> | ||
| 46 | #include <2geom/coord.h> | ||
| 47 | |||
| 48 | namespace Geom { | ||
| 49 | |||
| 50 | template <typename C> | ||
| 51 | class GenericOptRect; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief Axis aligned, non-empty, generic rectangle. | ||
| 55 | * @ingroup Primitives | ||
| 56 | */ | ||
| 57 | template <typename C> | ||
| 58 | class GenericRect | ||
| 59 | : CoordTraits<C>::RectOps | ||
| 60 | { | ||
| 61 | using CInterval = typename CoordTraits<C>::IntervalType; | ||
| 62 | using CPoint = typename CoordTraits<C>::PointType; | ||
| 63 | using CRect = typename CoordTraits<C>::RectType; | ||
| 64 | using OptCRect = typename CoordTraits<C>::OptRectType; | ||
| 65 | protected: | ||
| 66 | CInterval f[2]; | ||
| 67 | public: | ||
| 68 | using D1Value = CInterval; | ||
| 69 | using D1Reference = CInterval &; | ||
| 70 | using D1ConstReference = CInterval const &; | ||
| 71 | |||
| 72 | /// @name Create rectangles. | ||
| 73 | /// @{ | ||
| 74 | /** @brief Create a rectangle that contains only the point at (0, 0). */ | ||
| 75 | 100 | GenericRect() = default; | |
| 76 | /** @brief Create a rectangle from X and Y intervals. */ | ||
| 77 | 32015 | GenericRect(CInterval const &a, CInterval const &b) : f{a, b} {} | |
| 78 | /** @brief Create a rectangle from two points. */ | ||
| 79 | 118594 | GenericRect(CPoint const &a, CPoint const &b) : f{{a[X], b[X]}, {a[Y], b[Y]}} {} | |
| 80 | /** @brief Create rectangle from coordinates of two points. */ | ||
| 81 | ✗ | GenericRect(C x0, C y0, C x1, C y1) : f{{x0, x1}, {y0, y1}} {} | |
| 82 | /** @brief Create a rectangle from a range of points. | ||
| 83 | * The resulting rectangle will contain all points from the range. | ||
| 84 | * The return type of iterators must be convertible to Point. | ||
| 85 | * The range must not be empty. For possibly empty ranges, see OptRect. | ||
| 86 | * @param start Beginning of the range | ||
| 87 | * @param end End of the range | ||
| 88 | * @return Rectangle that contains all points from [start, end). */ | ||
| 89 | template <typename InputIterator> | ||
| 90 | static CRect from_range(InputIterator start, InputIterator end) { | ||
| 91 | assert(start != end); | ||
| 92 | CPoint p1 = *start; | ||
| 93 | auto result = CRect(p1, p1); | ||
| 94 | for (++start; start != end; ++start) { | ||
| 95 | result.expandTo(*start); | ||
| 96 | } | ||
| 97 | return result; | ||
| 98 | } | ||
| 99 | /** @brief Create a rectangle from a C-style array of points it should contain. */ | ||
| 100 | static CRect from_array(CPoint const *c, unsigned n) { | ||
| 101 | return GenericRect<C>::from_range(c, c + n); | ||
| 102 | } | ||
| 103 | /** @brief Create rectangle from origin and dimensions. */ | ||
| 104 | ✗ | static CRect from_xywh(C x, C y, C w, C h) { | |
| 105 | ✗ | return GenericRect<C>::from_xywh(CPoint(x, y), CPoint(w, h)); | |
| 106 | } | ||
| 107 | /** @brief Create rectangle from origin and dimensions. */ | ||
| 108 | ✗ | static CRect from_xywh(CPoint const &xy, CPoint const &wh) { | |
| 109 | ✗ | return CRect(xy, xy + wh); | |
| 110 | } | ||
| 111 | /// Create infinite rectangle. | ||
| 112 | static CRect infinite() { | ||
| 113 | auto min = std::numeric_limits<C>::min(); | ||
| 114 | auto max = std::numeric_limits<C>::max(); | ||
| 115 | return CRect(min, min, max, max); | ||
| 116 | } | ||
| 117 | /// @} | ||
| 118 | |||
| 119 | /// @name Inspect dimensions. | ||
| 120 | /// @{ | ||
| 121 | ✗ | CInterval &operator[](unsigned i) { return f[i]; } | |
| 122 | ✗ | CInterval const &operator[](unsigned i) const { return f[i]; } | |
| 123 | 35526 | CInterval &operator[](Dim2 d) { return f[d]; } | |
| 124 | 23697 | CInterval const &operator[](Dim2 d) const { return f[d]; } | |
| 125 | |||
| 126 | /** @brief Get the corner of the rectangle with smallest coordinate values. | ||
| 127 | * In 2Geom standard coordinate system, this means upper left. */ | ||
| 128 | ✗ | CPoint min() const { return CPoint(f[X].min(), f[Y].min()); } | |
| 129 | /** @brief Get the corner of the rectangle with largest coordinate values. | ||
| 130 | * In 2Geom standard coordinate system, this means lower right. */ | ||
| 131 | ✗ | CPoint max() const { return CPoint(f[X].max(), f[Y].max()); } | |
| 132 | /** @brief Return the n-th corner of the rectangle. | ||
| 133 | * Returns corners in the direction of growing angles, starting from | ||
| 134 | * the one given by min(). For the standard coordinate system used | ||
| 135 | * in 2Geom (+Y downwards), this means clockwise starting from | ||
| 136 | * the upper left. */ | ||
| 137 | 400 | CPoint corner(unsigned i) const { | |
| 138 |
4/4✓ Branch 0 taken 100 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 100 times.
|
400 | switch (i % 4) { |
| 139 | 100 | case 0: return CPoint(f[X].min(), f[Y].min()); | |
| 140 | 100 | case 1: return CPoint(f[X].max(), f[Y].min()); | |
| 141 | 100 | case 2: return CPoint(f[X].max(), f[Y].max()); | |
| 142 | 100 | default: return CPoint(f[X].min(), f[Y].max()); | |
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | /** @brief Return top coordinate of the rectangle (+Y is downwards). */ | ||
| 147 | 28 | C top() const { return f[Y].min(); } | |
| 148 | /** @brief Return bottom coordinate of the rectangle (+Y is downwards). */ | ||
| 149 | 33 | C bottom() const { return f[Y].max(); } | |
| 150 | /** @brief Return leftmost coordinate of the rectangle (+X is to the right). */ | ||
| 151 | 194782 | C left() const { return f[X].min(); } | |
| 152 | /** @brief Return rightmost coordinate of the rectangle (+X is to the right). */ | ||
| 153 | 713241 | C right() const { return f[X].max(); } | |
| 154 | |||
| 155 | /** @brief Get the horizontal extent of the rectangle. */ | ||
| 156 | 7 | C width() const { return f[X].extent(); } | |
| 157 | /** @brief Get the vertical extent of the rectangle. */ | ||
| 158 | 908230 | C height() const { return f[Y].extent(); } | |
| 159 | /** @brief Get the ratio of width to height of the rectangle. */ | ||
| 160 | Coord aspectRatio() const { return static_cast<Coord>(width()) / height(); } | ||
| 161 | |||
| 162 | /** @brief Get rectangle's width and height as a point. | ||
| 163 | * @return Point with X coordinate corresponding to the width and the Y coordinate | ||
| 164 | * corresponding to the height of the rectangle. */ | ||
| 165 | ✗ | CPoint dimensions() const { return CPoint(f[X].extent(), f[Y].extent()); } | |
| 166 | /** @brief Get the point in the geometric center of the rectangle. */ | ||
| 167 | ✗ | CPoint midpoint() const { return CPoint(f[X].middle(), f[Y].middle()); } | |
| 168 | |||
| 169 | /** @brief Compute the rectangle's area. */ | ||
| 170 | ✗ | C area() const { return f[X].extent() * f[Y].extent(); } | |
| 171 | /** @brief Check whether the rectangle has zero area. */ | ||
| 172 | bool hasZeroArea() const { return f[X].isSingular() || f[Y].isSingular(); } | ||
| 173 | |||
| 174 | /** @brief Get the larger extent (width or height) of the rectangle. */ | ||
| 175 | ✗ | C maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); } | |
| 176 | /** @brief Get the smaller extent (width or height) of the rectangle. */ | ||
| 177 | C minExtent() const { return std::min(f[X].extent(), f[Y].extent()); } | ||
| 178 | |||
| 179 | /** @brief Get rectangle's distance SQUARED away from the given point **/ | ||
| 180 | C distanceSq(CPoint const &p) const { | ||
| 181 | return (p - clamp(p)).lengthSq(); | ||
| 182 | } | ||
| 183 | |||
| 184 | /** @brief Clamp point to the rectangle. */ | ||
| 185 | ✗ | CPoint clamp(CPoint const &p) const { | |
| 186 | ✗ | return CPoint(f[X].clamp(p[X]), f[Y].clamp(p[Y])); | |
| 187 | } | ||
| 188 | /** @brief Get the nearest point on the edge of the rectangle. */ | ||
| 189 | CPoint nearestEdgePoint(CPoint const &p) const { | ||
| 190 | if (!contains(p)) { | ||
| 191 | return clamp(p); | ||
| 192 | } | ||
| 193 | CPoint result = p; | ||
| 194 | C cx = f[X].nearestEnd(p[X]); | ||
| 195 | C cy = f[Y].nearestEnd(p[Y]); | ||
| 196 | if (std::abs(cx - p[X]) <= std::abs(cy - p[Y])) { | ||
| 197 | result[X] = cx; | ||
| 198 | } else { | ||
| 199 | result[Y] = cy; | ||
| 200 | } | ||
| 201 | return result; | ||
| 202 | } | ||
| 203 | /// @} | ||
| 204 | |||
| 205 | /// @name Test other rectangles and points for inclusion. | ||
| 206 | /// @{ | ||
| 207 | /** @brief Check whether the rectangles have any common points. */ | ||
| 208 | 30209 | bool intersects(GenericRect<C> const &r) const { | |
| 209 |
4/4✓ Branch 2 taken 30153 times.
✓ Branch 3 taken 56 times.
✓ Branch 6 taken 30132 times.
✓ Branch 7 taken 21 times.
|
30209 | return f[X].intersects(r[X]) && f[Y].intersects(r[Y]); |
| 210 | } | ||
| 211 | /** @brief Check whether the rectangle includes all points in the given rectangle. */ | ||
| 212 | ✗ | bool contains(GenericRect<C> const &r) const { | |
| 213 | ✗ | return f[X].contains(r[X]) && f[Y].contains(r[Y]); | |
| 214 | } | ||
| 215 | |||
| 216 | /** @brief Check whether the rectangles have any common points. | ||
| 217 | * Empty rectangles will not intersect with any other rectangle. */ | ||
| 218 | inline bool intersects(OptCRect const &r) const; | ||
| 219 | /** @brief Check whether the rectangle includes all points in the given rectangle. | ||
| 220 | * Empty rectangles will be contained in any non-empty rectangle. */ | ||
| 221 | inline bool contains(OptCRect const &r) const; | ||
| 222 | |||
| 223 | /** @brief Check whether the given point is within the rectangle. */ | ||
| 224 | 285048 | bool contains(CPoint const &p) const { | |
| 225 |
4/4✓ Branch 2 taken 176915 times.
✓ Branch 3 taken 108133 times.
✓ Branch 6 taken 145270 times.
✓ Branch 7 taken 31645 times.
|
285048 | return f[X].contains(p[X]) && f[Y].contains(p[Y]); |
| 226 | } | ||
| 227 | /// @} | ||
| 228 | |||
| 229 | /// @name Modify the rectangle. | ||
| 230 | /// @{ | ||
| 231 | /** @brief Set the minimum X coordinate of the rectangle. */ | ||
| 232 | void setLeft(C val) { | ||
| 233 | f[X].setMin(val); | ||
| 234 | } | ||
| 235 | /** @brief Set the maximum X coordinate of the rectangle. */ | ||
| 236 | void setRight(C val) { | ||
| 237 | f[X].setMax(val); | ||
| 238 | } | ||
| 239 | /** @brief Set the minimum Y coordinate of the rectangle. */ | ||
| 240 | void setTop(C val) { | ||
| 241 | f[Y].setMin(val); | ||
| 242 | } | ||
| 243 | /** @brief Set the maximum Y coordinate of the rectangle. */ | ||
| 244 | void setBottom(C val) { | ||
| 245 | f[Y].setMax(val); | ||
| 246 | } | ||
| 247 | /** @brief Set the upper left point of the rectangle. */ | ||
| 248 | void setMin(CPoint const &p) { | ||
| 249 | f[X].setMin(p[X]); | ||
| 250 | f[Y].setMin(p[Y]); | ||
| 251 | } | ||
| 252 | /** @brief Set the lower right point of the rectangle. */ | ||
| 253 | void setMax(CPoint const &p) { | ||
| 254 | f[X].setMax(p[X]); | ||
| 255 | f[Y].setMax(p[Y]); | ||
| 256 | } | ||
| 257 | /** @brief Enlarge the rectangle to contain the given point. */ | ||
| 258 | 2452 | void expandTo(CPoint const &p) { | |
| 259 | 2452 | f[X].expandTo(p[X]); | |
| 260 | 2452 | f[Y].expandTo(p[Y]); | |
| 261 | 2452 | } | |
| 262 | /** @brief Enlarge the rectangle to contain the argument. */ | ||
| 263 | 11745 | void unionWith(CRect const &b) { | |
| 264 | 11745 | f[X].unionWith(b[X]); | |
| 265 | 11745 | f[Y].unionWith(b[Y]); | |
| 266 | 11745 | } | |
| 267 | /** @brief Enlarge the rectangle to contain the argument. | ||
| 268 | * Unioning with an empty rectangle results in no changes. */ | ||
| 269 | void unionWith(OptCRect const &b); | ||
| 270 | |||
| 271 | /** @brief Expand the rectangle in both directions by the specified amount. | ||
| 272 | * Note that this is different from scaling. Negative values will shrink the | ||
| 273 | * rectangle. If <code>-amount</code> is larger than | ||
| 274 | * half of the width, the X interval will contain only the X coordinate | ||
| 275 | * of the midpoint; same for height. */ | ||
| 276 | ✗ | void expandBy(C amount) { | |
| 277 | ✗ | expandBy(amount, amount); | |
| 278 | ✗ | } | |
| 279 | /** @brief Expand the rectangle in both directions. | ||
| 280 | * Note that this is different from scaling. Negative values will shrink the | ||
| 281 | * rectangle. If <code>-x</code> is larger than | ||
| 282 | * half of the width, the X interval will contain only the X coordinate | ||
| 283 | * of the midpoint; same for height. */ | ||
| 284 | ✗ | void expandBy(C x, C y) { | |
| 285 | ✗ | f[X].expandBy(x); | |
| 286 | ✗ | f[Y].expandBy(y); | |
| 287 | ✗ | } | |
| 288 | /** @brief Expand the rectangle by the coordinates of the given point. | ||
| 289 | * This will expand the width by the X coordinate of the point in both directions | ||
| 290 | * and the height by Y coordinate of the point. Negative coordinate values will | ||
| 291 | * shrink the rectangle. If <code>-p[X]</code> is larger than half of the width, | ||
| 292 | * the X interval will contain only the X coordinate of the midpoint; | ||
| 293 | * same for height. */ | ||
| 294 | ✗ | void expandBy(CPoint const &p) { | |
| 295 | ✗ | expandBy(p[X], p[Y]); | |
| 296 | ✗ | } | |
| 297 | /// @} | ||
| 298 | |||
| 299 | /// @name Operators | ||
| 300 | /// @{ | ||
| 301 | /** @brief Offset the rectangle by a vector. */ | ||
| 302 | GenericRect<C> &operator+=(CPoint const &p) { | ||
| 303 | f[X] += p[X]; | ||
| 304 | f[Y] += p[Y]; | ||
| 305 | return *this; | ||
| 306 | } | ||
| 307 | /** @brief Offset the rectangle by the negation of a vector. */ | ||
| 308 | GenericRect<C> &operator-=(CPoint const &p) { | ||
| 309 | f[X] -= p[X]; | ||
| 310 | f[Y] -= p[Y]; | ||
| 311 | return *this; | ||
| 312 | } | ||
| 313 | /** @brief Union two rectangles. */ | ||
| 314 | GenericRect<C> &operator|=(CRect const &o) { | ||
| 315 | unionWith(o); | ||
| 316 | return *this; | ||
| 317 | } | ||
| 318 | ✗ | GenericRect<C> &operator|=(OptCRect const &o) { | |
| 319 | ✗ | unionWith(o); | |
| 320 | ✗ | return *this; | |
| 321 | } | ||
| 322 | /** @brief Test for equality of rectangles. */ | ||
| 323 | bool operator==(CRect const &o) const { return f[X] == o[X] && f[Y] == o[Y]; } | ||
| 324 | /// @} | ||
| 325 | }; | ||
| 326 | |||
| 327 | /** | ||
| 328 | * @brief Axis-aligned generic rectangle that can be empty. | ||
| 329 | * @ingroup Primitives | ||
| 330 | */ | ||
| 331 | template <typename C> | ||
| 332 | class GenericOptRect | ||
| 333 | : public std::optional<typename CoordTraits<C>::RectType> | ||
| 334 | , boost::equality_comparable< typename CoordTraits<C>::OptRectType | ||
| 335 | , boost::equality_comparable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType | ||
| 336 | , boost::orable< typename CoordTraits<C>::OptRectType | ||
| 337 | , boost::andable< typename CoordTraits<C>::OptRectType | ||
| 338 | , boost::andable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType | ||
| 339 | >>>>> | ||
| 340 | { | ||
| 341 | using CInterval = typename CoordTraits<C>::IntervalType; | ||
| 342 | using OptCInterval = typename CoordTraits<C>::OptIntervalType; | ||
| 343 | using CPoint = typename CoordTraits<C>::PointType; | ||
| 344 | using CRect = typename CoordTraits<C>::RectType; | ||
| 345 | using OptCRect = typename CoordTraits<C>::OptRectType; | ||
| 346 | using Base = std::optional<CRect>; | ||
| 347 | public: | ||
| 348 | using D1Value = CInterval; | ||
| 349 | using D1Reference = CInterval &; | ||
| 350 | using D1ConstReference = CInterval const &; | ||
| 351 | |||
| 352 | /// @name Create potentially empty rectangles. | ||
| 353 | /// @{ | ||
| 354 | 591150 | GenericOptRect() = default; | |
| 355 | 32006 | GenericOptRect(GenericRect<C> const &a) : Base(CRect(a)) {} | |
| 356 | GenericOptRect(CPoint const &a, CPoint const &b) : Base(CRect(a, b)) {} | ||
| 357 | GenericOptRect(C x0, C y0, C x1, C y1) : Base(CRect(x0, y0, x1, y1)) {} | ||
| 358 | /// Creates an empty OptRect when one of the argument intervals is empty. | ||
| 359 | 32000 | GenericOptRect(OptCInterval const &x_int, OptCInterval const &y_int) { | |
| 360 |
3/6✓ Branch 1 taken 32000 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 32000 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 32000 times.
✗ Branch 7 not taken.
|
32000 | if (x_int && y_int) { |
| 361 |
1/2✓ Branch 5 taken 32000 times.
✗ Branch 6 not taken.
|
32000 | *this = CRect(*x_int, *y_int); |
| 362 | } | ||
| 363 | // else, stay empty. | ||
| 364 | 32000 | } | |
| 365 | |||
| 366 | /** @brief Create a rectangle from a range of points. | ||
| 367 | * The resulting rectangle will contain all points from the range. | ||
| 368 | * If the range contains no points, the result will be an empty rectangle. | ||
| 369 | * The return type of iterators must be convertible to the corresponding | ||
| 370 | * point type (Point or IntPoint). | ||
| 371 | * @param start Beginning of the range | ||
| 372 | * @param end End of the range | ||
| 373 | * @return Rectangle that contains all points from [start, end). */ | ||
| 374 | template <typename InputIterator> | ||
| 375 | static OptCRect from_range(InputIterator start, InputIterator end) { | ||
| 376 | OptCRect result; | ||
| 377 | for (; start != end; ++start) { | ||
| 378 | result.expandTo(*start); | ||
| 379 | } | ||
| 380 | return result; | ||
| 381 | } | ||
| 382 | /// @} | ||
| 383 | |||
| 384 | /// @name Check other rectangles and points for inclusion. | ||
| 385 | /// @{ | ||
| 386 | /** @brief Check for emptiness. */ | ||
| 387 | inline bool empty() const { return !*this; }; | ||
| 388 | /** @brief Check whether the rectangles have any common points. | ||
| 389 | * Empty rectangles will not intersect with any other rectangle. */ | ||
| 390 | bool intersects(CRect const &r) const { return r.intersects(*this); } | ||
| 391 | /** @brief Check whether the rectangle includes all points in the given rectangle. | ||
| 392 | * Empty rectangles will be contained in any non-empty rectangle. */ | ||
| 393 | bool contains(CRect const &r) const { return *this && (*this)->contains(r); } | ||
| 394 | |||
| 395 | /** @brief Check whether the rectangles have any common points. | ||
| 396 | * Empty rectangles will not intersect with any other rectangle. | ||
| 397 | * Two empty rectangles will not intersect each other. */ | ||
| 398 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
|
7 | bool intersects(OptCRect const &r) const { return *this && (*this)->intersects(r); } |
| 399 | /** @brief Check whether the rectangle includes all points in the given rectangle. | ||
| 400 | * Empty rectangles will be contained in any non-empty rectangle. | ||
| 401 | * An empty rectangle will not contain other empty rectangles. */ | ||
| 402 | bool contains(OptCRect const &r) const { return *this && (*this)->contains(r); } | ||
| 403 | |||
| 404 | /** @brief Check whether the given point is within the rectangle. | ||
| 405 | * An empty rectangle will not contain any points. */ | ||
| 406 |
3/4✓ Branch 1 taken 285048 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 145270 times.
✓ Branch 6 taken 139778 times.
|
285048 | bool contains(CPoint const &p) const { return *this && (*this)->contains(p); } |
| 407 | /// @} | ||
| 408 | |||
| 409 | /** @brief Compute the rectangle's area. */ | ||
| 410 | C area() const { return *this ? (*this)->area() : 0; } | ||
| 411 | /** @brief Check whether the rectangle has zero area. */ | ||
| 412 | bool hasZeroArea() const { return !*this || (*this)->hasZeroArea(); } | ||
| 413 | /** @brief Returns an empty optional (testing false) if the rectangle has zero area. */ | ||
| 414 | OptCRect regularized() const { return hasZeroArea() ? OptCRect() : *this; } | ||
| 415 | |||
| 416 | /// @name Modify the potentially empty rectangle. | ||
| 417 | /// @{ | ||
| 418 | /** @brief Enlarge the rectangle to contain the argument. | ||
| 419 | * If this rectangle is empty, after callng this method it will | ||
| 420 | * be equal to the argument. */ | ||
| 421 | 11745 | void unionWith(CRect const &b) { | |
| 422 |
1/2✓ Branch 1 taken 11745 times.
✗ Branch 2 not taken.
|
11745 | if (*this) { |
| 423 | 11745 | (*this)->unionWith(b); | |
| 424 | } else { | ||
| 425 | ✗ | *this = b; | |
| 426 | } | ||
| 427 | 11745 | } | |
| 428 | /** @brief Enlarge the rectangle to contain the argument. | ||
| 429 | * Unioning with an empty rectangle results in no changes. | ||
| 430 | * If this rectangle is empty, after calling this method it will | ||
| 431 | * be equal to the argument. */ | ||
| 432 | 11702 | void unionWith(OptCRect const &b) { | |
| 433 |
1/2✓ Branch 1 taken 11702 times.
✗ Branch 2 not taken.
|
11702 | if (b) unionWith(*b); |
| 434 | 11702 | } | |
| 435 | /** @brief Leave only the area overlapping with the argument. | ||
| 436 | * If the rectangles do not have any points in common, after calling | ||
| 437 | * this method the rectangle will be empty. */ | ||
| 438 | void intersectWith(CRect const &b) { | ||
| 439 | if (!*this) return; | ||
| 440 | OptCInterval x = (**this)[X] & b[X], y = (**this)[Y] & b[Y]; | ||
| 441 | if (x && y) { | ||
| 442 | *this = CRect(*x, *y); | ||
| 443 | } else { | ||
| 444 | *this = {}; | ||
| 445 | } | ||
| 446 | } | ||
| 447 | /** @brief Leave only the area overlapping with the argument. | ||
| 448 | * If the argument is empty or the rectangles do not have any points | ||
| 449 | * in common, after calling this method the rectangle will be empty. */ | ||
| 450 | void intersectWith(OptCRect const &b) { | ||
| 451 | if (b) { | ||
| 452 | intersectWith(*b); | ||
| 453 | } else { | ||
| 454 | *this = {}; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | /** @brief Create or enlarge the rectangle to contain the given point. | ||
| 458 | * If the rectangle is empty, after calling this method it will be non-empty | ||
| 459 | * and it will contain only the given point. */ | ||
| 460 | void expandTo(CPoint const &p) { | ||
| 461 | if (*this) { | ||
| 462 | (*this)->expandTo(p); | ||
| 463 | } else { | ||
| 464 | *this = CRect(p, p); | ||
| 465 | } | ||
| 466 | } | ||
| 467 | /// @} | ||
| 468 | |||
| 469 | /// @name Operators | ||
| 470 | /// @{ | ||
| 471 | /** @brief Union with @a b */ | ||
| 472 | 2 | GenericOptRect<C> &operator|=(OptCRect const &b) { | |
| 473 | 2 | unionWith(b); | |
| 474 | 2 | return *this; | |
| 475 | } | ||
| 476 | /** @brief Intersect with @a b */ | ||
| 477 | GenericOptRect<C> &operator&=(CRect const &b) { | ||
| 478 | intersectWith(b); | ||
| 479 | return *this; | ||
| 480 | } | ||
| 481 | /** @brief Intersect with @a b */ | ||
| 482 | GenericOptRect<C> &operator&=(OptCRect const &b) { | ||
| 483 | intersectWith(b); | ||
| 484 | return *this; | ||
| 485 | } | ||
| 486 | /** @brief Test for equality. | ||
| 487 | * All empty rectangles are equal. */ | ||
| 488 | bool operator==(OptCRect const &other) const { | ||
| 489 | if (!*this != !other) return false; | ||
| 490 | return *this ? **this == *other : true; | ||
| 491 | } | ||
| 492 | bool operator==(CRect const &other) const { | ||
| 493 | if (!*this) return false; | ||
| 494 | return **this == other; | ||
| 495 | } | ||
| 496 | /// @} | ||
| 497 | }; | ||
| 498 | |||
| 499 | template <typename C> | ||
| 500 | ✗ | inline void GenericRect<C>::unionWith(OptCRect const &b) { | |
| 501 | ✗ | if (b) { | |
| 502 | ✗ | unionWith(*b); | |
| 503 | } | ||
| 504 | ✗ | } | |
| 505 | template <typename C> | ||
| 506 | 7 | inline bool GenericRect<C>::intersects(OptCRect const &r) const { | |
| 507 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
|
7 | return r && intersects(*r); |
| 508 | } | ||
| 509 | template <typename C> | ||
| 510 | inline bool GenericRect<C>::contains(OptCRect const &r) const { | ||
| 511 | return !r || contains(*r); | ||
| 512 | } | ||
| 513 | |||
| 514 | template <typename C> | ||
| 515 | inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) { | ||
| 516 | return out << "Rect " << r[X] << " x " << r[Y]; | ||
| 517 | } | ||
| 518 | |||
| 519 | template <typename C> | ||
| 520 | inline std::ostream &operator<<(std::ostream &out, GenericOptRect<C> const &r) { | ||
| 521 | return r ? (out << *r) : (out << "Rect (empty)"); | ||
| 522 | } | ||
| 523 | |||
| 524 | } // namespace Geom | ||
| 525 | |||
| 526 | #endif // LIB2GEOM_SEEN_GENERIC_RECT_H | ||
| 527 | |||
| 528 | /* | ||
| 529 | Local Variables: | ||
| 530 | mode:c++ | ||
| 531 | c-file-style:"stroustrup" | ||
| 532 | c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) | ||
| 533 | indent-tabs-mode:nil | ||
| 534 | fill-column:99 | ||
| 535 | End: | ||
| 536 | */ | ||
| 537 | // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : | ||
| 538 |