GCC Code Coverage Report


Directory: ./
File: include/2geom/generic-rect.h
Date: 2024-03-18 17:01:34
Exec Total Coverage
Lines: 49 84 58.3%
Functions: 25 43 58.1%
Branches: 25 50 50.0%

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