motion_core/time/
frame_rate.rs1use std::num::NonZeroU32;
4
5use super::Duration;
6
7#[macro_export]
16macro_rules! fps {
17 ($frames:expr) => {
18 $crate::FrameRate::per_second(
19 const {
20 match ::std::num::NonZeroU32::new($frames) {
21 Some(frames) => frames,
22 None => panic!("frames per second must be nonzero"),
23 }
24 },
25 )
26 };
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31pub struct FrameRate {
32 numerator: NonZeroU32,
33 denominator: NonZeroU32,
34}
35
36impl FrameRate {
37 pub const NTSC_FILM: Self = Self::new(non_zero(24_000), non_zero(1_001));
39
40 pub const NTSC: Self = Self::new(non_zero(30_000), non_zero(1_001));
42
43 pub const NTSC_DOUBLE: Self = Self::new(non_zero(60_000), non_zero(1_001));
45
46 pub fn try_new(numerator: u32, denominator: u32) -> Result<Self, crate::ValidationError> {
51 match (NonZeroU32::new(numerator), NonZeroU32::new(denominator)) {
52 (Some(numerator), Some(denominator)) => Ok(Self::new(numerator, denominator)),
53 _ => Err(crate::ValidationError::invalid(
54 "frame_rate",
55 "ratio",
56 "frame-rate numerator and denominator must be nonzero",
57 )),
58 }
59 }
60
61 pub const fn new(numerator: NonZeroU32, denominator: NonZeroU32) -> Self {
63 let mut common = numerator.get();
64 let mut remainder = denominator.get();
65 while remainder != 0 {
66 let next = common % remainder;
67 common = remainder;
68 remainder = next;
69 }
70 Self {
71 numerator: non_zero(numerator.get() / common),
72 denominator: non_zero(denominator.get() / common),
73 }
74 }
75
76 pub const fn per_second(frames: NonZeroU32) -> Self {
78 Self {
79 numerator: frames,
80 denominator: NonZeroU32::MIN,
81 }
82 }
83
84 pub const fn numerator(self) -> u32 {
86 self.numerator.get()
87 }
88
89 pub const fn denominator(self) -> u32 {
91 self.denominator.get()
92 }
93
94 pub fn frame_duration(self) -> Duration {
97 Duration::from_frame(1, self)
98 }
99}
100
101impl From<NonZeroU32> for FrameRate {
102 fn from(frames_per_second: NonZeroU32) -> Self {
103 Self::per_second(frames_per_second)
104 }
105}
106
107const fn non_zero(value: u32) -> NonZeroU32 {
109 match NonZeroU32::new(value) {
110 Some(value) => value,
111 None => panic!("frame-rate component must be nonzero"),
112 }
113}