1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
use scene::{CurveHandle, ScalarAnimationKey};
use time::MediaTime;
pub const BEZIER_SOLVE_ITERATIONS: usize = 20;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BezierHandle {
pub dt: f64,
pub dv: f64,
}
impl From<CurveHandle> for BezierHandle {
fn from(handle: CurveHandle) -> Self {
Self {
dt: handle.dt.as_ticks() as f64,
dv: handle.dv,
}
}
}
pub fn bezier_point(progress: f64, p0: f64, p1: f64, p2: f64, p3: f64) -> f64 {
let mt = 1.0 - progress;
mt * mt * mt * p0
+ 3.0 * mt * mt * progress * p1
+ 3.0 * mt * progress * progress * p2
+ progress * progress * progress * p3
}
pub fn default_right_handle(
left_key: &ScalarAnimationKey,
right_key: &ScalarAnimationKey,
) -> BezierHandle {
let span = (right_key.time - left_key.time).as_ticks() as f64;
let value_delta = right_key.value - left_key.value;
BezierHandle {
dt: span / 3.0,
dv: value_delta / 3.0,
}
}
pub fn default_left_handle(
left_key: &ScalarAnimationKey,
right_key: &ScalarAnimationKey,
) -> BezierHandle {
let span = (right_key.time - left_key.time).as_ticks() as f64;
let value_delta = right_key.value - left_key.value;
BezierHandle {
dt: -span / 3.0,
dv: -value_delta / 3.0,
}
}
pub fn segment_handles(
left_key: &ScalarAnimationKey,
right_key: &ScalarAnimationKey,
) -> (BezierHandle, BezierHandle) {
let right_handle = left_key
.right_handle
.map(BezierHandle::from)
.unwrap_or_else(|| default_right_handle(left_key, right_key));
let left_handle = right_key
.left_handle
.map(BezierHandle::from)
.unwrap_or_else(|| default_left_handle(left_key, right_key));
(right_handle, left_handle)
}
pub fn solve_bezier_progress_for_time(
time: MediaTime,
left_key: &ScalarAnimationKey,
right_key: &ScalarAnimationKey,
) -> f64 {
let (right_handle, left_handle) = segment_handles(left_key, right_key);
let target = time.as_ticks() as f64;
let p0 = left_key.time.as_ticks() as f64;
let p3 = right_key.time.as_ticks() as f64;
let mut lower = 0.0;
let mut upper = 1.0;
for _ in 0..BEZIER_SOLVE_ITERATIONS {
let mid = (lower + upper) / 2.0;
let estimate = bezier_point(mid, p0, p0 + right_handle.dt, p3 + left_handle.dt, p3);
if estimate < target {
lower = mid;
} else {
upper = mid;
}
}
(lower + upper) / 2.0
}
#[cfg(test)]
mod tests {
use super::*;
use scene::{ScalarSegmentType, TangentMode};
fn key(time: i64, value: f64) -> ScalarAnimationKey {
ScalarAnimationKey {
id: format!("k{time}"),
time: MediaTime::from_ticks(time),
value,
left_handle: None,
right_handle: None,
segment_to_next: ScalarSegmentType::Bezier,
tangent_mode: TangentMode::Flat,
}
}
#[test]
fn bezier_point_evaluates_cubic() {
assert_eq!(bezier_point(0.0, 0.0, 1.0, 2.0, 3.0), 0.0);
assert_eq!(bezier_point(1.0, 0.0, 1.0, 2.0, 3.0), 3.0);
assert_eq!(bezier_point(0.5, 0.0, 0.0, 1.0, 1.0), 0.5);
}
#[test]
fn default_handles_split_span_into_thirds() {
let left = key(0, 10.0);
let right = key(90, 40.0);
let right_handle = default_right_handle(&left, &right);
let left_handle = default_left_handle(&left, &right);
assert_eq!(right_handle.dt, 30.0);
assert_eq!(right_handle.dv, 10.0);
assert_eq!(left_handle.dt, -30.0);
assert_eq!(left_handle.dv, -10.0);
}
#[test]
fn solve_progress_is_linear_for_diagonal_control_points() {
let left = key(0, 0.0);
let right = key(100, 100.0);
let progress = solve_bezier_progress_for_time(MediaTime::from_ticks(50), &left, &right);
assert!((progress - 0.5).abs() < 1e-6);
let progress = solve_bezier_progress_for_time(MediaTime::from_ticks(25), &left, &right);
assert!((progress - 0.25).abs() < 1e-6);
}
#[test]
fn solve_progress_respects_flat_tangents() {
let mut left = key(0, 0.0);
left.right_handle = Some(CurveHandle {
dt: MediaTime::from_ticks(0),
dv: 0.0,
});
let mut right = key(100, 100.0);
right.left_handle = Some(CurveHandle {
dt: MediaTime::from_ticks(0),
dv: 0.0,
});
let early = solve_bezier_progress_for_time(MediaTime::from_ticks(25), &left, &right);
let late = solve_bezier_progress_for_time(MediaTime::from_ticks(75), &left, &right);
assert!(early < 0.25);
assert!(late > 0.75);
}
}
|