LSSTApplications  16.0-10-g9d3e444,16.0-10-gb7df26b+3,16.0-10-gbd9fc95,16.0-11-g09ed895+3,16.0-11-g12e47bd+11,16.0-12-g5c924a4+21,16.0-12-g71e5ef5+10,16.0-15-g7af1f30+2,16.0-15-gdd5ca33+2,16.0-16-gf0259e2+1,16.0-18-gcf94535+13,16.0-19-g283fd30+1,16.0-19-g9d290d5+13,16.0-2-g0febb12+22,16.0-2-g9d5294e+76,16.0-20-g42ca1a7,16.0-20-g8d11721+2,16.0-23-g28ad22d+2,16.0-25-g723f23e8+2,16.0-29-g44d5b4c,16.0-32-ga392cb8+2,16.0-32-gc794025+1,16.0-4-g18f3627+20,16.0-4-g5f3a788+21,16.0-4-g6d309fa,16.0-4-ga3eb747+11,16.0-4-gabf74b7+42,16.0-4-gb13d127+7,16.0-5-g27fb78a+20,16.0-5-g6a53317+47,16.0-5-gb3f8a4b+101,16.0-6-g9321be7+5,16.0-6-gcbc7b31+58,16.0-6-gf49912c+44,16.0-65-g12857137,16.0-76-g802307b6d+2,16.0-8-g21fd5fe+45,16.0-8-g3a9f023+27,16.0-8-gc11f1cf+11,master-g5605b1aa4d+2,w.2019.05
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 Box2I Box2I::makeCenteredBox(Point2D const& center, Box2I::Extent const& size) {
97  if (!std::isfinite(center[0]) || !std::isfinite(center[1])) {
98  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Cannot make Box2I with non-finite center");
99  }
100 
101  lsst::geom::Point2D corner(center);
102  corner.shift(-0.5 * lsst::geom::Extent2D(size));
103  // compensate for Box2I's coordinate conventions (where max = min + size - 1)
104  corner.shift(lsst::geom::Extent2D(0.5, 0.5));
105  return lsst::geom::Box2I(lsst::geom::Point2I(corner), size, false);
106 }
107 
108 ndarray::View<boost::fusion::vector2<ndarray::index::Range, ndarray::index::Range> > Box2I::getSlices()
109  const {
110  return ndarray::view(getBeginY(), getEndY())(getBeginX(), getEndX());
111 }
112 
113 bool Box2I::contains(Point2I const& point) const noexcept {
114  return all(point.ge(this->getMin())) && all(point.le(this->getMax()));
115 }
116 
117 bool Box2I::contains(Box2I const& other) const noexcept {
118  return other.isEmpty() ||
119  (all(other.getMin().ge(this->getMin())) && all(other.getMax().le(this->getMax())));
120 }
121 
122 bool Box2I::overlaps(Box2I const& other) const noexcept {
123  return !(other.isEmpty() || this->isEmpty() || any(other.getMax().lt(this->getMin())) ||
124  any(other.getMin().gt(this->getMax())));
125 }
126 
127 void Box2I::grow(Extent2I const& buffer) {
128  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
129  _minimum -= buffer;
130  _dimensions += buffer * 2;
131  if (any(_dimensions.le(0))) *this = Box2I();
132 }
133 
134 void Box2I::shift(Extent2I const& offset) {
135  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
136  _minimum += offset;
137 }
138 
139 void Box2I::flipLR(int xextent) {
140  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
141  // Apply flip about y-axis assumine parent coordinate system
142  _minimum[0] = xextent - (_minimum[0] + _dimensions[0]);
143  // _dimensions should remain unchanged
144 }
145 
146 void Box2I::flipTB(int yextent) {
147  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
148  // Apply flip about y-axis assumine parent coordinate system
149  _minimum[1] = yextent - (_minimum[1] + _dimensions[1]);
150  // _dimensions should remain unchanged
151 }
152 
153 void Box2I::include(Point2I const& point) {
154  if (isEmpty()) {
155  _minimum = point;
156  _dimensions = Extent2I(1);
157  return;
158  }
159  Point2I maximum(getMax());
160  for (int n = 0; n < 2; ++n) {
161  if (point[n] < _minimum[n]) {
162  _minimum[n] = point[n];
163  } else if (point[n] > maximum[n]) {
164  maximum[n] = point[n];
165  }
166  }
167  _dimensions = Extent2I(1) + maximum - _minimum;
168 }
169 
170 void Box2I::include(Box2I const& other) {
171  if (other.isEmpty()) return;
172  if (this->isEmpty()) {
173  *this = other;
174  return;
175  }
176  Point2I maximum(getMax());
177  Point2I const& otherMin = other.getMin();
178  Point2I const otherMax = other.getMax();
179  for (int n = 0; n < 2; ++n) {
180  if (otherMin[n] < _minimum[n]) {
181  _minimum[n] = otherMin[n];
182  }
183  if (otherMax[n] > maximum[n]) {
184  maximum[n] = otherMax[n];
185  }
186  }
187  _dimensions = Extent2I(1) + maximum - _minimum;
188 }
189 
190 void Box2I::clip(Box2I const& other) noexcept {
191  if (isEmpty()) return;
192  if (other.isEmpty()) {
193  *this = Box2I();
194  return;
195  }
196  Point2I maximum(getMax());
197  Point2I const& otherMin = other.getMin();
198  Point2I const otherMax = other.getMax();
199  for (int n = 0; n < 2; ++n) {
200  if (otherMin[n] > _minimum[n]) {
201  _minimum[n] = otherMin[n];
202  }
203  if (otherMax[n] < maximum[n]) {
204  maximum[n] = otherMax[n];
205  }
206  }
207  if (any(maximum.lt(_minimum))) {
208  *this = Box2I();
209  return;
210  }
211  _dimensions = Extent2I(1) + maximum - _minimum;
212 }
213 
214 bool Box2I::operator==(Box2I const& other) const noexcept {
215  return other._minimum == this->_minimum && other._dimensions == this->_dimensions;
216 }
217 
218 bool Box2I::operator!=(Box2I const& other) const noexcept {
219  return other._minimum != this->_minimum || other._dimensions != this->_dimensions;
220 }
221 
222 std::size_t Box2I::hash_value() const noexcept {
223  // Completely arbitrary seed
224  return utils::hashCombine(17, _minimum, _dimensions);
225 }
226 
228  std::vector<Point2I> retVec;
229  retVec.push_back(getMin());
230  retVec.push_back(Point2I(getMaxX(), getMinY()));
231  retVec.push_back(getMax());
232  retVec.push_back(Point2I(getMinX(), getMaxY()));
233  return retVec;
234 }
235 
237 
239 
240 Box2D::Box2D() noexcept : _minimum(INVALID), _maximum(INVALID) {}
241 
242 Box2D::Box2D(Point2D const& minimum, Point2D const& maximum, bool invert) noexcept
243  : _minimum(minimum), _maximum(maximum) {
244  for (int n = 0; n < 2; ++n) {
245  if (_minimum[n] == _maximum[n]) {
246  *this = Box2D();
247  return;
248  } else if (_minimum[n] > _maximum[n]) {
249  if (invert) {
250  std::swap(_minimum[n], _maximum[n]);
251  } else {
252  *this = Box2D();
253  return;
254  }
255  }
256  }
257 }
258 
259 Box2D::Box2D(Point2D const& corner, Extent2D const& dimensions, bool invert) noexcept
260  : _minimum(corner), _maximum(corner + dimensions) {
261  for (int n = 0; n < 2; ++n) {
262  if (_minimum[n] == _maximum[n]) {
263  *this = Box2D();
264  return;
265  } else if (_minimum[n] > _maximum[n]) {
266  if (invert) {
267  std::swap(_minimum[n], _maximum[n]);
268  } else {
269  *this = Box2D();
270  return;
271  }
272  }
273  }
274 }
275 
276 Box2D::Box2D(Box2I const& other) noexcept
277  : _minimum(Point2D(other.getMin()) - Extent2D(0.5)),
278  _maximum(Point2D(other.getMax()) + Extent2D(0.5)) {
279  if (other.isEmpty()) *this = Box2D();
280 }
281 
282 Box2D Box2D::makeCenteredBox(Point2D const& center, Box2D::Extent const& size) noexcept {
283  lsst::geom::Point2D corner(center);
284  corner.shift(-0.5 * size);
285  return lsst::geom::Box2D(corner, size, false);
286 }
287 
288 bool Box2D::contains(Point2D const& point) const noexcept {
289  return all(point.ge(this->getMin())) && all(point.lt(this->getMax()));
290 }
291 
292 bool Box2D::contains(Box2D const& other) const noexcept {
293  return other.isEmpty() ||
294  (all(other.getMin().ge(this->getMin())) && all(other.getMax().le(this->getMax())));
295 }
296 
297 bool Box2D::overlaps(Box2D const& other) const noexcept {
298  return !(other.isEmpty() || this->isEmpty() || any(other.getMax().le(this->getMin())) ||
299  any(other.getMin().ge(this->getMax())));
300 }
301 
302 void Box2D::grow(Extent2D const& buffer) {
303  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
304  _minimum -= buffer;
305  _maximum += buffer;
306  if (any(_minimum.ge(_maximum))) *this = Box2D();
307 }
308 
309 void Box2D::shift(Extent2D const& offset) {
310  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
311  _minimum += offset;
312  _maximum += offset;
313 }
314 
315 void Box2D::flipLR(float xextent) {
316  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
317  // Swap min and max values for x dimension
318  _minimum[0] += _maximum[0];
319  _maximum[0] = _minimum[0] - _maximum[0];
320  _minimum[0] -= _maximum[0];
321  // Apply flip assuming coordinate system of parent.
322  _minimum[0] = xextent - _minimum[0];
323  _maximum[0] = xextent - _maximum[0];
324  // _dimensions should remain unchanged
325 }
326 
327 void Box2D::flipTB(float yextent) {
328  if (isEmpty()) return; // should we throw an exception here instead of a no-op?
329  // Swap min and max values for y dimension
330  _minimum[1] += _maximum[1];
331  _maximum[1] = _minimum[1] - _maximum[1];
332  _minimum[1] -= _maximum[1];
333  // Apply flip assuming coordinate system of parent.
334  _minimum[1] = yextent - _minimum[1];
335  _maximum[1] = yextent - _maximum[1];
336  // _dimensions should remain unchanged
337 }
338 
339 void Box2D::include(Point2D const& point) noexcept {
340  if (isEmpty()) {
341  _minimum = point;
342  _maximum = point;
343  _tweakMax(0);
344  _tweakMax(1);
345  return;
346  }
347  for (int n = 0; n < 2; ++n) {
348  if (point[n] < _minimum[n]) {
349  _minimum[n] = point[n];
350  } else if (point[n] >= _maximum[n]) {
351  _maximum[n] = point[n];
352  _tweakMax(n);
353  }
354  }
355 }
356 
357 void Box2D::include(Box2D const& other) noexcept {
358  if (other.isEmpty()) return;
359  if (this->isEmpty()) {
360  *this = other;
361  return;
362  }
363  Point2D const& otherMin = other.getMin();
364  Point2D const& otherMax = other.getMax();
365  for (int n = 0; n < 2; ++n) {
366  if (otherMin[n] < _minimum[n]) {
367  _minimum[n] = otherMin[n];
368  }
369  if (otherMax[n] > _maximum[n]) {
370  _maximum[n] = otherMax[n];
371  }
372  }
373 }
374 
375 void Box2D::clip(Box2D const& other) noexcept {
376  if (isEmpty()) return;
377  if (other.isEmpty()) {
378  *this = Box2D();
379  return;
380  }
381  Point2D const& otherMin = other.getMin();
382  Point2D const& otherMax = other.getMax();
383  for (int n = 0; n < 2; ++n) {
384  if (otherMin[n] > _minimum[n]) {
385  _minimum[n] = otherMin[n];
386  }
387  if (otherMax[n] < _maximum[n]) {
388  _maximum[n] = otherMax[n];
389  }
390  }
391  if (any(_maximum.le(_minimum))) {
392  *this = Box2D();
393  return;
394  }
395 }
396 
397 bool Box2D::operator==(Box2D const& other) const noexcept {
398  return (other.isEmpty() && this->isEmpty()) ||
399  (other._minimum == this->_minimum && other._maximum == this->_maximum);
400 }
401 
402 bool Box2D::operator!=(Box2D const& other) const noexcept {
403  return !(other.isEmpty() && other.isEmpty()) &&
404  (other._minimum != this->_minimum || other._maximum != this->_maximum);
405 }
406 
407 std::size_t Box2D::hash_value() const noexcept {
408  if (isEmpty()) {
409  // All empty boxes are equal and must have equal hashes
410  return 179;
411  } else {
412  // Completely arbitrary seed
413  return utils::hashCombine(17, _minimum, _maximum);
414  }
415 }
416 
418  std::vector<Point2D> retVec;
419  retVec.push_back(getMin());
420  retVec.push_back(Point2D(getMaxX(), getMinY()));
421  retVec.push_back(getMax());
422  retVec.push_back(Point2D(getMinX(), getMaxY()));
423  return retVec;
424 }
425 
427  if (box.isEmpty()) return os << "Box2I()";
428  return os << "Box2I(Point2I" << box.getMin() << ", Extent2I" << box.getDimensions() << ")";
429 }
430 
432  if (box.isEmpty()) return os << "Box2D()";
433  return os << "Box2D(Point2D" << box.getMin() << ", Extent2D" << box.getDimensions() << ")";
434 }
435 
436 } // namespace geom
437 } // namespace lsst
bool operator==(Box2I const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:214
double getMinY() const noexcept
Definition: Box.h:384
std::vector< Point2D > getCorners() const
Get the corner points.
Definition: Box.cc:417
Extent2I const getDimensions() const noexcept
Definition: Box.h:173
afw::table::PointKey< int > dimensions
Definition: GaussianPsf.cc:49
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:139
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:294
T swap(T... args)
double getMinX() const noexcept
Definition: Box.h:383
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:339
bool isEmpty() const noexcept
Return true if the box contains no points.
Definition: Box.h:183
double getMaxX() const noexcept
Definition: Box.h:387
Extent2D const getDimensions() const noexcept
Definition: Box.h:397
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Box.cc:407
T floor(T... args)
Relationship invert(Relationship r)
Given the relationship between two sets A and B (i.e.
Definition: Relationship.h:55
Point2D const getMin() const noexcept
Definition: Box.h:382
void shift(Extent2D const &offset)
Shift the position of the box by the given offset.
Definition: Box.cc:309
std::vector< Point2I > getCorners() const
Get the corner points.
Definition: Box.cc:227
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:375
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:304
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:315
Point2I const getMin() const noexcept
Definition: Box.h:143
bool isEmpty() const noexcept
Return true if the box contains no points.
Definition: Box.h:420
T push_back(T... args)
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
double getMaxY() const noexcept
Definition: Box.h:388
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:208
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:288
static double const INVALID
Value used to specify undefined coordinate values.
Definition: Box.h:307
void grow(double buffer)
Increase the size of the box by the given buffer amount in all directions.
Definition: Box.h:445
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:282
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:222
Point2D const getMax() const noexcept
Definition: Box.h:386
bool operator!=(Box2I const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:218
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:327
int getEndX() const noexcept
Definition: Box.h:163
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
Definition: Box.cc:153
bool overlaps(Box2D const &other) const noexcept
Return true if any points in other are also in this.
Definition: Box.cc:297
void shift(Extent2I const &offset)
Shift the position of the box by the given offset.
Definition: Box.cc:134
bool overlaps(Box2I const &other) const noexcept
Return true if any points in other are also in this.
Definition: Box.cc:122
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition: Box.cc:113
void clip(Box2I const &other) noexcept
Shrink this to ensure that other.contains(*this).
Definition: Box.cc:190
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:108
Reports invalid arguments.
Definition: Runtime.h:66
ItemVariant const * other
Definition: Schema.cc:56
static Box2I makeCenteredBox(Point2D const &center, Extent const &size)
Create a box centered as closely as possible on a particular point.
Definition: Box.cc:96
Box2I() noexcept
Construct an empty box.
Definition: Box.h:63
T quiet_NaN(T... args)
Box2D() noexcept
Construct an empty box.
Definition: Box.cc:240
bool operator==(Box2D const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:397
Extent< double, 2 > Extent2D
Definition: Extent.h:400
std::size_t hashCombine(std::size_t seed) noexcept
Combine hashes.
Definition: hashCombine.h:35
An integer coordinate rectangle.
Definition: Box.h:54
STL class.
int getMinY() const noexcept
Definition: Box.h:145
std::ostream * os
Definition: Schema.cc:746
void flipTB(int yExtent)
Flip a bounding box about the x-axis given a parent box of extent (yExtent).
Definition: Box.cc:146
bool operator!=(Box2D const &other) const noexcept
Compare two boxes for equality.
Definition: Box.cc:402