LSSTApplications  17.0+11,17.0+113,17.0+64,18.0.0+13,18.0.0+28,18.0.0+5,18.0.0+66,18.0.0-4-g68ffd23,18.1.0-1-g0001055+8,18.1.0-1-g03d53ef+1,18.1.0-1-g1349e88+42,18.1.0-1-g2505f39+33,18.1.0-1-g5315e5e+1,18.1.0-1-g5e4b7ea+10,18.1.0-1-g7e8fceb+1,18.1.0-1-g85f8cd4+35,18.1.0-1-gd55f500+24,18.1.0-12-g42eabe8e+26,18.1.0-14-g259bd21+5,18.1.0-14-gd04256d+31,18.1.0-2-g4903023+9,18.1.0-2-g5f9922c+11,18.1.0-2-gd3b74e5+2,18.1.0-2-gfbf3545+19,18.1.0-2-gfefb8b5+30,18.1.0-20-g4b62d031a,18.1.0-21-gb3d55290+13,18.1.0-22-gcd16eb0+1,18.1.0-3-g52aa583+16,18.1.0-3-g8f4a2b1+29,18.1.0-3-gb69f684+26,18.1.0-4-g1ee41a7+1,18.1.0-5-g6dbcb01+27,18.1.0-5-gc286bb7+3,18.1.0-6-g857e778+2,18.1.0-7-gae09a6d+14,18.1.0-8-g42b2ab3+8,18.1.0-8-gc69d46e+13,18.1.0-9-gee19f03,w.2019.42
LSSTDataManagementBasePackage
Box.cc
Go to the documentation of this file.
1 /*
2  * Developed for the LSST Data Management System.
3  * This product includes software developed by the LSST Project
4  * (https://www.lsst.org).
5  * See the COPYRIGHT file at the top-level directory of this distribution
6  * for details of code ownership.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <cmath>
23 #include <limits>
24 
25 #include "lsst/utils/hashCombine.h"
26 #include "lsst/geom/Box.h"
27 
28 namespace lsst {
29 namespace geom {
30 
31 Box2I::Box2I(Point2I const& minimum, Point2I const& maximum, bool invert)
32  : _minimum(minimum), _dimensions(maximum - minimum) {
33  for (int n = 0; n < 2; ++n) {
34  if (_dimensions[n] < 0) {
35  if (invert) {
36  _minimum[n] += _dimensions[n];
37  _dimensions[n] = -_dimensions[n];
38  } else {
39  *this = Box2I();
40  return;
41  }
42  }
43  }
44  _dimensions += Extent2I(1);
45 }
46 
47 Box2I::Box2I(Point2I const& corner, Extent2I const& dimensions, bool invert)
48  : _minimum(corner), _dimensions(dimensions) {
49  for (int n = 0; n < 2; ++n) {
50  if (_dimensions[n] == 0) {
51  *this = Box2I();
52  return;
53  } else if (_dimensions[n] < 0) {
54  if (invert) {
55  _minimum[n] += (_dimensions[n] + 1);
56  _dimensions[n] = -_dimensions[n];
57  } else {
58  *this = Box2I();
59  return;
60  }
61  }
62  }
63  if (!isEmpty() && any(getMin().gt(getMax()))) {
65  "Box dimensions too large; integer overflow detected.");
66  }
67 }
68 
69 Box2I::Box2I(Box2D const& other, EdgeHandlingEnum edgeHandling) : _minimum(), _dimensions() {
70  if (other.isEmpty()) {
71  *this = Box2I();
72  return;
73  }
74  if (!std::isfinite(other.getMinX()) || !std::isfinite(other.getMinY()) ||
75  !std::isfinite(other.getMaxX()) || !std::isfinite(other.getMaxY())) {
76  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Cannot convert non-finite Box2D to Box2I");
77  }
78  Point2D fpMin(other.getMin() + Extent2D(0.5));
79  Point2D fpMax(other.getMax() - Extent2D(0.5));
80  switch (edgeHandling) {
81  case EXPAND:
82  for (int n = 0; n < 2; ++n) {
83  _minimum[n] = static_cast<int>(std::floor(fpMin[n]));
84  _dimensions[n] = static_cast<int>(std::ceil(fpMax[n])) + 1 - _minimum[n];
85  }
86  break;
87  case SHRINK:
88  for (int n = 0; n < 2; ++n) {
89  _minimum[n] = static_cast<int>(std::ceil(fpMin[n]));
90  _dimensions[n] = static_cast<int>(std::floor(fpMax[n])) + 1 - _minimum[n];
91  }
92  break;
93  }
94 }
95 
96 Point2D const Box2I::getCenter() const noexcept {
97  return Box2D(*this).getCenter();
98 }
99 
100 Box2I Box2I::makeCenteredBox(Point2D const& center, Box2I::Extent const& size) {
101  if (!std::isfinite(center[0]) || !std::isfinite(center[1])) {
102  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Cannot make Box2I with non-finite center");
103  }
104 
105  lsst::geom::Point2D corner(center);
106  corner.shift(-0.5 * lsst::geom::Extent2D(size));
107  // compensate for Box2I's coordinate conventions (where max = min + size - 1)
108  corner.shift(lsst::geom::Extent2D(0.5, 0.5));
109  return lsst::geom::Box2I(lsst::geom::Point2I(corner), size, false);
110 }
111 
112 ndarray::View<boost::fusion::vector2<ndarray::index::Range, ndarray::index::Range> > Box2I::getSlices()
113  const {
114  return ndarray::view(getBeginY(), getEndY())(getBeginX(), getEndX());
115 }
116 
117 bool Box2I::contains(Point2I const& point) const noexcept {
118  return all(point.ge(this->getMin())) && all(point.le(this->getMax()));
119 }
120 
121 bool Box2I::contains(Box2I const& other) const noexcept {
122  return other.isEmpty() ||
123  (all(other.getMin().ge(this->getMin())) && all(other.getMax().le(this->getMax())));
124 }
125 
126 bool Box2I::overlaps(Box2I const& other) const noexcept {
127  return !(other.isEmpty() || this->isEmpty() || any(other.getMax().lt(this->getMin())) ||
128  any(other.getMin().gt(this->getMax())));
129 }
130 
131 void Box2I::grow(Extent2I const& buffer) {
132  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
133  _minimum -= buffer;
134  _dimensions += buffer * 2;
135  if (any(_dimensions.le(0))) *this = Box2I();
136 }
137 
138 void Box2I::shift(Extent2I const& offset) {
139  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
140  _minimum += offset;
141 }
142 
143 void Box2I::flipLR(int xextent) {
144  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
145  // Apply flip about y-axis assumine parent coordinate system
146  _minimum[0] = xextent - (_minimum[0] + _dimensions[0]);
147  // _dimensions should remain unchanged
148 }
149 
150 void Box2I::flipTB(int yextent) {
151  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
152  // Apply flip about y-axis assumine parent coordinate system
153  _minimum[1] = yextent - (_minimum[1] + _dimensions[1]);
154  // _dimensions should remain unchanged
155 }
156 
157 void Box2I::include(Point2I const& point) {
158  if (isEmpty()) {
159  _minimum = point;
160  _dimensions = Extent2I(1);
161  return;
162  }
163  Point2I maximum(getMax());
164  for (int n = 0; n < 2; ++n) {
165  if (point[n] < _minimum[n]) {
166  _minimum[n] = point[n];
167  } else if (point[n] > maximum[n]) {
168  maximum[n] = point[n];
169  }
170  }
171  _dimensions = Extent2I(1) + maximum - _minimum;
172 }
173 
174 void Box2I::include(Box2I const& other) {
175  if (other.isEmpty()) return;
176  if (this->isEmpty()) {
177  *this = other;
178  return;
179  }
180  Point2I maximum(getMax());
181  Point2I const& otherMin = other.getMin();
182  Point2I const otherMax = other.getMax();
183  for (int n = 0; n < 2; ++n) {
184  if (otherMin[n] < _minimum[n]) {
185  _minimum[n] = otherMin[n];
186  }
187  if (otherMax[n] > maximum[n]) {
188  maximum[n] = otherMax[n];
189  }
190  }
191  _dimensions = Extent2I(1) + maximum - _minimum;
192 }
193 
194 void Box2I::clip(Box2I const& other) noexcept {
195  if (isEmpty()) return;
196  if (other.isEmpty()) {
197  *this = Box2I();
198  return;
199  }
200  Point2I maximum(getMax());
201  Point2I const& otherMin = other.getMin();
202  Point2I const otherMax = other.getMax();
203  for (int n = 0; n < 2; ++n) {
204  if (otherMin[n] > _minimum[n]) {
205  _minimum[n] = otherMin[n];
206  }
207  if (otherMax[n] < maximum[n]) {
208  maximum[n] = otherMax[n];
209  }
210  }
211  if (any(maximum.lt(_minimum))) {
212  *this = Box2I();
213  return;
214  }
215  _dimensions = Extent2I(1) + maximum - _minimum;
216 }
217 
218 bool Box2I::operator==(Box2I const& other) const noexcept {
219  return other._minimum == this->_minimum && other._dimensions == this->_dimensions;
220 }
221 
222 bool Box2I::operator!=(Box2I const& other) const noexcept {
223  return other._minimum != this->_minimum || other._dimensions != this->_dimensions;
224 }
225 
226 std::size_t Box2I::hash_value() const noexcept {
227  // Completely arbitrary seed
228  return utils::hashCombine(17, _minimum, _dimensions);
229 }
230 
232  std::vector<Point2I> retVec;
233  retVec.push_back(getMin());
234  retVec.push_back(Point2I(getMaxX(), getMinY()));
235  retVec.push_back(getMax());
236  retVec.push_back(Point2I(getMinX(), getMaxY()));
237  return retVec;
238 }
239 
241 
243 
244 Box2D::Box2D() noexcept : _minimum(INVALID), _maximum(INVALID) {}
245 
246 Box2D::Box2D(Point2D const& minimum, Point2D const& maximum, bool invert) noexcept
247  : _minimum(minimum), _maximum(maximum) {
248  for (int n = 0; n < 2; ++n) {
249  if (_minimum[n] == _maximum[n]) {
250  *this = Box2D();
251  return;
252  } else if (_minimum[n] > _maximum[n]) {
253  if (invert) {
254  std::swap(_minimum[n], _maximum[n]);
255  } else {
256  *this = Box2D();
257  return;
258  }
259  }
260  }
261 }
262 
263 Box2D::Box2D(Point2D const& corner, Extent2D const& dimensions, bool invert) noexcept
264  : _minimum(corner), _maximum(corner + dimensions) {
265  for (int n = 0; n < 2; ++n) {
266  if (_minimum[n] == _maximum[n]) {
267  *this = Box2D();
268  return;
269  } else if (_minimum[n] > _maximum[n]) {
270  if (invert) {
271  std::swap(_minimum[n], _maximum[n]);
272  } else {
273  *this = Box2D();
274  return;
275  }
276  }
277  }
278 }
279 
280 Box2D::Box2D(Box2I const& other) noexcept
281  : _minimum(Point2D(other.getMin()) - Extent2D(0.5)),
282  _maximum(Point2D(other.getMax()) + Extent2D(0.5)) {
283  if (other.isEmpty()) *this = Box2D();
284 }
285 
286 Box2D Box2D::makeCenteredBox(Point2D const& center, Box2D::Extent const& size) noexcept {
287  lsst::geom::Point2D corner(center);
288  corner.shift(-0.5 * size);
289  return lsst::geom::Box2D(corner, size, false);
290 }
291 
292 bool Box2D::contains(Point2D const& point) const noexcept {
293  return all(point.ge(this->getMin())) && all(point.lt(this->getMax()));
294 }
295 
296 bool Box2D::contains(Box2D const& other) const noexcept {
297  return other.isEmpty() ||
298  (all(other.getMin().ge(this->getMin())) && all(other.getMax().le(this->getMax())));
299 }
300 
301 bool Box2D::overlaps(Box2D const& other) const noexcept {
302  return !(other.isEmpty() || this->isEmpty() || any(other.getMax().le(this->getMin())) ||
303  any(other.getMin().ge(this->getMax())));
304 }
305 
306 void Box2D::grow(Extent2D const& buffer) {
307  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
308  _minimum -= buffer;
309  _maximum += buffer;
310  if (any(_minimum.ge(_maximum))) *this = Box2D();
311 }
312 
313 void Box2D::shift(Extent2D const& offset) {
314  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
315  _minimum += offset;
316  _maximum += offset;
317 }
318 
319 void Box2D::flipLR(float xextent) {
320  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
321  // Swap min and max values for x dimension
322  _minimum[0] += _maximum[0];
323  _maximum[0] = _minimum[0] - _maximum[0];
324  _minimum[0] -= _maximum[0];
325  // Apply flip assuming coordinate system of parent.
326  _minimum[0] = xextent - _minimum[0];
327  _maximum[0] = xextent - _maximum[0];
328  // _dimensions should remain unchanged
329 }
330 
331 void Box2D::flipTB(float yextent) {
332  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
333  // Swap min and max values for y dimension
334  _minimum[1] += _maximum[1];
335  _maximum[1] = _minimum[1] - _maximum[1];
336  _minimum[1] -= _maximum[1];
337  // Apply flip assuming coordinate system of parent.
338  _minimum[1] = yextent - _minimum[1];
339  _maximum[1] = yextent - _maximum[1];
340  // _dimensions should remain unchanged
341 }
342 
343 void Box2D::include(Point2D const& point) noexcept {
344  if (isEmpty()) {
345  _minimum = point;
346  _maximum = point;
347  _tweakMax(0);
348  _tweakMax(1);
349  return;
350  }
351  for (int n = 0; n < 2; ++n) {
352  if (point[n] < _minimum[n]) {
353  _minimum[n] = point[n];
354  } else if (point[n] >= _maximum[n]) {
355  _maximum[n] = point[n];
356  _tweakMax(n);
357  }
358  }
359 }
360 
361 void Box2D::include(Box2D const& other) noexcept {
362  if (other.isEmpty()) return;
363  if (this->isEmpty()) {
364  *this = other;
365  return;
366  }
367  Point2D const& otherMin = other.getMin();
368  Point2D const& otherMax = other.getMax();
369  for (int n = 0; n < 2; ++n) {
370  if (otherMin[n] < _minimum[n]) {
371  _minimum[n] = otherMin[n];
372  }
373  if (otherMax[n] > _maximum[n]) {
374  _maximum[n] = otherMax[n];
375  }
376  }
377 }
378 
379 void Box2D::clip(Box2D const& other) noexcept {
380  if (isEmpty()) return;
381  if (other.isEmpty()) {
382  *this = Box2D();
383  return;
384  }
385  Point2D const& otherMin = other.getMin();
386  Point2D const& otherMax = other.getMax();
387  for (int n = 0; n < 2; ++n) {
388  if (otherMin[n] > _minimum[n]) {
389  _minimum[n] = otherMin[n];
390  }
391  if (otherMax[n] < _maximum[n]) {
392  _maximum[n] = otherMax[n];
393  }
394  }
395  if (any(_maximum.le(_minimum))) {
396  *this = Box2D();
397  return;
398  }
399 }
400 
401 bool Box2D::operator==(Box2D const& other) const noexcept {
402  return (other.isEmpty() && this->isEmpty()) ||
403  (other._minimum == this->_minimum && other._maximum == this->_maximum);
404 }
405 
406 bool Box2D::operator!=(Box2D const& other) const noexcept {
407  return !(other.isEmpty() && other.isEmpty()) &&
408  (other._minimum != this->_minimum || other._maximum != this->_maximum);
409 }
410 
411 std::size_t Box2D::hash_value() const noexcept {
412  if (isEmpty()) {
413  // All empty boxes are equal and must have equal hashes
414  return 179;
415  } else {
416  // Completely arbitrary seed
417  return utils::hashCombine(17, _minimum, _maximum);
418  }
419 }
420 
422  std::vector<Point2D> retVec;
423  retVec.push_back(getMin());
424  retVec.push_back(Point2D(getMaxX(), getMinY()));
425  retVec.push_back(getMax());
426  retVec.push_back(Point2D(getMinX(), getMaxY()));
427  return retVec;
428 }
429 
431  if (box.isEmpty()) return os << "Box2I()";
432  return os << "Box2I(Point2I" << box.getMin() << ", Extent2I" << box.getDimensions() << ")";
433 }
434 
436  if (box.isEmpty()) return os << "Box2D()";
437  return os << "Box2D(Point2D" << box.getMin() << ", Extent2D" << box.getDimensions() << ")";
438 }
439 
440 } // namespace geom
441 } // namespace lsst
bool operator==(Box2I const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:218
double getMinY() const noexcept
Definition: Box.h:395
std::vector< Point2D > getCorners() const
Get the corner points.
Definition: Box.cc:421
Extent2I const getDimensions() const noexcept
Definition: Box.h:173
Point2I const getMax() const noexcept
Definition: Box.h:147
void flipLR(int xExtent)
Flip a bounding box about the y-axis given a parent box of extent (xExtent).
Definition: Box.cc:143
void shift(Extent< T, N > const &offset) noexcept(Super::IS_ELEMENT_NOTHROW_COPYABLE)
Shift the point by the given offset.
Definition: Point.h:132
T ceil(T... args)
A floating-point coordinate rectangle geometry.
Definition: Box.h:305
T swap(T... args)
double getMinX() const noexcept
Definition: Box.h:394
T epsilon(T... args)
A coordinate class intended to represent absolute positions.
CoordinateExpr< N > le(Extent< T, N > const &other) const noexcept
Definition: Extent.cc:68
void include(Point2D const &point) noexcept
Expand this to ensure that this->contains(point).
Definition: Box.cc:343
bool isEmpty() const noexcept
Return true if the box contains no points.
Definition: Box.h:194
double getMaxX() const noexcept
Definition: Box.h:398
Extent2D const getDimensions() const noexcept
Definition: Box.h:408
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Box.cc:411
T floor(T... args)
Relationship invert(Relationship r)
Given the relationship between two sets A and B (i.e.
Definition: Relationship.h:55
ItemVariant const * other
Definition: Schema.cc:56
Point2D const getMin() const noexcept
Definition: Box.h:393
void shift(Extent2D const &offset)
Shift the position of the box by the given offset.
Definition: Box.cc:313
std::vector< Point2I > getCorners() const
Get the corner points.
Definition: Box.cc:231
int getEndY() const noexcept
Definition: Box.h:164
void clip(Box2D const &other) noexcept
Shrink this to ensure that other.contains(*this).
Definition: Box.cc:379
Point< double, 2 > Point2D
Definition: Point.h:324
static double const EPSILON
Value the maximum coordinate is multiplied by to increase it by the smallest possible amount...
Definition: Box.h:315
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
int getBeginY() const noexcept
Definition: Box.h:160
void flipLR(float xExtent)
Flip a bounding box about the y-axis given a parent box of extent (xExtent).
Definition: Box.cc:319
Point2I const getMin() const noexcept
Definition: Box.h:143
bool isEmpty() const noexcept
Return true if the box contains no points.
Definition: Box.h:431
T push_back(T... args)
afw::table::PointKey< int > dimensions
Definition: GaussianPsf.cc:49
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
double getMaxY() const noexcept
Definition: Box.h:399
A base class for image defects.
Reports when the result of an arithmetic operation is too large for the destination type...
Definition: Runtime.h:124
CoordinateExpr< N > ge(Point< T, N > const &other) const noexcept
Definition: Point.cc:111
void grow(int buffer)
Increase the size of the box by the given buffer amount in all directions.
Definition: Box.h:219
int getMaxY() const noexcept
Definition: Box.h:149
bool contains(Point2D const &point) const noexcept
Return true if the box contains the point.
Definition: Box.cc:292
static double const INVALID
Value used to specify undefined coordinate values.
Definition: Box.h:318
void grow(double buffer)
Increase the size of the box by the given buffer amount in all directions.
Definition: Box.h:456
int getBeginX() const noexcept
Definition: Box.h:159
static Box2D makeCenteredBox(Point2D const &center, Extent const &size) noexcept
Create a box centered on a particular point.
Definition: Box.cc:286
T isfinite(T... args)
std::ostream & operator<<(std::ostream &os, lsst::geom::AffineTransform const &transform)
A coordinate class intended to represent offsets and dimensions.
Point< int, 2 > Point2I
Definition: Point.h:321
int getMaxX() const noexcept
Definition: Box.h:148
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Box.cc:226
Point2D const getMax() const noexcept
Definition: Box.h:397
bool operator!=(Box2I const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:222
int getMinX() const noexcept
Definition: Box.h:144
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
Extent< int, 2 > Extent2I
Definition: Extent.h:397
STL class.
CoordinateExpr< N > lt(Point< T, N > const &other) const noexcept
Definition: Point.cc:90
void flipTB(float yExtent)
Flip a bounding box about the x-axis given a parent box of extent (yExtent).
Definition: Box.cc:331
int getEndX() const noexcept
Definition: Box.h:163
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
Definition: Box.cc:157
bool overlaps(Box2D const &other) const noexcept
Return true if any points in other are also in this.
Definition: Box.cc:301
void shift(Extent2I const &offset)
Shift the position of the box by the given offset.
Definition: Box.cc:138
bool overlaps(Box2I const &other) const noexcept
Return true if any points in other are also in this.
Definition: Box.cc:126
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition: Box.cc:117
void clip(Box2I const &other) noexcept
Shrink this to ensure that other.contains(*this).
Definition: Box.cc:194
Point2D const getCenter() const noexcept
Definition: Box.cc:96
ndarray::View< boost::fusion::vector2< ndarray::index::Range, ndarray::index::Range > > getSlices() const
Return slices to extract the box&#39;s region from an ndarray::Array.
Definition: Box.cc:112
Reports invalid arguments.
Definition: Runtime.h:66
static Box2I makeCenteredBox(Point2D const &center, Extent const &size)
Create a box centered as closely as possible on a particular point.
Definition: Box.cc:100
Box2I() noexcept
Construct an empty box.
Definition: Box.h:63
T quiet_NaN(T... args)
Box2D() noexcept
Construct an empty box.
Definition: Box.cc:244
bool operator==(Box2D const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:401
Extent< double, 2 > Extent2D
Definition: Extent.h:400
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
Definition: hashCombine.h:35
std::ostream * os
Definition: Schema.cc:746
An integer coordinate rectangle.
Definition: Box.h:54
STL class.
int getMinY() const noexcept
Definition: Box.h:145
void flipTB(int yExtent)
Flip a bounding box about the x-axis given a parent box of extent (yExtent).
Definition: Box.cc:150
bool operator!=(Box2D const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:406