lsst.geom main-g55b2fe3290+4a00534000
Angle.h
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#ifndef LSST_GEOM_ANGLE_H
22#define LSST_GEOM_ANGLE_H
23
24#include <cmath>
25#include <iostream>
26#include <type_traits>
27
28#include "boost/math/constants/constants.hpp"
29
30namespace lsst {
31namespace geom {
32
33/*
34 * None of C99, C++98, and C++11 define M_PI, so we'll do it ourselves
35 */
36#pragma clang diagnostic push
37#pragma clang diagnostic ignored "-Wunused-variable"
39double constexpr PI = boost::math::constants::pi<double>();
40double constexpr TWOPI = boost::math::constants::pi<double>() * 2.0;
41double constexpr HALFPI = boost::math::constants::pi<double>() * 0.5;
42double constexpr ONE_OVER_PI = 1.0 / boost::math::constants::pi<double>();
43// sqrt is not a constexpr on OS X
44double const SQRTPI = sqrt(boost::math::constants::pi<double>());
45double const INVSQRTPI = 1.0 / sqrt(boost::math::constants::pi<double>());
46double constexpr ROOT2 = boost::math::constants::root_two<double>(); // sqrt(2)
47#pragma clang diagnostic pop
48
49// These shouldn't be necessary if the Angle class is used, but sometimes you just need
50// them. Better to define them once here than have *180/PI throughout the code...
51inline constexpr double degToRad(double x) noexcept { return x * PI / 180.; }
52inline constexpr double radToDeg(double x) noexcept { return x * 180. / PI; }
53inline constexpr double radToArcsec(double x) noexcept { return x * 3600. * 180. / PI; }
54inline constexpr double radToMas(double x) noexcept { return x * 1000. * 3600. * 180. / PI; }
55inline constexpr double arcsecToRad(double x) noexcept { return (x / 3600.) * PI / 180.; }
56inline constexpr double masToRad(double x) noexcept { return (x / (1000. * 3600.)) * PI / 180.; }
57
58class Angle;
70class AngleUnit final {
71 friend class Angle;
72 template <typename T>
73 friend constexpr Angle operator*(T lhs, AngleUnit rhs) noexcept;
74
75public:
83 // Current implementation does not throw exceptions, but somebody may want
84 // to add input validation later
85 explicit constexpr AngleUnit(double val) : _val(val) {}
86
95 constexpr bool operator==(AngleUnit const& rhs) const noexcept;
96
98 std::size_t hash_value() const noexcept { return std::hash<double>()(_val); }
99
100private:
101 double _val;
102};
103
104inline constexpr bool AngleUnit::operator==(AngleUnit const& rhs) const noexcept {
105 return (_val == rhs._val);
106}
107
108AngleUnit constexpr radians = AngleUnit(1.0);
109AngleUnit constexpr degrees = AngleUnit(PI / 180.0);
110AngleUnit constexpr hours = AngleUnit(PI * 15.0 / 180.0);
111AngleUnit constexpr arcminutes = AngleUnit(PI / 60 / 180.0);
112AngleUnit constexpr arcseconds = AngleUnit(PI / 180.0 / 3600.0);
113// Note: if we use PI / 180.0 / 3.6e6 then 60*60*180*1000*lsst.geom.milliarcseconds
114// does not test exactly equal to 180*lsst.geom.degrees
116 AngleUnit(PI / (180.0 * 3.6e6));
117
127class Angle final {
128 friend class AngleUnit;
129
130public:
136 explicit constexpr Angle(double val, AngleUnit units = radians) noexcept : _val(val * units._val) {}
137
139 constexpr Angle() noexcept : _val(0) {}
140
142 constexpr Angle(Angle const& other) noexcept = default;
143
145 constexpr Angle(Angle&& other) noexcept = default;
146
148 Angle& operator=(Angle const& other) noexcept = default;
149
151 Angle& operator=(Angle&& other) noexcept = default;
152
153 ~Angle() = default;
154
156 constexpr operator double() const noexcept { return _val; }
157
163 constexpr double asAngularUnits(AngleUnit const& units) const noexcept { return _val / units._val; }
164
166 constexpr double asRadians() const noexcept { return asAngularUnits(radians); }
167
169 constexpr double asDegrees() const noexcept { return asAngularUnits(degrees); }
170
172 constexpr double asHours() const noexcept { return asAngularUnits(hours); }
173
175 constexpr double asArcminutes() const noexcept { return asAngularUnits(arcminutes); }
176
178 constexpr double asArcseconds() const noexcept { return asAngularUnits(arcseconds); }
179
181 constexpr double asMilliarcseconds() const noexcept { return asAngularUnits(milliarcseconds); }
182
195 Angle wrap() const noexcept;
196
209 Angle wrapCtr() const noexcept;
210
223 Angle wrapNear(Angle const& refAng) const noexcept;
224
233 Angle separation(Angle const& other) const noexcept;
234
235#define ANGLE_OPUP_TYPE(OP, TYPE) \
236 Angle& operator OP(TYPE const& d) noexcept { \
237 _val OP d; \
238 return *this; \
239 }
240
242
243 ANGLE_OPUP_TYPE(*=, double)
246
247
248 ANGLE_OPUP_TYPE(+=, double)
251
252
253 ANGLE_OPUP_TYPE(-=, double)
256
257#undef ANGLE_OPUP_TYPE
258
259#define ANGLE_COMP(OP) \
260 constexpr bool operator OP(const Angle& rhs) const noexcept { return _val OP rhs._val; }
261
263
267
268
274
275#undef ANGLE_COMP
276
278 std::size_t hash_value() const noexcept { return std::hash<double>()(_val); }
279
280private:
281 double _val;
282};
283
284/*
285 * Operators for Angles.
286 */
287#define ANGLE_OP(OP) \
288 inline constexpr Angle operator OP(Angle a, Angle d) noexcept { \
289 return Angle(static_cast<double>(a) OP static_cast<double>(d)); \
290 }
291
292// We need both int and double versions to avoid ambiguous overloading due to
293// implicit conversion of Angle to double
294#define ANGLE_OP_TYPE(OP, TYPE) \
295 inline constexpr Angle operator OP(Angle a, TYPE d) noexcept { \
296 return Angle(static_cast<double>(a) OP d); \
297 } \
298 \
299 inline constexpr Angle operator OP(TYPE d, Angle a) noexcept { \
300 return Angle(d OP static_cast<double>(a)); \
301 }
302
324
332
333#undef ANGLE_OP
334#undef ANGLE_OP_TYPE
335
341inline constexpr Angle operator-(Angle angle) { return Angle(-static_cast<double>(angle)); }
342
343// Apparently @relatesalso doesn't work with grouping
349inline constexpr Angle operator/(Angle a, int d) noexcept { return Angle(static_cast<double>(a) / d); }
350
356inline constexpr Angle operator/(Angle a, double d) noexcept { return Angle(static_cast<double>(a) / d); }
357
358// Division is different. Don't allow division by an Angle
359template <typename T>
360constexpr double operator/(T const lhs, Angle rhs) noexcept = delete;
361
377
379template <typename T>
380inline constexpr bool isAngle(T) noexcept {
382};
383
394template <typename T>
395inline constexpr Angle operator*(T lhs, AngleUnit rhs) noexcept {
396 static_assert(std::is_arithmetic<T>::value,
397 "Only numeric types may be multiplied by an AngleUnit to create an Angle!");
398 return Angle(lhs * rhs._val);
399}
400
401// Inline method definitions, placed last in order to benefit from Angle's full API
402
403inline Angle Angle::wrap() const noexcept {
404 double wrapped = std::fmod(_val, TWOPI);
405 // wrapped is in the range (-TWOPI, TWOPI)
406 if (wrapped < 0.0) wrapped += TWOPI;
407 // if wrapped is small enough, adding 2 pi gives 2 pi
408 if (wrapped >= TWOPI) wrapped = 0.0;
409 return wrapped * radians;
410}
411
412inline Angle Angle::wrapCtr() const noexcept {
413 double wrapped = std::fmod(_val, TWOPI);
414 // wrapped is in the range [-TWOPI, TWOPI]
415 if (wrapped < -PI) {
416 wrapped += TWOPI;
417 if (wrapped >= PI) {
418 // handle roundoff error, however unlikely
419 wrapped = -PI;
420 }
421 } else if (wrapped >= PI) {
422 wrapped -= TWOPI;
423 if (wrapped < -PI) {
424 // handle roundoff error, however unlikely
425 wrapped = -PI;
426 }
427 }
428 return wrapped * radians;
429}
430
431inline Angle Angle::wrapNear(Angle const& refAng) const noexcept {
432 // compute (this - refAng).wrapCtr() + refAng
433 // which is correct except for roundoff error at the edges
434 double const refAngRad = refAng.asRadians();
435 double wrapped = (*this - refAng).wrapCtr().asRadians() + refAngRad;
436
437 // roundoff can cause slightly out-of-range values; fix those as bast we can;
438 // note that both conditionals are wanted, since either or both may execute
439 // (though if/else could be used if the lower limit was squishy for radians)
440 if (wrapped - refAngRad >= PI) {
441 wrapped -= TWOPI;
442 }
443 if (wrapped - refAngRad < -PI) {
444 wrapped += TWOPI;
445 }
446 return wrapped * radians;
447}
448
449inline Angle Angle::separation(Angle const& other) const noexcept { return (*this - other).wrapCtr(); }
450
451} // namespace geom
452} // namespace lsst
453
454namespace std {
455template <>
456struct hash<lsst::geom::AngleUnit> {
459 size_t operator()(argument_type const& x) const noexcept { return x.hash_value(); }
460};
461
462template <>
463struct hash<lsst::geom::Angle> {
466 size_t operator()(argument_type const& x) const noexcept { return x.hash_value(); }
467};
468} // namespace std
469
470#endif // if !defined(LSST_GEOM_ANGLE_H)
#define ANGLE_OPUP_TYPE(OP, TYPE)
Definition: Angle.h:235
#define ANGLE_OP_TYPE(OP, TYPE)
Definition: Angle.h:294
#define ANGLE_OP(OP)
Definition: Angle.h:287
#define ANGLE_COMP(OP)
Definition: Angle.h:259
A class representing an angle.
Definition: Angle.h:127
Angle wrapCtr() const noexcept
Wrap this angle to the range [-π, π).
Definition: Angle.h:412
constexpr Angle(Angle const &other) noexcept=default
Copy constructor.
constexpr double asRadians() const noexcept
Return an Angle's value in radians.
Definition: Angle.h:166
constexpr double asHours() const noexcept
Return an Angle's value in hours.
Definition: Angle.h:172
Angle separation(Angle const &other) const noexcept
The signed difference between two Angles.
Definition: Angle.h:449
constexpr Angle(double val, AngleUnit units=radians) noexcept
Construct an Angle with the specified value (interpreted in the given units).
Definition: Angle.h:136
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Angle.h:278
constexpr Angle() noexcept
Construct the zero angle.
Definition: Angle.h:139
constexpr Angle operator/(Angle a, double d) noexcept
Ratio of an angle and a scalar.
Definition: Angle.h:356
constexpr double asDegrees() const noexcept
Return an Angle's value in degrees.
Definition: Angle.h:169
constexpr double asMilliarcseconds() const noexcept
Return an Angle's value in milliarcseconds.
Definition: Angle.h:181
constexpr double asArcseconds() const noexcept
Return an Angle's value in arcseconds.
Definition: Angle.h:178
constexpr Angle operator/(Angle a, int d) noexcept
Ratio of an angle and a scalar.
Definition: Angle.h:349
Angle wrapNear(Angle const &refAng) const noexcept
Wrap this angle to a value x such that -π ≤ x - refAng ≤ π, approximately.
Definition: Angle.h:431
Angle & operator=(Angle const &other) noexcept=default
Copy assignment.
Angle & operator=(Angle &&other) noexcept=default
Move assignment.
Angle wrap() const noexcept
Wrap this angle to the range [0, 2π).
Definition: Angle.h:403
constexpr Angle operator-(Angle angle)
An angle in the opposite sense.
Definition: Angle.h:341
constexpr Angle(Angle &&other) noexcept=default
Move constructor.
constexpr double asAngularUnits(AngleUnit const &units) const noexcept
Return an Angle's value in the specified units.
Definition: Angle.h:163
constexpr double asArcminutes() const noexcept
Return an Angle's value in arcminutes.
Definition: Angle.h:175
A class used to convert scalar POD types such as double to Angle.
Definition: Angle.h:70
constexpr AngleUnit(double val)
Define a new angle unit.
Definition: Angle.h:85
constexpr bool operator==(AngleUnit const &rhs) const noexcept
Test if two units are the same.
Definition: Angle.h:104
std::size_t hash_value() const noexcept
Return a hash of this object.
Definition: Angle.h:98
constexpr friend Angle operator*(T lhs, AngleUnit rhs) noexcept
Use AngleUnit to convert a POD (e.g. int, double) to an Angle; e.g. 180*degrees.
Definition: Angle.h:395
T fmod(T... args)
constexpr Angle operator*(Angle a, Angle d) noexcept
Product of two angles.
Definition: Angle.h:322
double const SQRTPI
Definition: Angle.h:44
constexpr double arcsecToRad(double x) noexcept
Definition: Angle.h:55
double const INVSQRTPI
Definition: Angle.h:45
constexpr double radToMas(double x) noexcept
Definition: Angle.h:54
constexpr double PI
The ratio of a circle's circumference to diameter.
Definition: Angle.h:39
constexpr Angle operator/(Angle a, int d) noexcept
Ratio of an angle and a scalar.
Definition: Angle.h:349
constexpr AngleUnit arcseconds
constant with units of arcseconds
Definition: Angle.h:112
constexpr double HALFPI
Definition: Angle.h:41
constexpr AngleUnit degrees
constant with units of degrees
Definition: Angle.h:109
constexpr double radToDeg(double x) noexcept
Definition: Angle.h:52
constexpr double masToRad(double x) noexcept
Definition: Angle.h:56
constexpr double radToArcsec(double x) noexcept
Definition: Angle.h:53
constexpr double TWOPI
Definition: Angle.h:40
constexpr AngleUnit hours
constant with units of hours
Definition: Angle.h:110
constexpr AngleUnit milliarcseconds
constant with units of milliarcseconds
Definition: Angle.h:115
constexpr double degToRad(double x) noexcept
Definition: Angle.h:51
std::ostream & operator<<(std::ostream &os, lsst::geom::AffineTransform const &transform)
constexpr double ONE_OVER_PI
Definition: Angle.h:42
constexpr double ROOT2
Definition: Angle.h:46
constexpr AngleUnit radians
constant with units of radians
Definition: Angle.h:108
constexpr bool isAngle(T) noexcept
Allow a user to check if they have an angle.
Definition: Angle.h:380
constexpr AngleUnit arcminutes
constant with units of arcminutes
Definition: Angle.h:111
STL namespace.
size_t operator()(argument_type const &x) const noexcept
Definition: Angle.h:466
size_t operator()(argument_type const &x) const noexcept
Definition: Angle.h:459