1use alpha2_model::{LoopMode, TimeMap};
2use alpha2_time::{FrameRate, MediaTime, TICKS_PER_SECOND, TimeRange};
3
4#[derive(Copy, Clone, Debug, Eq, PartialEq)]
11pub(crate) struct Mirror {
12 ticks: i128,
13 per: i128,
14}
15
16impl Mirror {
17 pub(crate) const TICK: Self = Self { ticks: 1, per: 1 };
19
20 pub(crate) fn frame(rate: FrameRate) -> Self {
22 Self {
23 ticks: i128::from(TICKS_PER_SECOND) * i128::from(rate.denominator()),
24 per: i128::from(rate.numerator()),
25 }
26 }
27
28 fn start_of(self, frame: i128) -> Option<i128> {
29 Some(frame.checked_mul(self.ticks)?.div_euclid(self.per))
30 }
31
32 fn frame_of(self, tick: i128) -> Option<i128> {
33 Some(
34 tick.checked_add(1)?
35 .checked_mul(self.per)?
36 .checked_sub(1)?
37 .div_euclid(self.ticks),
38 )
39 }
40
41 fn offset(self, start: i128, length: i128, elapsed: i128, scale: i128) -> Option<i128> {
48 let first = self.frame_of(start.checked_sub(1)?)?.checked_add(1)?;
49 let last = self.frame_of(start.checked_add(length)?.checked_sub(1)?)?;
50 if first > last {
51 return Some(0);
52 }
53 let tick = start.checked_add(elapsed)?;
54 let frame = self.frame_of(tick)?;
55 let (frame, into) = if frame < first {
56 (first, 0)
57 } else {
58 (frame, tick.checked_sub(self.start_of(frame)?)?)
59 };
60 let mirrored = self
61 .start_of(first.checked_add(last)?.checked_sub(frame)?)?
62 .checked_sub(into)?
63 .checked_sub(start)?;
64 if mirrored <= 0 {
65 return Some(0);
66 }
67 Some(mirrored.checked_mul(scale)?.div_euclid(length))
68 }
69}
70
71pub(crate) fn map_source_time(
85 timeline: TimeRange,
86 source: TimeRange,
87 time_map: TimeMap,
88 mirror: Mirror,
89 time: MediaTime,
90) -> Option<MediaTime> {
91 let timeline_duration = i128::from(timeline.duration()?.ticks());
92 let source_duration = i128::from(source.duration()?.ticks());
93 if timeline_duration == 0 || source_duration == 0 {
94 return None;
95 }
96 let elapsed = i128::from(time.ticks()) - i128::from(timeline.start().ticks());
97 let offset = match time_map {
98 TimeMap::Forward => elapsed
99 .checked_mul(source_duration)?
100 .div_euclid(timeline_duration),
101 TimeMap::Reverse => mirror.offset(
102 i128::from(timeline.start().ticks()),
103 timeline_duration,
104 elapsed,
105 source_duration,
106 )?,
107 TimeMap::Freeze { source_time } => return Some(source_time),
108 TimeMap::Loop { mode } => {
109 let within = elapsed.rem_euclid(source_duration);
110 let pass = elapsed.div_euclid(source_duration);
111 match mode {
112 LoopMode::PingPong if pass.rem_euclid(2) == 1 => mirror.offset(
113 i128::from(timeline.start().ticks())
114 .checked_add(pass.checked_mul(source_duration)?)?,
115 source_duration,
116 within,
117 source_duration,
118 )?,
119 LoopMode::Repeat | LoopMode::PingPong => within,
120 }
121 }
122 };
123 let mapped = i128::from(source.start().ticks()).checked_add(offset)?;
124 i64::try_from(mapped).ok().map(MediaTime::from_ticks)
125}
126
127#[must_use]
134pub fn picture_source_time(
135 timeline: TimeRange,
136 source: TimeRange,
137 time_map: TimeMap,
138 frame_rate: FrameRate,
139 time: MediaTime,
140) -> Option<MediaTime> {
141 map_source_time(timeline, source, time_map, Mirror::frame(frame_rate), time)
142}