use crate::{MotionValue, Transition};
use std::time::Duration;
#[cfg(feature = "gpui")]
use std::time::Instant;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SvgTarget {
values: [f64; 5],
}
impl Default for SvgTarget {
fn default() -> Self {
Self {
values: [1.0, 1.0, 0.0, 0.0, 0.0],
}
}
}
impl SvgTarget {
pub fn scale(self, factor: f32) -> Self {
self.scale_xy(factor, factor)
}
pub fn scale_xy(mut self, x: f32, y: f32) -> Self {
assert!(x.is_finite() && y.is_finite(), "SVG scale must be finite");
self.values[0] = f64::from(x);
self.values[1] = f64::from(y);
self
}
pub fn rotate_degrees(mut self, degrees: f64) -> Self {
assert!(degrees.is_finite(), "SVG rotation must be finite");
self.values[2] = degrees;
self
}
pub fn translate(mut self, x: f32, y: f32) -> Self {
assert!(
x.is_finite() && y.is_finite(),
"SVG translation must be finite"
);
self.values[3] = x.into();
self.values[4] = y.into();
self
}
pub fn translation(self) -> [f32; 2] {
[self.values[3], self.values[4]]
.map(|v| v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32)
}
pub fn scale_factors(self) -> [f32; 2] {
[self.values[0], self.values[1]]
.map(|v| v.clamp(f64::from(f32::MIN), f64::from(f32::MAX)) as f32)
}
pub fn rotation_degrees(self) -> f64 {
self.values[2]
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SvgMotion {
target: SvgTarget,
initial: Option<SvgTarget>,
transition: Transition,
delay: Duration,
}
impl SvgMotion {
pub fn new(target: SvgTarget) -> Self {
Self {
target,
initial: None,
transition: Transition::default(),
delay: Duration::ZERO,
}
}
pub fn initial(mut self, initial: SvgTarget) -> Self {
self.initial = Some(initial);
self
}
pub fn transition(mut self, transition: Transition) -> Self {
self.transition = transition;
self
}
pub fn delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct SvgFrame {
target: SvgTarget,
velocity: [f64; 3],
translation_velocity: [f64; 2],
active: bool,
completed: bool,
}
impl SvgFrame {
pub fn target(self) -> SvgTarget {
self.target
}
pub fn velocity(self) -> [f64; 3] {
self.velocity
}
pub fn translation_velocity(self) -> [f64; 2] {
self.translation_velocity
}
pub fn is_animating(self) -> bool {
self.active
}
pub fn just_completed(self) -> bool {
self.completed
}
}
#[derive(Debug)]
pub struct SvgMotionState {
channels: [MotionValue; 5],
initialized: bool,
#[cfg(feature = "gpui")]
pub(crate) epoch: Instant,
}
impl Default for SvgMotionState {
fn default() -> Self {
Self {
channels: SvgTarget::default().values.map(MotionValue::new),
initialized: false,
#[cfg(feature = "gpui")]
epoch: Instant::now(),
}
}
}
impl SvgMotionState {
pub fn sample(&mut self, motion: SvgMotion, now: Duration, reduced_motion: bool) -> SvgFrame {
if !self.initialized {
let initial = motion.initial.unwrap_or(motion.target);
for (channel, value) in self.channels.iter_mut().zip(initial.values) {
channel.jump(value, now);
}
self.initialized = true;
}
let mut active = false;
let mut completed = false;
let mut velocity = [0.0; 5];
let values = std::array::from_fn(|ix| {
self.channels[ix].animate_delayed(
motion.target.values[ix],
motion.transition,
motion.delay,
now,
);
let frame = self.channels[ix].sample(now, reduced_motion);
active |= frame.is_animating();
completed |= frame.just_completed();
velocity[ix] = frame.velocity();
frame.value()
});
SvgFrame {
target: SvgTarget { values },
velocity: [velocity[0], velocity[1], velocity[2]],
translation_velocity: [velocity[3], velocity[4]],
active,
completed: completed && !active,
}
}
}