animation: move curve primitives into layout crate
This commit is contained in:
parent
1558666601
commit
db94c9167f
2 changed files with 162 additions and 159 deletions
161
src/animation.rs
161
src/animation.rs
|
|
@ -19,127 +19,9 @@ use {
|
|||
|
||||
pub mod multiphase;
|
||||
|
||||
pub use jay_layout_animation::{AnimationCurve, AnimationStyle};
|
||||
|
||||
const DEFAULT_DURATION_MS: u32 = 160;
|
||||
const CURVE_MAX_POINTS: usize = 33;
|
||||
const CURVE_FLATNESS_EPSILON: f32 = 0.001;
|
||||
const CURVE_MAX_DEPTH: u8 = 8;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum AnimationCurve {
|
||||
Linear,
|
||||
Piecewise(PiecewiseCurve),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum AnimationStyle {
|
||||
Plain,
|
||||
Multiphase,
|
||||
}
|
||||
|
||||
impl AnimationStyle {
|
||||
pub fn from_config(value: u32) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Plain),
|
||||
1 => Some(Self::Multiphase),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationCurve {
|
||||
pub fn from_config(value: u32) -> Self {
|
||||
match value {
|
||||
0 => Self::Linear,
|
||||
1 => Self::from_cubic_bezier(0.25, 0.1, 0.25, 1.0).unwrap(),
|
||||
2 => Self::from_cubic_bezier(0.42, 0.0, 1.0, 1.0).unwrap(),
|
||||
4 => Self::from_cubic_bezier(0.42, 0.0, 0.58, 1.0).unwrap(),
|
||||
_ => Self::from_cubic_bezier(0.0, 0.0, 0.58, 1.0).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> Option<Self> {
|
||||
if !x1.is_finite()
|
||||
|| !y1.is_finite()
|
||||
|| !x2.is_finite()
|
||||
|| !y2.is_finite()
|
||||
|| !(0.0..=1.0).contains(&x1)
|
||||
|| !(0.0..=1.0).contains(&x2)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(Self::Piecewise(PiecewiseCurve::from_cubic_bezier(
|
||||
x1, y1, x2, y2,
|
||||
)))
|
||||
}
|
||||
|
||||
fn sample(self, t: f64) -> f64 {
|
||||
let t = t.clamp(0.0, 1.0);
|
||||
match self {
|
||||
Self::Linear => t,
|
||||
Self::Piecewise(curve) => curve.sample(t as f32) as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct PiecewiseCurve {
|
||||
len: u8,
|
||||
points: [CurvePoint; CURVE_MAX_POINTS],
|
||||
}
|
||||
|
||||
impl PiecewiseCurve {
|
||||
fn from_cubic_bezier(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
|
||||
let mut points = Vec::with_capacity(CURVE_MAX_POINTS);
|
||||
let p0 = cubic_bezier_point(x1, y1, x2, y2, 0.0);
|
||||
let p1 = cubic_bezier_point(x1, y1, x2, y2, 1.0);
|
||||
points.push(p0);
|
||||
flatten_cubic_bezier(&mut points, (x1, y1, x2, y2), 0.0, p0, 1.0, p1, 0);
|
||||
let mut array = [CurvePoint::default(); CURVE_MAX_POINTS];
|
||||
let len = points.len().min(CURVE_MAX_POINTS);
|
||||
array[..len].copy_from_slice(&points[..len]);
|
||||
Self {
|
||||
len: len as u8,
|
||||
points: array,
|
||||
}
|
||||
}
|
||||
|
||||
fn sample(self, x: f32) -> f32 {
|
||||
let len = self.len as usize;
|
||||
if len <= 1 {
|
||||
return x;
|
||||
}
|
||||
let points = &self.points[..len];
|
||||
if x <= points[0].x {
|
||||
return points[0].y;
|
||||
}
|
||||
if x >= points[len - 1].x {
|
||||
return points[len - 1].y;
|
||||
}
|
||||
let mut lo = 0;
|
||||
let mut hi = len - 1;
|
||||
while lo + 1 < hi {
|
||||
let mid = (lo + hi) / 2;
|
||||
if points[mid].x <= x {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
let from = points[lo];
|
||||
let to = points[hi];
|
||||
if to.x <= from.x {
|
||||
return to.y;
|
||||
}
|
||||
let t = (x - from.x) / (to.x - from.x);
|
||||
from.y + (to.y - from.y) * t
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
struct CurvePoint {
|
||||
x: f32,
|
||||
y: f32,
|
||||
}
|
||||
|
||||
pub struct AnimationState {
|
||||
pub enabled: Cell<bool>,
|
||||
|
|
@ -885,45 +767,6 @@ pub(crate) fn expand_damage_rect(rect: Rect, width: i32) -> Rect {
|
|||
)
|
||||
}
|
||||
|
||||
fn flatten_cubic_bezier(
|
||||
points: &mut Vec<CurvePoint>,
|
||||
controls: (f32, f32, f32, f32),
|
||||
t0: f32,
|
||||
p0: CurvePoint,
|
||||
t1: f32,
|
||||
p1: CurvePoint,
|
||||
depth: u8,
|
||||
) {
|
||||
let tm = (t0 + t1) * 0.5;
|
||||
let pm = cubic_bezier_point(controls.0, controls.1, controls.2, controls.3, tm);
|
||||
let projected_y = if p1.x <= p0.x {
|
||||
(p0.y + p1.y) * 0.5
|
||||
} else {
|
||||
let tx = (pm.x - p0.x) / (p1.x - p0.x);
|
||||
p0.y + (p1.y - p0.y) * tx
|
||||
};
|
||||
if (pm.y - projected_y).abs() > CURVE_FLATNESS_EPSILON
|
||||
&& depth < CURVE_MAX_DEPTH
|
||||
&& points.len() + 2 < CURVE_MAX_POINTS
|
||||
{
|
||||
flatten_cubic_bezier(points, controls, t0, p0, tm, pm, depth + 1);
|
||||
flatten_cubic_bezier(points, controls, tm, pm, t1, p1, depth + 1);
|
||||
} else {
|
||||
points.push(p1);
|
||||
}
|
||||
}
|
||||
|
||||
fn cubic_bezier_point(x1: f32, y1: f32, x2: f32, y2: f32, t: f32) -> CurvePoint {
|
||||
fn bezier(a: f32, b: f32, t: f32) -> f32 {
|
||||
let inv = 1.0 - t;
|
||||
3.0 * inv * inv * t * a + 3.0 * inv * t * t * b + t * t * t
|
||||
}
|
||||
CurvePoint {
|
||||
x: bezier(x1, x2, t),
|
||||
y: bezier(y1, y2, t),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue