1use std::fmt;
25use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
26
27#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Rad(pub f64);
33
34impl Rad {
35 pub const ZERO: Self = Rad(0.0);
37
38 pub const PI: Self = Rad(std::f64::consts::PI);
40
41 pub const TAU: Self = Rad(std::f64::consts::TAU);
43
44 pub const FRAC_PI_2: Self = Rad(std::f64::consts::FRAC_PI_2);
46
47 pub const FRAC_PI_4: Self = Rad(std::f64::consts::FRAC_PI_4);
49
50 #[inline]
52 pub const fn new(value: f64) -> Self {
53 Rad(value)
54 }
55
56 #[inline]
58 pub fn to_deg(self) -> Deg {
59 Deg(self.0.to_degrees())
60 }
61
62 #[inline]
64 pub fn value(self) -> f64 {
65 self.0
66 }
67
68 #[inline]
70 pub fn sin(self) -> f64 {
71 self.0.sin()
72 }
73
74 #[inline]
76 pub fn cos(self) -> f64 {
77 self.0.cos()
78 }
79
80 #[inline]
82 pub fn tan(self) -> f64 {
83 self.0.tan()
84 }
85
86 #[inline]
88 pub fn abs(self) -> Self {
89 Rad(self.0.abs())
90 }
91
92 pub fn normalize(self) -> Self {
94 let mut angle = self.0 % std::f64::consts::TAU;
95 if angle > std::f64::consts::PI {
96 angle -= std::f64::consts::TAU;
97 } else if angle < -std::f64::consts::PI {
98 angle += std::f64::consts::TAU;
99 }
100 Rad(angle)
101 }
102
103 #[inline]
105 pub fn clamp(self, min: Self, max: Self) -> Self {
106 Rad(self.0.clamp(min.0, max.0))
107 }
108}
109
110impl fmt::Display for Rad {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(f, "{:.4} rad", self.0)
113 }
114}
115
116impl Add for Rad {
118 type Output = Self;
119 #[inline]
120 fn add(self, rhs: Self) -> Self {
121 Rad(self.0 + rhs.0)
122 }
123}
124
125impl Sub for Rad {
126 type Output = Self;
127 #[inline]
128 fn sub(self, rhs: Self) -> Self {
129 Rad(self.0 - rhs.0)
130 }
131}
132
133impl Mul<f64> for Rad {
134 type Output = Self;
135 #[inline]
136 fn mul(self, rhs: f64) -> Self {
137 Rad(self.0 * rhs)
138 }
139}
140
141impl Mul<Rad> for f64 {
142 type Output = Rad;
143 #[inline]
144 fn mul(self, rhs: Rad) -> Rad {
145 Rad(self * rhs.0)
146 }
147}
148
149impl Div<f64> for Rad {
150 type Output = Self;
151 #[inline]
152 fn div(self, rhs: f64) -> Self {
153 Rad(self.0 / rhs)
154 }
155}
156
157impl Div<Rad> for Rad {
158 type Output = f64;
159 #[inline]
160 fn div(self, rhs: Rad) -> f64 {
161 self.0 / rhs.0
162 }
163}
164
165impl Neg for Rad {
166 type Output = Self;
167 #[inline]
168 fn neg(self) -> Self {
169 Rad(-self.0)
170 }
171}
172
173impl AddAssign for Rad {
174 #[inline]
175 fn add_assign(&mut self, rhs: Self) {
176 self.0 += rhs.0;
177 }
178}
179
180impl SubAssign for Rad {
181 #[inline]
182 fn sub_assign(&mut self, rhs: Self) {
183 self.0 -= rhs.0;
184 }
185}
186
187impl MulAssign<f64> for Rad {
188 #[inline]
189 fn mul_assign(&mut self, rhs: f64) {
190 self.0 *= rhs;
191 }
192}
193
194impl DivAssign<f64> for Rad {
195 #[inline]
196 fn div_assign(&mut self, rhs: f64) {
197 self.0 /= rhs;
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
206pub struct Deg(pub f64);
207
208impl Deg {
209 pub const ZERO: Self = Deg(0.0);
211
212 pub const DEG_180: Self = Deg(180.0);
214
215 pub const DEG_360: Self = Deg(360.0);
217
218 pub const DEG_90: Self = Deg(90.0);
220
221 pub const DEG_45: Self = Deg(45.0);
223
224 #[inline]
226 pub const fn new(value: f64) -> Self {
227 Deg(value)
228 }
229
230 #[inline]
232 pub fn to_rad(self) -> Rad {
233 Rad(self.0.to_radians())
234 }
235
236 #[inline]
238 pub fn value(self) -> f64 {
239 self.0
240 }
241
242 #[inline]
244 pub fn abs(self) -> Self {
245 Deg(self.0.abs())
246 }
247
248 pub fn normalize(self) -> Self {
250 let mut angle = self.0 % 360.0;
251 if angle > 180.0 {
252 angle -= 360.0;
253 } else if angle < -180.0 {
254 angle += 360.0;
255 }
256 Deg(angle)
257 }
258
259 #[inline]
261 pub fn clamp(self, min: Self, max: Self) -> Self {
262 Deg(self.0.clamp(min.0, max.0))
263 }
264}
265
266impl fmt::Display for Deg {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 write!(f, "{:.2}°", self.0)
269 }
270}
271
272impl Add for Deg {
274 type Output = Self;
275 #[inline]
276 fn add(self, rhs: Self) -> Self {
277 Deg(self.0 + rhs.0)
278 }
279}
280
281impl Sub for Deg {
282 type Output = Self;
283 #[inline]
284 fn sub(self, rhs: Self) -> Self {
285 Deg(self.0 - rhs.0)
286 }
287}
288
289impl Mul<f64> for Deg {
290 type Output = Self;
291 #[inline]
292 fn mul(self, rhs: f64) -> Self {
293 Deg(self.0 * rhs)
294 }
295}
296
297impl Mul<Deg> for f64 {
298 type Output = Deg;
299 #[inline]
300 fn mul(self, rhs: Deg) -> Deg {
301 Deg(self * rhs.0)
302 }
303}
304
305impl Div<f64> for Deg {
306 type Output = Self;
307 #[inline]
308 fn div(self, rhs: f64) -> Self {
309 Deg(self.0 / rhs)
310 }
311}
312
313impl Div<Deg> for Deg {
314 type Output = f64;
315 #[inline]
316 fn div(self, rhs: Deg) -> f64 {
317 self.0 / rhs.0
318 }
319}
320
321impl Neg for Deg {
322 type Output = Self;
323 #[inline]
324 fn neg(self) -> Self {
325 Deg(-self.0)
326 }
327}
328
329impl AddAssign for Deg {
330 #[inline]
331 fn add_assign(&mut self, rhs: Self) {
332 self.0 += rhs.0;
333 }
334}
335
336impl SubAssign for Deg {
337 #[inline]
338 fn sub_assign(&mut self, rhs: Self) {
339 self.0 -= rhs.0;
340 }
341}
342
343impl MulAssign<f64> for Deg {
344 #[inline]
345 fn mul_assign(&mut self, rhs: f64) {
346 self.0 *= rhs;
347 }
348}
349
350impl DivAssign<f64> for Deg {
351 #[inline]
352 fn div_assign(&mut self, rhs: f64) {
353 self.0 /= rhs;
354 }
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
361#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
362pub struct NewtonMeter(pub f64);
363
364impl NewtonMeter {
365 pub const ZERO: Self = NewtonMeter(0.0);
367
368 #[inline]
370 pub const fn new(value: f64) -> Self {
371 NewtonMeter(value)
372 }
373
374 #[inline]
376 pub fn value(self) -> f64 {
377 self.0
378 }
379
380 #[inline]
382 pub fn abs(self) -> Self {
383 NewtonMeter(self.0.abs())
384 }
385
386 #[inline]
388 pub fn clamp(self, min: Self, max: Self) -> Self {
389 NewtonMeter(self.0.clamp(min.0, max.0))
390 }
391}
392
393impl fmt::Display for NewtonMeter {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 write!(f, "{:.3} N·m", self.0)
396 }
397}
398
399impl Add for NewtonMeter {
401 type Output = Self;
402 #[inline]
403 fn add(self, rhs: Self) -> Self {
404 NewtonMeter(self.0 + rhs.0)
405 }
406}
407
408impl Sub for NewtonMeter {
409 type Output = Self;
410 #[inline]
411 fn sub(self, rhs: Self) -> Self {
412 NewtonMeter(self.0 - rhs.0)
413 }
414}
415
416impl Mul<f64> for NewtonMeter {
417 type Output = Self;
418 #[inline]
419 fn mul(self, rhs: f64) -> Self {
420 NewtonMeter(self.0 * rhs)
421 }
422}
423
424impl Mul<NewtonMeter> for f64 {
425 type Output = NewtonMeter;
426 #[inline]
427 fn mul(self, rhs: NewtonMeter) -> NewtonMeter {
428 NewtonMeter(self * rhs.0)
429 }
430}
431
432impl Div<f64> for NewtonMeter {
433 type Output = Self;
434 #[inline]
435 fn div(self, rhs: f64) -> Self {
436 NewtonMeter(self.0 / rhs)
437 }
438}
439
440impl Div<NewtonMeter> for NewtonMeter {
441 type Output = f64;
442 #[inline]
443 fn div(self, rhs: NewtonMeter) -> f64 {
444 self.0 / rhs.0
445 }
446}
447
448impl Neg for NewtonMeter {
449 type Output = Self;
450 #[inline]
451 fn neg(self) -> Self {
452 NewtonMeter(-self.0)
453 }
454}
455
456impl AddAssign for NewtonMeter {
457 #[inline]
458 fn add_assign(&mut self, rhs: Self) {
459 self.0 += rhs.0;
460 }
461}
462
463impl SubAssign for NewtonMeter {
464 #[inline]
465 fn sub_assign(&mut self, rhs: Self) {
466 self.0 -= rhs.0;
467 }
468}
469
470impl MulAssign<f64> for NewtonMeter {
471 #[inline]
472 fn mul_assign(&mut self, rhs: f64) {
473 self.0 *= rhs;
474 }
475}
476
477impl DivAssign<f64> for NewtonMeter {
478 #[inline]
479 fn div_assign(&mut self, rhs: f64) {
480 self.0 /= rhs;
481 }
482}
483
484#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
488#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
489pub struct RadPerSecond(pub f64);
490
491impl RadPerSecond {
492 pub const ZERO: Self = RadPerSecond(0.0);
494
495 #[inline]
497 pub const fn new(value: f64) -> Self {
498 RadPerSecond(value)
499 }
500
501 #[inline]
503 pub fn value(&self) -> f64 {
504 self.0
505 }
506
507 #[inline]
509 pub fn from_rad_per_sec(value: f64) -> Self {
510 RadPerSecond(value)
511 }
512
513 #[inline]
515 pub fn abs(self) -> Self {
516 RadPerSecond(self.0.abs())
517 }
518
519 #[inline]
521 pub fn clamp(self, min: Self, max: Self) -> Self {
522 RadPerSecond(self.0.clamp(min.0, max.0))
523 }
524}
525
526impl fmt::Display for RadPerSecond {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 write!(f, "{:.4} rad/s", self.0)
529 }
530}
531
532impl Add for RadPerSecond {
534 type Output = Self;
535 #[inline]
536 fn add(self, rhs: RadPerSecond) -> Self {
537 RadPerSecond(self.0 + rhs.0)
538 }
539}
540
541impl Sub for RadPerSecond {
542 type Output = Self;
543 #[inline]
544 fn sub(self, rhs: RadPerSecond) -> Self {
545 RadPerSecond(self.0 - rhs.0)
546 }
547}
548
549impl Mul<f64> for RadPerSecond {
550 type Output = Self;
551 #[inline]
552 fn mul(self, rhs: f64) -> Self {
553 RadPerSecond(self.0 * rhs)
554 }
555}
556
557impl Mul<RadPerSecond> for f64 {
558 type Output = RadPerSecond;
559 #[inline]
560 fn mul(self, rhs: RadPerSecond) -> RadPerSecond {
561 RadPerSecond(self * rhs.0)
562 }
563}
564
565impl Div<f64> for RadPerSecond {
566 type Output = Self;
567 #[inline]
568 fn div(self, rhs: f64) -> Self {
569 RadPerSecond(self.0 / rhs)
570 }
571}
572
573impl Div<RadPerSecond> for RadPerSecond {
574 type Output = f64;
575 #[inline]
576 fn div(self, rhs: RadPerSecond) -> f64 {
577 self.0 / rhs.0
578 }
579}
580
581impl Neg for RadPerSecond {
582 type Output = Self;
583 #[inline]
584 fn neg(self) -> Self {
585 RadPerSecond(-self.0)
586 }
587}
588
589impl AddAssign for RadPerSecond {
590 #[inline]
591 fn add_assign(&mut self, rhs: Self) {
592 self.0 += rhs.0;
593 }
594}
595
596impl SubAssign for RadPerSecond {
597 #[inline]
598 fn sub_assign(&mut self, rhs: Self) {
599 self.0 -= rhs.0;
600 }
601}
602
603impl MulAssign<f64> for RadPerSecond {
604 #[inline]
605 fn mul_assign(&mut self, rhs: f64) {
606 self.0 *= rhs;
607 }
608}
609
610impl DivAssign<f64> for RadPerSecond {
611 #[inline]
612 fn div_assign(&mut self, rhs: f64) {
613 self.0 /= rhs;
614 }
615}
616
617impl Div<std::time::Duration> for RadPerSecond {
619 type Output = f64; fn div(self, rhs: std::time::Duration) -> Self::Output {
621 self.0 / rhs.as_secs_f64()
622 }
623}
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628
629 #[test]
631 fn test_rad_to_deg() {
632 let rad = Rad(std::f64::consts::PI);
633 let deg = rad.to_deg();
634 assert!((deg.0 - 180.0).abs() < 1e-6);
635 }
636
637 #[test]
638 fn test_deg_to_rad() {
639 let deg = Deg(180.0);
640 let rad = deg.to_rad();
641 assert!((rad.0 - std::f64::consts::PI).abs() < 1e-6);
642 }
643
644 #[test]
645 fn test_rad_operations() {
646 let r1 = Rad(1.0);
647 let r2 = Rad(2.0);
648
649 assert_eq!(r1 + r2, Rad(3.0));
650 assert_eq!(r2 - r1, Rad(1.0));
651 assert_eq!(r1 * 2.0, Rad(2.0));
652 assert_eq!(r2 / 2.0, Rad(1.0));
653 assert_eq!(-r1, Rad(-1.0));
654 }
655
656 #[test]
657 fn test_deg_operations() {
658 let d1 = Deg(90.0);
659 let d2 = Deg(180.0);
660
661 assert_eq!(d1 + d2, Deg(270.0));
662 assert_eq!(d2 - d1, Deg(90.0));
663 assert_eq!(d1 * 2.0, Deg(180.0));
664 assert_eq!(d2 / 2.0, Deg(90.0));
665 assert_eq!(-d1, Deg(-90.0));
666 }
667
668 #[test]
669 fn test_newton_meter_operations() {
670 let nm1 = NewtonMeter(10.0);
671 let nm2 = NewtonMeter(5.0);
672
673 assert_eq!(nm1 + nm2, NewtonMeter(15.0));
674 assert_eq!(nm1 - nm2, NewtonMeter(5.0));
675 assert_eq!(nm1 * 2.0, NewtonMeter(20.0));
676 assert_eq!(nm1 / 2.0, NewtonMeter(5.0));
677 assert_eq!(-nm1, NewtonMeter(-10.0));
678 }
679
680 #[test]
681 fn test_rad_normalize() {
682 use std::f64::consts::PI;
683
684 assert_eq!(Rad(0.0).normalize(), Rad(0.0));
685 assert_eq!(Rad(PI).normalize(), Rad(PI));
686 assert_eq!(Rad(-PI).normalize(), Rad(-PI));
687
688 let normalized = Rad(3.0 * PI).normalize();
691 assert!((normalized.0 - PI).abs() < 1e-10);
693 }
694
695 #[test]
696 fn test_deg_normalize() {
697 assert_eq!(Deg(0.0).normalize(), Deg(0.0));
698 assert_eq!(Deg(180.0).normalize(), Deg(180.0));
699 assert_eq!(Deg(-180.0).normalize(), Deg(-180.0));
700
701 let normalized = Deg(540.0).normalize();
703 assert!((normalized.0 - 180.0).abs() < 1e-10);
704 }
705
706 #[test]
707 fn test_rad_trig_functions() {
708 let rad = Rad(std::f64::consts::FRAC_PI_2);
709 assert!((rad.sin() - 1.0).abs() < 1e-10);
710 assert!(rad.cos().abs() < 1e-10);
711 }
712
713 #[test]
714 fn test_display() {
715 let rad = Rad(std::f64::consts::FRAC_PI_2);
716 let deg = Deg(90.0);
717 let nm = NewtonMeter(10.5);
718
719 assert_eq!(format!("{}", rad), "1.5708 rad");
720 assert_eq!(format!("{}", deg), "90.00°");
721 assert_eq!(format!("{}", nm), "10.500 N·m");
722 }
723
724 #[test]
725 fn test_clamp() {
726 let rad = Rad(5.0);
727 assert_eq!(rad.clamp(Rad(-1.0), Rad(1.0)), Rad(1.0));
728
729 let deg = Deg(200.0);
730 assert_eq!(deg.clamp(Deg(-90.0), Deg(90.0)), Deg(90.0));
731
732 let nm = NewtonMeter(-100.0);
733 assert_eq!(
734 nm.clamp(NewtonMeter(-50.0), NewtonMeter(50.0)),
735 NewtonMeter(-50.0)
736 );
737 }
738
739 #[test]
740 fn test_assign_operators() {
741 let mut rad = Rad(1.0);
742 rad += Rad(2.0);
743 assert_eq!(rad, Rad(3.0));
744
745 rad -= Rad(1.0);
746 assert_eq!(rad, Rad(2.0));
747
748 rad *= 2.0;
749 assert_eq!(rad, Rad(4.0));
750
751 rad /= 2.0;
752 assert_eq!(rad, Rad(2.0));
753 }
754
755 #[test]
757 fn test_rad_per_second_operations() {
758 let v1 = RadPerSecond(10.0);
759 let v2 = RadPerSecond(5.0);
760
761 assert_eq!(v1 + v2, RadPerSecond(15.0));
762 assert_eq!(v1 - v2, RadPerSecond(5.0));
763 assert_eq!(v1 * 2.0, RadPerSecond(20.0));
764 assert_eq!(v1 / 2.0, RadPerSecond(5.0));
765 assert_eq!(-v1, RadPerSecond(-10.0));
766 }
767
768 #[test]
769 fn test_rad_per_second_assign_operators() {
770 let mut vel = RadPerSecond(1.0);
771 vel += RadPerSecond(2.0);
772 assert_eq!(vel, RadPerSecond(3.0));
773
774 vel -= RadPerSecond(1.0);
775 assert_eq!(vel, RadPerSecond(2.0));
776
777 vel *= 2.0;
778 assert_eq!(vel, RadPerSecond(4.0));
779
780 vel /= 2.0;
781 assert_eq!(vel, RadPerSecond(2.0));
782 }
783
784 #[test]
785 fn test_rad_per_second_div_duration() {
786 let vel = RadPerSecond(10.0);
787 let duration = std::time::Duration::from_secs(2);
788 let accel = vel / duration;
789 assert!((accel - 5.0).abs() < 1e-10); }
791
792 #[test]
793 fn test_rad_per_second_display() {
794 let vel = RadPerSecond(std::f64::consts::PI);
795 assert_eq!(format!("{}", vel), "3.1416 rad/s");
796 }
797
798 #[test]
799 fn test_rad_per_second_clamp() {
800 let vel = RadPerSecond(10.0);
801 assert_eq!(
802 vel.clamp(RadPerSecond(-5.0), RadPerSecond(5.0)),
803 RadPerSecond(5.0)
804 );
805 }
806}