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