gpui_base/motion/
keyframes.rs1use std::sync::Arc;
2
3use super::{Easing, Interpolate};
4
5#[derive(Clone, Debug)]
6pub struct Keyframe<T> {
7 pub offset: f32,
8 pub value: T,
9 pub easing: Easing,
10}
11
12impl<T> Keyframe<T> {
13 pub fn new(offset: f32, value: T) -> Self {
14 Self {
15 offset,
16 value,
17 easing: Easing::Linear,
18 }
19 }
20
21 pub fn ease(mut self, easing: Easing) -> Self {
22 self.easing = easing;
23 self
24 }
25}
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum KeyframeError {
29 TooFewFrames,
30 OffsetNotFinite,
31 OffsetOutOfRange,
32 OffsetsNotMonotonic,
33 MissingEndpoint,
34}
35
36#[derive(Clone, Debug)]
37pub struct Keyframes<T> {
38 frames: Arc<[Keyframe<T>]>,
39}
40
41impl<T: Interpolate> Keyframes<T> {
42 pub fn try_new(frames: impl IntoIterator<Item = Keyframe<T>>) -> Result<Self, KeyframeError> {
43 let frames: Vec<_> = frames.into_iter().collect();
44 if frames.len() < 2 {
45 return Err(KeyframeError::TooFewFrames);
46 }
47 if frames.iter().any(|frame| !frame.offset.is_finite()) {
48 return Err(KeyframeError::OffsetNotFinite);
49 }
50 if frames
51 .iter()
52 .any(|frame| !(0.0..=1.0).contains(&frame.offset))
53 {
54 return Err(KeyframeError::OffsetOutOfRange);
55 }
56 if frames
57 .windows(2)
58 .any(|pair| pair[0].offset > pair[1].offset)
59 {
60 return Err(KeyframeError::OffsetsNotMonotonic);
61 }
62 if frames.first().unwrap().offset != 0.0 || frames.last().unwrap().offset != 1.0 {
63 return Err(KeyframeError::MissingEndpoint);
64 }
65 Ok(Self {
66 frames: frames.into(),
67 })
68 }
69
70 #[inline]
71 pub fn sample(&self, progress: f32) -> T {
72 let progress = progress.clamp(0.0, 1.0);
73 let upper = self
74 .frames
75 .partition_point(|frame| frame.offset <= progress);
76 if upper == 0 {
77 return self.frames[0].value.clone();
78 }
79 if upper == self.frames.len() {
80 return self.frames[self.frames.len() - 1].value.clone();
81 }
82 let from = &self.frames[upper - 1];
83 let to = &self.frames[upper];
84 if from.offset == to.offset {
85 return to.value.clone();
86 }
87 let segment = (progress - from.offset) / (to.offset - from.offset);
88 from.value
89 .interpolate(&to.value, from.easing.sample(segment))
90 }
91
92 pub fn len(&self) -> usize {
93 self.frames.len()
94 }
95
96 pub fn is_empty(&self) -> bool {
97 self.frames.is_empty()
98 }
99}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102pub enum DiscreteError {
103 InvalidSwitchPoint,
104}
105
106#[derive(Clone, Debug)]
107pub struct Discrete<T> {
108 from: T,
109 to: T,
110 switch_at: f32,
111}
112
113impl<T> Discrete<T> {
114 pub fn new(from: T, to: T) -> Self {
115 Self {
116 from,
117 to,
118 switch_at: 0.5,
119 }
120 }
121
122 pub fn switch_at(mut self, progress: f32) -> Result<Self, DiscreteError> {
123 if !progress.is_finite() || !(0.0..=1.0).contains(&progress) {
124 return Err(DiscreteError::InvalidSwitchPoint);
125 }
126 self.switch_at = progress;
127 Ok(self)
128 }
129}
130
131impl<T: Clone> Discrete<T> {
132 #[inline]
133 pub fn sample(&self, progress: f32) -> T {
134 if progress < self.switch_at {
135 self.from.clone()
136 } else {
137 self.to.clone()
138 }
139 }
140}