opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
angle.h
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <cmath>
7#include <compare>
8#include <concepts>
9#include <numbers>
10#include <ostream>
11#include <string_view>
12#include <type_traits>
13
14namespace osc
15{
16 // satisfied if `T` can be used as a type trait that describes angular units (e.g. radians)
17 template<typename T>
18 concept AngularUnitTraits = requires(T) {
19 { T::radians_per_rep } -> std::convertible_to<double>;
20 { T::unit_label } -> std::convertible_to<std::string_view>;
21 };
22
23 // a floating point number of type `Rep`, expressed in the given `Units`
24 template<std::floating_point Rep, AngularUnitTraits Units>
25 class Angle final {
26 public:
27 using value_type = Rep;
28
29 constexpr Angle() = default;
30
31 // explicitly constructs the angle from a raw value in the given units
32 template<std::convertible_to<Rep> Rep2>
33 explicit constexpr Angle(const Rep2& value_) :
34 value_{static_cast<Rep>(value_)}
35 {}
36
37 // implicitly constructs from an angle with the same representation, but expressed in other units
38 template<AngularUnitTraits Units2>
39 constexpr Angle(const Angle<Rep, Units2>& other) :
40 value_{static_cast<Rep>(other.count() * (Units2::radians_per_rep/Units::radians_per_rep))}
41 {}
42
43 // explicitly constructs from an angle with representation `Rep2`, expressed in other units
44 template<std::convertible_to<Rep> Rep2, AngularUnitTraits Units2>
45 explicit constexpr Angle(const Angle<Rep2, Units2>& other) :
46 value_{static_cast<Rep>(other.count() * (Units2::radians_per_rep/Units::radians_per_rep))}
47 {}
48
49 // returns the underlying floating point representation of the angle
50 constexpr Rep count() const { return value_; }
51
52 constexpr Angle operator+() const { return Angle{*this}; }
53
54 constexpr Angle operator-() const { return Angle{-value_}; }
55
56 constexpr friend auto operator<=>(const Angle&, const Angle&) = default;
57
58 constexpr friend Angle& operator+=(Angle& lhs, const Angle& rhs)
59 {
60 lhs.value_ += rhs.value_;
61 return lhs;
62 }
63
64 constexpr friend Angle& operator-=(Angle& lhs, const Angle& rhs)
65 {
66 lhs.value_ -= rhs.value_;
67 return lhs;
68 }
69
70 // scalar multiplication (both lhs and rhs)
71 template<std::convertible_to<Rep> Rep2>
72 constexpr friend Angle operator*(const Rep2& scalar, const Angle& rhs)
73 {
74 return Angle{static_cast<Rep>(scalar) * rhs.value_};
75 }
76 template<std::convertible_to<Rep> Rep2>
77 constexpr friend Angle operator*(const Angle& lhs, const Rep2& scalar)
78 {
79 return Angle{lhs.value_ * static_cast<Rep>(scalar)};
80 }
81
82 // scalar division (only on the rhs: reciporical angular units aren't supported)
83 template<std::convertible_to<Rep> Rep2>
84 constexpr friend Angle operator/(const Angle& lhs, const Rep2& scalar)
85 {
86 return Angle{lhs.value_ / static_cast<Rep>(scalar)};
87 }
88 private:
89 Rep value_{0};
90 };
91
92 // heterogeneously adds two angles (e.g. `90_deg + 1_turn`) by first converting them to a common angle type
93 template<
94 std::floating_point Rep1,
95 AngularUnitTraits Units1,
96 std::floating_point Rep2,
97 AngularUnitTraits Units2
98 >
99 constexpr typename std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>> operator+(
102 {
103 using CA = std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>>;
104 return CA{CA{lhs}.count() + CA{rhs}.count()};
105 }
106
107 // heterogeneously subtracts two angles (e.g. `90_deg - 1_turn`) by first converting them to a common angle type
108 template<
109 std::floating_point Rep1,
110 AngularUnitTraits Units1,
111 std::floating_point Rep2,
112 AngularUnitTraits Units2
113 >
114 constexpr typename std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>> operator-(
117 {
118 using CA = std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>>;
119 return CA{CA{lhs}.count() - CA{rhs}.count()};
120 }
121
122 // heterogeneously compares two angles (e.g. `360_deg == 1_turn`) by first converting them to a common type
123 template<
124 std::floating_point Rep1,
125 AngularUnitTraits Units1,
126 std::floating_point Rep2,
127 AngularUnitTraits Units2
128 >
130 {
131 using CA = std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>>;
132 return CA{lhs} == CA{rhs};
133 }
134
135 // heterogeneously three-way compares two angles (e.g. `90_deg < 3_rad`) by first converting them to a common angle type
136 template<
137 std::floating_point Rep1,
138 AngularUnitTraits Units1,
139 std::floating_point Rep2,
140 AngularUnitTraits Units2
141 >
143 {
144 using CA = std::common_type_t<Angle<Rep1, Units1>, Angle<Rep2, Units2>>;
145 return CA{lhs} <=> CA{rhs};
146 }
147
148 // writes `angle`'s value, followed by a space, followed by its units (use `.count()` if you just want the value)
149 template<std::floating_point Rep, AngularUnitTraits Units>
150 std::ostream& operator<<(std::ostream& o, const Angle<Rep, Units>& angle)
151 {
152 return o << angle.count() << ' ' << Units::unit_label;
153 }
154
155 // tag `Unorm8` as scalar-like, so that other parts of the codebase (e.g.
156 // vectors, matrices) accept it
157 template<std::floating_point Rep, AngularUnitTraits Units>
158 struct IsScalar<Angle<Rep, Units>> final {
159 static constexpr bool value = true;
160 };
161}
162
163// a specialization of `std::common_type` for `osc::Angle`s
164//
165// (similar to how it's specialized for `std::chrono::duration`)
166template<
167 std::floating_point Rep1,
169 std::floating_point Rep2,
171>
172struct std::common_type<osc::Angle<Rep1, Units1>, osc::Angle<Rep2, Units2>> {
173 // the units of the common type is the "largest" of either
174 using units = typename std::conditional_t<(Units1::radians_per_rep > Units2::radians_per_rep), Units1, Units2>;
176};
177
178
179// unit trait implementations for common units (rad, deg, turn)
180namespace osc
181{
182 // radians
183
185 static inline constexpr double radians_per_rep = 1.0;
186 static inline constexpr std::string_view unit_label = "rad";
187 };
188
189 template<typename T>
193
194 namespace literals
195 {
196 constexpr Radians operator""_rad(long double radians) { return Radians{radians}; }
197 constexpr Radians operator""_rad(unsigned long long int radians) { return Radians{radians}; }
198 }
199
200 // degrees
201
203 static inline constexpr double radians_per_rep = std::numbers::pi_v<double>/180.0;
204 static inline constexpr std::string_view unit_label = "deg";
205 };
206
207 template<typename T>
211
212 namespace literals
213 {
214 constexpr Degrees operator""_deg(long double degrees) { return Degrees{degrees}; }
215 constexpr Degrees operator""_deg(unsigned long long int degrees) { return Degrees{degrees}; }
216 }
217
218 // turns
219
221 static inline constexpr double radians_per_rep = 2.0*std::numbers::pi_v<double>;
222 static inline constexpr std::string_view unit_label = "turn";
223 };
224
225 template<typename T>
229
230 namespace literals
231 {
232 constexpr Turns operator""_turn(long double turns) { return Turns{turns}; }
233 constexpr Turns operator""_turn(unsigned long long int turns) { return Turns{turns}; }
234 }
235}
236
237// common mathematical functions, and algorithms, for angles
238//
239// note: if a generic algorithm is already available in `<algorithm>`, `<ranges>`, or
240// `<oscar/Utils/Algorithms.h>`, then it shouldn't be here: these are specializations
241// for cases that can't be generically handled (e.g. heterogeneous unit coercion)
242namespace osc
243{
244 // `mod` (homogeneous and heterogeneous)
245 template<
246 std::floating_point Rep1,
247 AngularUnitTraits Units1,
248 std::floating_point Rep2,
249 AngularUnitTraits Units2
250 >
251 auto mod(Angle<Rep1, Units1> x, Angle<Rep2, Units2> y) -> std::common_type_t<decltype(x), decltype(y)>
252 {
253 using CA = std::common_type_t<decltype(x), decltype(y)>;
254 return CA{std::fmod(CA{x}.count(), CA{y}.count())};
255 }
256
257 // heterogeneous `min`
258 //
259 // note: homogeneous `min` is provided via `std::ranges::min` or `osc::min` algorithms
260 template<
261 std::floating_point Rep1,
262 AngularUnitTraits Units1,
263 std::floating_point Rep2,
264 AngularUnitTraits Units2
265 >
266 requires (not std::is_same_v<Units1, Units2>)
267 constexpr auto min(Angle<Rep1, Units1> x, Angle<Rep2, Units2> y) -> std::common_type_t<decltype(x), decltype(y)>
268 {
269 using CA = std::common_type_t<decltype(x), decltype(y)>;
270 return CA{std::min(CA{x}.count(), CA{y}.count())};
271 }
272
273 // heterogeneous `max`
274 //
275 // note: homogeneous `max` is provided via `std::ranges::max` or `osc::max` algorithms
276 template<
277 std::floating_point Rep1,
278 AngularUnitTraits Units1,
279 std::floating_point Rep2,
280 AngularUnitTraits Units2
281 >
282 requires (not std::is_same_v<Units1, Units2>)
283 constexpr auto max(Angle<Rep1, Units1> x, Angle<Rep2, Units2> y) -> std::common_type_t<decltype(x), decltype(y)>
284 {
285 using CA = std::common_type_t<decltype(x), decltype(y)>;
286 return CA{std::max(CA{x}.count(), CA{y}.count())};
287 }
288
289 // heterogeneous `clamp`
290 //
291 // note: homogeneous `clamp` is provided via `std::ranges::clamp` or `osc::clamp` algorithms
292 template<
293 std::floating_point Rep,
294 AngularUnitTraits Units,
295 std::convertible_to<Angle<Rep, Units>> AngleMin,
296 std::convertible_to<Angle<Rep, Units>> AngleMax
297 >
298 requires (
299 not std::is_same_v<Angle<Rep, Units>, AngleMin> or
300 not std::is_same_v<Angle<Rep, Units>, AngleMax> or
301 not std::is_same_v<AngleMin, AngleMax>
302 )
304 const Angle<Rep, Units>& v,
305 const AngleMin& min,
306 const AngleMax& max)
307 {
308 return std::clamp(v, Angle<Rep, Units>{min}, Angle<Rep, Units>{max});
309 }
310}
Definition angle.h:25
constexpr friend Angle operator*(const Angle &lhs, const Rep2 &scalar)
Definition angle.h:77
constexpr friend Angle & operator+=(Angle &lhs, const Angle &rhs)
Definition angle.h:58
constexpr Angle(const Angle< Rep2, Units2 > &other)
Definition angle.h:45
constexpr Angle()=default
Rep value_type
Definition angle.h:27
constexpr friend Angle & operator-=(Angle &lhs, const Angle &rhs)
Definition angle.h:64
constexpr Angle operator-() const
Definition angle.h:54
constexpr friend Angle operator/(const Angle &lhs, const Rep2 &scalar)
Definition angle.h:84
constexpr Angle(const Rep2 &value_)
Definition angle.h:33
constexpr Rep count() const
Definition angle.h:50
constexpr friend Angle operator*(const Rep2 &scalar, const Angle &rhs)
Definition angle.h:72
constexpr Angle operator+() const
Definition angle.h:52
constexpr friend auto operator<=>(const Angle &, const Angle &)=default
constexpr Angle(const Angle< Rep, Units2 > &other)
Definition angle.h:39
Definition angle.h:18
Definition custom_decoration_generator.h:5
std::ostream & operator<<(std::ostream &, const Object &)
constexpr auto min(Angle< Rep1, Units1 > x, Angle< Rep2, Units2 > y) -> std::common_type_t< decltype(x), decltype(y)>
Definition angle.h:267
constexpr Angle< Rep, Units > clamp(const Angle< Rep, Units > &v, const AngleMin &min, const AngleMax &max)
Definition angle.h:303
bool operator==(const Class &, const Class &)
constexpr auto max(Angle< Rep1, Units1 > x, Angle< Rep2, Units2 > y) -> std::common_type_t< decltype(x), decltype(y)>
Definition angle.h:283
constexpr std::common_type_t< Angle< Rep1, Units1 >, Angle< Rep2, Units2 > > operator+(const Angle< Rep1, Units1 > &lhs, const Angle< Rep2, Units2 > &rhs)
Definition angle.h:99
auto mod(Angle< Rep1, Units1 > x, Angle< Rep2, Units2 > y) -> std::common_type_t< decltype(x), decltype(y)>
Definition angle.h:251
constexpr U to(T &&value)
Definition conversion.h:56
constexpr auto operator<=>(const Angle< Rep1, Units1 > &lhs, const Angle< Rep2, Units2 > &rhs)
Definition angle.h:142
constexpr std::common_type_t< Angle< Rep1, Units1 >, Angle< Rep2, Units2 > > operator-(const Angle< Rep1, Units1 > &lhs, const Angle< Rep2, Units2 > &rhs)
Definition angle.h:114
Definition angle.h:202
static constexpr std::string_view unit_label
Definition angle.h:204
static constexpr double radians_per_rep
Definition angle.h:203
Definition scalar.h:11
static constexpr bool value
Definition scalar.h:12
Definition angle.h:184
static constexpr std::string_view unit_label
Definition angle.h:186
static constexpr double radians_per_rep
Definition angle.h:185
Definition angle.h:220
static constexpr std::string_view unit_label
Definition angle.h:222
static constexpr double radians_per_rep
Definition angle.h:221
typename std::conditional_t<(Units1::radians_per_rep > Units2::radians_per_rep), Units1, Units2 > units
Definition angle.h:174