1use crate::{about_eq_hash, about_eq_ord};
2
3use super::{EQ_GRANULARITY, EQ_GRANULARITY_100, Factor, about_eq};
4
5use std::{
6 f32::consts::{PI, TAU},
7 fmt, ops,
8};
9
10#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
18#[serde(transparent)]
19pub struct AngleRadian(pub f32);
20impl ops::Add for AngleRadian {
21 type Output = Self;
22
23 fn add(self, rhs: Self) -> Self::Output {
24 Self(self.0 + rhs.0)
25 }
26}
27impl ops::AddAssign for AngleRadian {
28 fn add_assign(&mut self, rhs: Self) {
29 self.0 += rhs.0;
30 }
31}
32impl ops::Sub for AngleRadian {
33 type Output = Self;
34
35 fn sub(self, rhs: Self) -> Self::Output {
36 Self(self.0 - rhs.0)
37 }
38}
39impl ops::SubAssign for AngleRadian {
40 fn sub_assign(&mut self, rhs: Self) {
41 self.0 -= rhs.0;
42 }
43}
44impl ops::Neg for AngleRadian {
45 type Output = Self;
46
47 fn neg(self) -> Self::Output {
48 Self(-self.0)
49 }
50}
51impl AngleRadian {
52 pub fn modulo(self) -> Self {
54 AngleRadian(self.0.rem_euclid(TAU))
55 }
56
57 pub fn lerp(self, to: Self, factor: Factor) -> Self {
59 Self(lerp(self.0, to.0, factor))
60 }
61
62 pub fn slerp(self, to: Self, factor: Factor) -> Self {
72 Self(slerp(self.0, to.0, TAU, factor))
73 }
74}
75
76impl PartialEq for AngleRadian {
77 fn eq(&self, other: &Self) -> bool {
78 about_eq(self.0, other.0, EQ_GRANULARITY)
79 }
80}
81impl Eq for AngleRadian {}
82impl std::hash::Hash for AngleRadian {
83 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
84 about_eq_hash(self.0, EQ_GRANULARITY, state);
85 }
86}
87impl Ord for AngleRadian {
88 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
89 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
90 }
91}
92impl PartialOrd for AngleRadian {
93 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98impl From<AngleGradian> for AngleRadian {
99 fn from(grad: AngleGradian) -> Self {
100 AngleRadian(grad.0 * PI / 200.0)
101 }
102}
103impl From<AngleDegree> for AngleRadian {
104 fn from(deg: AngleDegree) -> Self {
105 AngleRadian(deg.0.to_radians())
106 }
107}
108impl From<AngleTurn> for AngleRadian {
109 fn from(turn: AngleTurn) -> Self {
110 AngleRadian(turn.0 * TAU)
111 }
112}
113impl From<AngleRadian> for euclid::Angle<f32> {
114 fn from(rad: AngleRadian) -> Self {
115 euclid::Angle::radians(rad.0)
116 }
117}
118
119impl fmt::Debug for AngleRadian {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 if f.alternate() {
122 f.debug_tuple("AngleRadian").field(&self.0).finish()
123 } else {
124 write!(f, "{}.rad()", self.0)
125 }
126 }
127}
128impl fmt::Display for AngleRadian {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "{} rad", self.0)
131 }
132}
133
134impl std::str::FromStr for AngleRadian {
136 type Err = std::num::ParseFloatError;
137
138 fn from_str(s: &str) -> Result<Self, Self::Err> {
139 crate::parse_suffix(s, &[" rad", "rad", ".rad()"]).map(AngleRadian)
140 }
141}
142
143#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
151#[serde(transparent)]
152pub struct AngleGradian(pub f32);
153impl AngleGradian {
154 pub fn modulo(self) -> Self {
156 AngleGradian(self.0.rem_euclid(400.0))
157 }
158
159 pub fn lerp(self, to: Self, factor: Factor) -> Self {
161 Self(lerp(self.0, to.0, factor))
162 }
163
164 pub fn slerp(self, to: Self, factor: Factor) -> Self {
174 Self(slerp(self.0, to.0, 400.0, factor))
175 }
176}
177impl ops::Add for AngleGradian {
178 type Output = Self;
179
180 fn add(self, rhs: Self) -> Self::Output {
181 Self(self.0 + rhs.0)
182 }
183}
184impl ops::AddAssign for AngleGradian {
185 fn add_assign(&mut self, rhs: Self) {
186 self.0 += rhs.0;
187 }
188}
189impl ops::Sub for AngleGradian {
190 type Output = Self;
191
192 fn sub(self, rhs: Self) -> Self::Output {
193 Self(self.0 - rhs.0)
194 }
195}
196impl ops::SubAssign for AngleGradian {
197 fn sub_assign(&mut self, rhs: Self) {
198 self.0 -= rhs.0;
199 }
200}
201impl ops::Neg for AngleGradian {
202 type Output = Self;
203
204 fn neg(self) -> Self::Output {
205 Self(-self.0)
206 }
207}
208impl Ord for AngleGradian {
209 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
210 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
211 }
212}
213impl PartialOrd for AngleGradian {
214 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
215 Some(self.cmp(other))
216 }
217}
218
219impl PartialEq for AngleGradian {
220 fn eq(&self, other: &Self) -> bool {
221 about_eq(self.0, other.0, EQ_GRANULARITY_100)
222 }
223}
224impl Eq for AngleGradian {}
225impl std::hash::Hash for AngleGradian {
226 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
227 about_eq_hash(self.0, EQ_GRANULARITY_100, state);
228 }
229}
230impl From<AngleRadian> for AngleGradian {
231 fn from(rad: AngleRadian) -> Self {
232 AngleGradian(rad.0 * 200.0 / PI)
233 }
234}
235impl From<AngleDegree> for AngleGradian {
236 fn from(deg: AngleDegree) -> Self {
237 AngleGradian(deg.0 * 10.0 / 9.0)
238 }
239}
240impl From<AngleTurn> for AngleGradian {
241 fn from(turn: AngleTurn) -> Self {
242 AngleGradian(turn.0 * 400.0)
243 }
244}
245impl fmt::Debug for AngleGradian {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 if f.alternate() {
248 f.debug_tuple("AngleGradian").field(&self.0).finish()
249 } else {
250 write!(f, "{}.grad()", self.0)
251 }
252 }
253}
254impl fmt::Display for AngleGradian {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 write!(f, "{} gon", self.0)
257 }
258}
259impl std::str::FromStr for AngleGradian {
261 type Err = std::num::ParseFloatError;
262
263 fn from_str(s: &str) -> Result<Self, Self::Err> {
264 crate::parse_suffix(s, &[" gon", "gon", ".grad()"]).map(AngleGradian)
265 }
266}
267
268#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
276#[serde(transparent)]
277pub struct AngleDegree(pub f32);
278impl AngleDegree {
279 pub fn modulo(self) -> Self {
281 AngleDegree(self.0.rem_euclid(360.0))
282 }
283
284 pub fn lerp(self, to: Self, factor: Factor) -> Self {
286 Self(lerp(self.0, to.0, factor))
287 }
288
289 pub fn slerp(self, to: Self, factor: Factor) -> Self {
299 Self(slerp(self.0, to.0, 360.0, factor))
300 }
301}
302impl ops::Add for AngleDegree {
303 type Output = Self;
304
305 fn add(self, rhs: Self) -> Self::Output {
306 Self(self.0 + rhs.0)
307 }
308}
309impl ops::AddAssign for AngleDegree {
310 fn add_assign(&mut self, rhs: Self) {
311 self.0 += rhs.0;
312 }
313}
314impl ops::Sub for AngleDegree {
315 type Output = Self;
316
317 fn sub(self, rhs: Self) -> Self::Output {
318 Self(self.0 - rhs.0)
319 }
320}
321impl ops::SubAssign for AngleDegree {
322 fn sub_assign(&mut self, rhs: Self) {
323 self.0 -= rhs.0;
324 }
325}
326impl ops::Neg for AngleDegree {
327 type Output = Self;
328
329 fn neg(self) -> Self::Output {
330 Self(-self.0)
331 }
332}
333
334impl PartialEq for AngleDegree {
335 fn eq(&self, other: &Self) -> bool {
336 about_eq(self.0, other.0, EQ_GRANULARITY_100)
337 }
338}
339impl Eq for AngleDegree {}
340impl Ord for AngleDegree {
341 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
342 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
343 }
344}
345impl PartialOrd for AngleDegree {
346 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
347 Some(self.cmp(other))
348 }
349}
350impl std::hash::Hash for AngleDegree {
351 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
352 about_eq_hash(self.0, EQ_GRANULARITY_100, state);
353 }
354}
355impl From<AngleRadian> for AngleDegree {
356 fn from(rad: AngleRadian) -> Self {
357 AngleDegree(rad.0.to_degrees())
358 }
359}
360impl From<AngleGradian> for AngleDegree {
361 fn from(grad: AngleGradian) -> Self {
362 AngleDegree(grad.0 * 9.0 / 10.0)
363 }
364}
365impl From<AngleTurn> for AngleDegree {
366 fn from(turn: AngleTurn) -> Self {
367 AngleDegree(turn.0 * 360.0)
368 }
369}
370impl fmt::Debug for AngleDegree {
371 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372 if f.alternate() {
373 f.debug_tuple("AngleDegree").field(&self.0).finish()
374 } else {
375 write!(f, "{}.deg()", self.0)
376 }
377 }
378}
379impl fmt::Display for AngleDegree {
380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381 write!(f, "{}º", self.0)
382 }
383}
384impl std::str::FromStr for AngleDegree {
386 type Err = std::num::ParseFloatError;
387
388 fn from_str(s: &str) -> Result<Self, Self::Err> {
389 crate::parse_suffix(s, &["º", ".deg()"]).map(AngleDegree)
390 }
391}
392
393#[derive(Copy, Clone, serde::Serialize, serde::Deserialize)]
401#[serde(transparent)]
402pub struct AngleTurn(pub f32);
403impl AngleTurn {
404 pub fn modulo(self) -> Self {
406 AngleTurn(self.0.rem_euclid(1.0))
407 }
408
409 pub fn lerp(self, to: Self, factor: Factor) -> Self {
411 Self(lerp(self.0, to.0, factor))
412 }
413
414 pub fn slerp(self, to: Self, factor: Factor) -> Self {
424 Self(slerp(self.0, to.0, 1.0, factor))
425 }
426}
427impl ops::Add for AngleTurn {
428 type Output = Self;
429
430 fn add(self, rhs: Self) -> Self::Output {
431 Self(self.0 + rhs.0)
432 }
433}
434impl ops::AddAssign for AngleTurn {
435 fn add_assign(&mut self, rhs: Self) {
436 self.0 += rhs.0;
437 }
438}
439impl ops::Sub for AngleTurn {
440 type Output = Self;
441
442 fn sub(self, rhs: Self) -> Self::Output {
443 Self(self.0 - rhs.0)
444 }
445}
446impl ops::SubAssign for AngleTurn {
447 fn sub_assign(&mut self, rhs: Self) {
448 self.0 -= rhs.0;
449 }
450}
451impl ops::Neg for AngleTurn {
452 type Output = Self;
453
454 fn neg(self) -> Self::Output {
455 Self(-self.0)
456 }
457}
458
459impl fmt::Debug for AngleTurn {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 if f.alternate() {
462 f.debug_tuple("AngleTurn").field(&self.0).finish()
463 } else {
464 write!(f, "{}.turn()", self.0)
465 }
466 }
467}
468impl fmt::Display for AngleTurn {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 if (self.0 - 1.0).abs() < 0.0001 {
471 write!(f, "1 turn")
472 } else {
473 write!(f, "{} turns", self.0)
474 }
475 }
476}
477impl std::str::FromStr for AngleTurn {
479 type Err = std::num::ParseFloatError;
480
481 fn from_str(s: &str) -> Result<Self, Self::Err> {
482 crate::parse_suffix(s, &[" turn", " turns", "turn", "turns", ".turn()"]).map(AngleTurn)
483 }
484}
485impl PartialEq for AngleTurn {
486 fn eq(&self, other: &Self) -> bool {
487 about_eq(self.0, other.0, EQ_GRANULARITY)
488 }
489}
490impl Eq for AngleTurn {}
491impl Ord for AngleTurn {
492 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
493 about_eq_ord(self.0, other.0, EQ_GRANULARITY)
494 }
495}
496impl PartialOrd for AngleTurn {
497 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
498 Some(self.cmp(other))
499 }
500}
501impl std::hash::Hash for AngleTurn {
502 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
503 about_eq_hash(self.0, EQ_GRANULARITY, state);
504 }
505}
506
507impl From<AngleRadian> for AngleTurn {
508 fn from(rad: AngleRadian) -> Self {
509 AngleTurn(rad.0 / TAU)
510 }
511}
512impl From<AngleGradian> for AngleTurn {
513 fn from(grad: AngleGradian) -> Self {
514 AngleTurn(grad.0 / 400.0)
515 }
516}
517impl From<AngleDegree> for AngleTurn {
518 fn from(deg: AngleDegree) -> Self {
519 AngleTurn(deg.0 / 360.0)
520 }
521}
522
523pub trait AngleUnits {
537 fn rad(self) -> AngleRadian;
539 fn grad(self) -> AngleGradian;
541 fn deg(self) -> AngleDegree;
543 fn turn(self) -> AngleTurn;
545}
546impl AngleUnits for f32 {
547 fn rad(self) -> AngleRadian {
548 AngleRadian(self)
549 }
550
551 fn grad(self) -> AngleGradian {
552 AngleGradian(self)
553 }
554
555 fn deg(self) -> AngleDegree {
556 AngleDegree(self)
557 }
558
559 fn turn(self) -> AngleTurn {
560 AngleTurn(self)
561 }
562}
563impl AngleUnits for i32 {
564 fn rad(self) -> AngleRadian {
565 AngleRadian(self as f32)
566 }
567
568 fn grad(self) -> AngleGradian {
569 AngleGradian(self as f32)
570 }
571
572 fn deg(self) -> AngleDegree {
573 AngleDegree(self as f32)
574 }
575
576 fn turn(self) -> AngleTurn {
577 AngleTurn(self as f32)
578 }
579}
580
581fn lerp(from: f32, to: f32, factor: Factor) -> f32 {
582 from + (to - from) * factor.0
583}
584
585fn slerp(from: f32, to: f32, turn: f32, factor: Factor) -> f32 {
586 let angle_to = {
587 let d = (to - from) % turn;
588 2.0 * d % turn - d
589 };
590 from + angle_to * factor.0
591}