use crate::nearly_equal_f32;
use super::{Keyframe, normalize_offset};
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum KeyframeSegment<'a> {
Empty,
Single(&'a Keyframe),
Exact(&'a Keyframe),
Between {
from: &'a Keyframe,
to: &'a Keyframe,
progress: f32,
},
}
impl<'a> KeyframeSegment<'a> {
pub(crate) fn find(frames: &'a [Keyframe], offset: f32) -> Self {
let offset = normalize_offset(offset);
match frames {
[] => Self::Empty,
[single] => Self::Single(single),
[first, ..] if offset <= first.offset() => Self::Exact(first),
[.., last] if offset >= last.offset() => Self::Exact(last),
_ => find_inside_track(frames, offset),
}
}
#[must_use]
pub(crate) const fn is_resolved(self) -> bool {
matches!(self, Self::Single(_) | Self::Exact(_))
}
}
fn find_inside_track(frames: &[Keyframe], offset: f32) -> KeyframeSegment<'_> {
if let Some(exact) = frames
.iter()
.find(|frame| nearly_equal_f32(frame.offset(), offset))
{
return KeyframeSegment::Exact(exact);
}
for pair in frames.windows(2) {
let from = &pair[0];
let to = &pair[1];
if from.offset() <= offset && offset <= to.offset() {
return KeyframeSegment::Between {
from,
to,
progress: segment_progress(from.offset(), to.offset(), offset),
};
}
}
KeyframeSegment::Empty
}
fn segment_progress(from: f32, to: f32, offset: f32) -> f32 {
let span = to - from;
if span > f32::EPSILON {
((offset - from) / span).clamp(0.0, 1.0)
} else {
0.0
}
}