LSST Applications g07dc498a13+8a3ff5e555,g1409bbee79+8a3ff5e555,g1a7e361dbc+8a3ff5e555,g1fd858c14a+cdfc45a1e6,g28da252d5a+05df2523c9,g33399d78f5+b7ce9b29cb,g35bb328faa+e55fef2c71,g3bd4b5ce2c+801aef9193,g43bc871e57+32b9ddb877,g53246c7159+e55fef2c71,g60b5630c4e+f284161bd5,g6992a3b7c1+89734069dd,g6e5c4a0e23+7c1dc9d5af,g78460c75b0+8427c4cc8f,g786e29fd12+307f82e6af,g8534526c7b+af2545e932,g85d8d04dbe+6bd817bf56,g89139ef638+8a3ff5e555,g8b49a6ea8e+f284161bd5,g9125e01d80+e55fef2c71,g989de1cb63+8a3ff5e555,g9a9baf55bd+a4ec829099,g9f33ca652e+eafd8913dc,gaaedd4e678+8a3ff5e555,gabe3b4be73+9c0c3c7524,gb092a606b0+b01f69f56e,gb58c049af0+28045f66fd,gc2fcbed0ba+f284161bd5,gca43fec769+e55fef2c71,gcf25f946ba+b7ce9b29cb,gd6cbbdb0b4+784e334a77,gde0f65d7ad+3bc0905521,ge278dab8ac+25667260f6,geab183fbe5+f284161bd5,gecb8035dfe+0fa5abcb94,gf1e97e5484+b700727375,gf58bf46354+e55fef2c71,gfe7187db8c+f9d6462591,w.2025.01
LSST Data Management Base Package
Loading...
Searching...
No Matches
CompoundRegion.cc
Go to the documentation of this file.
1/*
2 * This file is part of sphgeom.
3 *
4 * Developed for the LSST Data Management System.
5 * This product includes software developed by the LSST Project
6 * (http://www.lsst.org).
7 * See the COPYRIGHT file at the top-level directory of this distribution
8 * for details of code ownership.
9 *
10 * This software is dual licensed under the GNU General Public License and also
11 * under a 3-clause BSD license. Recipients may choose which of these licenses
12 * to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
13 * respectively. If you choose the GPL option then the following text applies
14 * (but note that there is still no warranty even if you opt for BSD instead):
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29
32
34
35#include <algorithm>
36#include <iostream>
37#include <stdexcept>
38
39#include "lsst/sphgeom/Box.h"
40#include "lsst/sphgeom/Box3d.h"
41#include "lsst/sphgeom/Circle.h"
44#include "lsst/sphgeom/codec.h"
45
46namespace lsst {
47namespace sphgeom {
48
49namespace {
50
51// A version of decodeU64 from codec.h that checks for buffer overruns and
52// increments the buffer pointer it is given.
53std::uint64_t consumeDecodeU64(std::uint8_t const *&buffer, std::uint8_t const *end) {
54 if (buffer + 8 > end) {
55 throw std::runtime_error("Encoded CompoundRegion is truncated.");
56 }
58 buffer += 8;
59 return result;
60}
61
62template <typename F>
63auto getUnionBounds(UnionRegion const &compound, F func) {
64 if (compound.nOperands() == 0) {
65 return func(Box::empty());
66 }
67 auto bounds = func(compound.getOperand(0));
68 for (std::size_t i = 1; i < compound.nOperands(); ++ i) {
69 bounds.expandTo(func(compound.getOperand(i)));
70 }
71 return bounds;
72}
73
74template <typename F>
75auto getIntersectionBounds(IntersectionRegion const &compound, F func) {
76 if (compound.nOperands() == 0) {
77 return func(Box::full());
78 }
79 auto bounds = func(compound.getOperand(0));
80 for (std::size_t i = 1; i < compound.nOperands(); ++ i) {
81 bounds.clipTo(func(compound.getOperand(i)));
82 }
83 return bounds;
84}
85
86} // namespace
87
89 : _operands(std::move(operands))
90{
91}
92
94 : _operands()
95{
96 for (auto&& operand: other._operands) {
97 _operands.emplace_back(operand->clone());
98 }
99}
100
101// Flatten vector of regions in-place.
102template <typename Compound>
104 for (size_t i = 0; i != _operands.size(); ) {
105 if (auto compound = dynamic_cast<Compound*>(_operands[i].get())) {
106 // Move all regions from this operand, then remove it.
107 std::move(
108 compound->_operands.begin(),
109 compound->_operands.end(),
110 std::inserter(_operands, _operands.begin() + i + 1)
111 );
112 _operands.erase(_operands.begin() + i);
113 } else {
114 ++ i;
115 }
116 }
117}
118
119Relationship CompoundRegion::relate(Box const &b) const { return relate(static_cast<Region const &>(b)); }
120Relationship CompoundRegion::relate(Circle const &c) const { return relate(static_cast<Region const &>(c)); }
121Relationship CompoundRegion::relate(ConvexPolygon const &p) const { return relate(static_cast<Region const &>(p)); }
122Relationship CompoundRegion::relate(Ellipse const &e) const { return relate(static_cast<Region const &>(e)); }
123
126 buffer.push_back(tc);
127 for (auto&& operand: _operands) {
128 auto operand_buffer = operand->encode();
129 encodeU64(operand_buffer.size(), buffer);
130 buffer.insert(buffer.end(), operand_buffer.begin(), operand_buffer.end());
131 }
132 return buffer;
133}
134
136 std::uint8_t tc, std::uint8_t const *buffer, std::size_t nBytes) {
137 std::uint8_t const *end = buffer + nBytes;
138 if (nBytes == 0) {
139 throw std::runtime_error("Encoded CompoundRegion is truncated.");
140 }
141 if (buffer[0] != tc) {
142 throw std::runtime_error("Byte string is not an encoded CompoundRegion.");
143 }
144 ++buffer;
146 while (buffer != end) {
147 std::uint64_t nBytes = consumeDecodeU64(buffer, end);
148 if (buffer + nBytes > end) {
149 throw std::runtime_error("Encoded CompoundRegion is truncated.");
150 }
151 result.push_back(Region::decode(buffer, nBytes));
152 buffer += nBytes;
153 }
154 return result;
155}
156
158 if (n == 0) {
159 throw std::runtime_error("Encoded CompoundRegion is truncated.");
160 }
161 switch (buffer[0]) {
163 return UnionRegion::decode(buffer, n);
165 return IntersectionRegion::decode(buffer, n);
166 default:
167 throw std::runtime_error("Byte string is not an encoded CompoundRegion.");
168 }
169}
170
172 : CompoundRegion(std::move(operands))
173{
174 flatten_operands<UnionRegion>();
175}
176
178 // It can be empty when there are no operands or all operands are empty.
179 for (auto&& operand: operands()) {
180 if (not operand->isEmpty()) {
181 return false;
182 }
183 }
184 return true;
185}
186
188 return getUnionBounds(*this, [](Region const &r) { return r.getBoundingBox(); });
189}
190
192 return getUnionBounds(*this, [](Region const &r) { return r.getBoundingBox3d(); });
193}
194
196 return getUnionBounds(*this, [](Region const &r) { return r.getBoundingCircle(); });
197}
198
200 for (auto&& operand: operands()) {
201 if (operand->contains(v)) {
202 return true;
203 }
204 }
205 return false;
206}
207
209 if (nOperands() == 0) {
210 return DISJOINT;
211 }
212 auto result = DISJOINT | WITHIN;
213 // When result becomes CONTAINS we can stop checking.
214 auto const stop = CONTAINS;
215 for (auto&& operand: operands()) {
216 auto rel = operand->relate(rhs);
217 // All operands must be disjoint with the given region for the union
218 // to be disjoint with it.
219 if ((rel & DISJOINT) != DISJOINT) {
220 result &= ~DISJOINT;
221 }
222 // All operands must be within the given region for the union to be
223 // within it.
224 if ((rel & WITHIN) != WITHIN) {
225 result &= ~WITHIN;
226 }
227 // If any operand contains the given region, the union contains it.
228 if ((rel & CONTAINS) == CONTAINS) {
229 result |= CONTAINS;
230 }
231 if (result == stop) {
232 break;
233 }
234 }
235
236 return result;
237}
238
240 // Union overlaps if any operand overlaps, and disjoint when all are
241 // disjoint. Empty union is disjoint with anyhting.
242 if (nOperands() == 0) {
243 return TriState(false);
244 }
245 bool may_overlap = false;
246 for (auto&& operand: operands()) {
247 auto state = operand->overlaps(other);
248 if (state == true) {
249 // Definitely overlap.
250 return TriState(true);
251 } if (not state.known()) {
252 // May or may not overlap.
253 may_overlap = true;
254 }
255 }
256 if (may_overlap) {
257 return TriState();
258 }
259 // None overlaps.
260 return TriState(false);
261}
262
264 return overlaps(static_cast<Region const&>(b));
265}
266
268 return overlaps(static_cast<Region const&>(c));
269}
270
272 return overlaps(static_cast<Region const&>(p));
273}
274
276 return overlaps(static_cast<Region const&>(e));
277}
278
280 : CompoundRegion(std::move(operands))
281{
282 flatten_operands<IntersectionRegion>();
283}
284
286 // Intersection is harder to decide - the only clear case is when there are
287 // no operands, which we declare to be equivalent to full sphere. Other
288 // clear case is when all operands are empty.
289 if (nOperands() == 0) {
290 return false;
291 }
292 if (std::all_of(
293 operands().begin(), operands().end(), [](auto const& operand) { return operand->isEmpty(); }
294 )) {
295 return true;
296 }
297 // Another test is for when any operand is disjoint with any other operands.
298 auto begin = operands().begin();
299 auto const end = operands().end();
300 for (auto op1 = begin; op1 != end; ++ op1) {
301 for (auto op2 = op1 + 1; op2 != end; ++ op2) {
302 if ((*op1)->overlaps(**op2) == false) {
303 return true;
304 }
305 }
306 }
307 // Still may be empty but hard to guess.
308 return false;
309}
310
312 return getIntersectionBounds(*this, [](Region const &r) { return r.getBoundingBox(); });
313}
314
316 return getIntersectionBounds(*this, [](Region const &r) { return r.getBoundingBox3d(); });
317}
318
320 return getIntersectionBounds(*this, [](Region const &r) { return r.getBoundingCircle(); });
321}
322
324 for (auto&& operand: operands()) {
325 if (not operand->contains(v)) {
326 return false;
327 }
328 }
329 return true;
330}
331
333 auto result = CONTAINS;
334 // When result becomes DISJOINT | WITHIN we can stop checking.
335 auto const stop = DISJOINT | WITHIN;
336 for (auto&& operand: operands()) {
337 auto rel = operand->relate(rhs);
338 // All operands must contain the given region for the intersection to
339 // contain it.
340 if ((rel & CONTAINS) != CONTAINS) {
341 result &= ~CONTAINS;
342 }
343 // If any operand is disjoint with the given region, the
344 // intersection is disjoint with it.
345 if ((rel & DISJOINT) == DISJOINT) {
346 result |= DISJOINT;
347 }
348 // If any operand is within the given region, the intersection is
349 // within it.
350 if ((rel & WITHIN) == WITHIN) {
351 result |= WITHIN;
352 }
353 if (result == stop) {
354 break;
355 }
356 }
357
358 return result;
359}
360
362 // Intersection case is harder, difficult to guess "definitely overlaps"
363 // without building actual overlap region. It is easier to check for
364 // disjoint - if any operand is disjoint then intersection is disjoint too.
365 if (nOperands() == 0) {
366 // Empty intersection is equivalent to whole sphere, so it should
367 // overlap anything, but there is case of empty regions that overlap
368 // nothing.
369 return TriState(not other.isEmpty());
370 }
371
372 for (auto&& operand: operands()) {
373 auto state = operand->overlaps(other);
374 if (state == false) {
375 return TriState(false);
376 }
377 }
378 // Not disjoint, but may or may not overlap.
379 return TriState();
380}
381
383 return overlaps(static_cast<Region const&>(b));
384}
385
387 return overlaps(static_cast<Region const&>(c));
388}
389
391 return overlaps(static_cast<Region const&>(p));
392}
393
395 return overlaps(static_cast<Region const&>(e));
396}
397
398} // namespace sphgeom
399} // namespace lsst
py::object result
Definition _schema.cc:429
int end
This file declares a class for representing axis-aligned bounding boxes in ℝ³.
This file declares a class for representing circular regions on the unit sphere.
This file declares classes for representing compound regions on the unit sphere.
This file declares a class for representing convex polygons with great circle edges on the unit spher...
table::Key< int > b
T all_of(T... args)
Box3d represents a box in ℝ³.
Definition Box3d.h:49
Box represents a rectangle in spherical coordinate space that contains its boundary.
Definition Box.h:62
static Box empty()
Definition Box.h:77
static Box full()
Definition Box.h:79
Circle is a circular region on the unit sphere that contains its boundary.
Definition Circle.h:54
CompoundRegion is an intermediate base class for spherical regions that are comprised of a point-set ...
virtual Relationship relate(Region const &r) const =0
std::vector< std::unique_ptr< Region > > const & operands() const
std::vector< std::uint8_t > _encode(std::uint8_t tc) const
CompoundRegion(std::vector< std::unique_ptr< Region > > operands) noexcept
Construct by taking ownership of operands.
static std::unique_ptr< CompoundRegion > decode(std::vector< std::uint8_t > const &s)
static std::vector< std::unique_ptr< Region > > _decode(std::uint8_t tc, std::uint8_t const *buffer, std::size_t nBytes)
ConvexPolygon is a closed convex polygon on the unit sphere.
Ellipse is an elliptical region on the sphere.
Definition Ellipse.h:177
Relationship relate(Region const &r) const override
IntersectionRegion(std::vector< std::unique_ptr< Region > > operands)
Construct by taking ownership of operands.
static std::unique_ptr< IntersectionRegion > decode(std::vector< std::uint8_t > const &s)
static constexpr std::uint8_t TYPE_CODE
Circle getBoundingCircle() const override
getBoundingCircle returns a bounding-circle for this region.
bool isEmpty() const override
isEmpty returns true when a region does not contain any points.
Box3d getBoundingBox3d() const override
getBoundingBox3d returns a 3-dimensional bounding-box for this region.
bool contains(UnitVector3d const &v) const override
contains tests whether the given unit vector is inside this region.
Box getBoundingBox() const override
getBoundingBox returns a bounding-box for this region.
TriState overlaps(Region const &other) const override
Region is a minimal interface for 2-dimensional regions on the unit sphere.
Definition Region.h:89
static std::unique_ptr< Region > decode(std::vector< std::uint8_t > const &s)
Definition Region.h:162
virtual bool isEmpty() const =0
isEmpty returns true when a region does not contain any points.
TriState represents a boolean value with additional unknown state.
Definition TriState.h:46
Box getBoundingBox() const override
getBoundingBox returns a bounding-box for this region.
static constexpr std::uint8_t TYPE_CODE
TriState overlaps(Region const &other) const override
bool isEmpty() const override
isEmpty returns true when a region does not contain any points.
Relationship relate(Region const &r) const override
bool contains(UnitVector3d const &v) const override
contains tests whether the given unit vector is inside this region.
UnionRegion(std::vector< std::unique_ptr< Region > > operands)
Construct by taking ownership of operands.
Circle getBoundingCircle() const override
getBoundingCircle returns a bounding-circle for this region.
static std::unique_ptr< UnionRegion > decode(std::vector< std::uint8_t > const &s)
Box3d getBoundingBox3d() const override
getBoundingBox3d returns a 3-dimensional bounding-box for this region.
UnitVector3d is a unit vector in ℝ³ with components stored in double precision.
This file contains simple helper functions for encoding and decoding primitive types to/from byte str...
T end(T... args)
T insert(T... args)
T inserter(T... args)
T move(T... args)
std::uint64_t decodeU64(std::uint8_t const *buffer)
decodeU64 extracts an uint64 from the 8 byte little-endian byte sequence in buffer.
Definition codec.h:116
void encodeU64(std::uint64_t item, std::vector< std::uint8_t > &buffer)
encodeU64 appends an uint64 in little-endian byte order to the end of buffer.
Definition codec.h:96
STL namespace.
T push_back(T... args)
This file declares a class for representing longitude/latitude angle boxes on the unit sphere.
This file declares a class for representing elliptical regions on the unit sphere.