use bevy::prelude::*;
use crate::animations::{Lerp, Runner, build_runner};
use crate::protocol::units::Length;
use super::spec::ChannelTransition;
use super::{shape_channel, transform3d};
macro_rules! with_input_channels {
($cb:ident) => {
$cb! {
(translate_x, (Length::Px(0.0)), transform),
(translate_y, (Length::Px(0.0)), transform),
(scale, (1.0), transform),
(scale_x, (1.0), transform),
(scale_y, (1.0), transform),
(rotate, (0.0), transform),
(opacity, (1.0), value),
(width, (Length::Auto), size),
(height, (Length::Auto), size),
(max_width, (Length::Auto), size),
(max_height, (Length::Auto), size),
}
};
}
pub(super) use with_input_channels;
#[derive(Component, Default)]
#[require(UiTransform)]
pub struct TransitionState {
pub(super) translate_x: ProgressChannel<Length>,
pub(super) translate_y: ProgressChannel<Length>,
pub(super) scale: Channel,
pub(super) scale_x: Channel,
pub(super) scale_y: Channel,
pub(super) rotate: Channel,
pub(super) opacity: Channel,
pub(super) color: ProgressChannel<[f32; 4]>,
pub(super) width: ProgressChannel<Length>,
pub(super) height: ProgressChannel<Length>,
pub(super) max_width: ProgressChannel<Length>,
pub(super) max_height: ProgressChannel<Length>,
pub(super) filter: FilterChannel,
pub(super) backdrop_filter: FilterChannel,
pub(super) transform3d: transform3d::Transform3dChannels,
pub(super) shape: shape_channel::ShapeChannel,
pub(super) initialized: bool,
}
#[derive(Default)]
pub(super) struct FilterChannel {
pub(super) wire: crate::filters::FilterChain,
pub(super) channel: EasedChannel<Vec<crate::filters::ResolvedFilterPass>, FilterEaseInterp>,
}
#[derive(Default)]
pub(super) struct FilterEaseInterp {
plan: Option<crate::filters::FilterEase>,
}
impl Interp<Vec<crate::filters::ResolvedFilterPass>> for FilterEaseInterp {
fn arm(
&mut self,
_from: &Vec<crate::filters::ResolvedFilterPass>,
_to: &Vec<crate::filters::ResolvedFilterPass>,
) {
}
fn sample(
&self,
p: f32,
_target: &Vec<crate::filters::ResolvedFilterPass>,
) -> Vec<crate::filters::ResolvedFilterPass> {
self.plan.as_ref().map(|e| e.sample(p)).unwrap_or_default()
}
fn settle(
&self,
_target: &Vec<crate::filters::ResolvedFilterPass>,
) -> Vec<crate::filters::ResolvedFilterPass> {
self.plan
.as_ref()
.map(|e| e.settle().to_vec())
.unwrap_or_default()
}
}
impl FilterChannel {
pub(super) fn drive(
&mut self,
input: Option<&crate::filters::FilterChain>,
mut resolved: Option<Mut<crate::filters::ResolvedFilterChain>>,
spec: Option<&ChannelTransition>,
registry: Option<&crate::filters::FilterRegistry>,
assets: Option<&AssetServer>,
dt: f32,
) -> bool {
let retargeted = match input {
Some(fi) => *fi != self.wire,
None => !self.wire.0.is_empty(),
};
if retargeted {
let to_wire = input.cloned().unwrap_or_default();
let from_wire = std::mem::replace(&mut self.wire, to_wire);
match (spec, resolved.as_deref()) {
(Some(spec), Some(chain)) if !self.wire.0.is_empty() => {
self.channel.interp.plan = Some(crate::filters::plan_filter_ease(
&from_wire,
&self.wire,
self.channel.current.clone(),
chain.passes.clone(),
registry,
assets,
chain.scale,
));
self.channel.arm(chain.passes.clone(), spec);
}
_ => {
self.channel.interp.plan = None;
self.channel.init(
resolved
.as_deref()
.map(|c| c.passes.clone())
.unwrap_or_default(),
);
}
}
}
let mut wrote = false;
if self.channel.runner.is_some() {
match resolved.as_mut() {
Some(resolved) => {
self.channel.tick(dt);
if resolved.passes != self.channel.current {
let chain = &mut **resolved;
chain.passes = self.channel.current.clone();
chain.version = chain.version.wrapping_add(1);
wrote = true;
}
}
None => {
self.channel.interp.plan = None;
self.channel.init(Vec::new());
}
}
}
wrote
}
}
#[derive(Default)]
pub(super) struct Channel {
pub(super) current: f32,
pub(super) target: f32,
pub(super) runner: Option<Runner>,
}
impl Channel {
pub(super) fn init(&mut self, value: f32) {
self.current = value;
self.target = value;
self.runner = None;
}
pub(super) fn drive(&mut self, target: f32, spec: Option<&ChannelTransition>, dt: f32) -> f32 {
if target != self.target {
self.target = target;
match spec {
Some(s) => self.runner = Some(build_runner(&s.to_driver(target), self.current)),
None => {
self.current = target;
self.runner = None;
}
}
}
if let Some(r) = self.runner.as_mut() {
let (v, done) = r.step(dt);
self.current = v;
if done {
self.runner = None;
}
}
self.current
}
}
pub(super) trait Interp<T> {
fn arm(&mut self, from: &T, to: &T);
fn sample(&self, p: f32, target: &T) -> T;
fn settle(&self, target: &T) -> T;
}
#[derive(Default)]
pub(super) struct EasedChannel<T, I> {
pub(super) current: T,
pub(super) target: T,
pub(super) interp: I,
pub(super) runner: Option<Runner>,
}
impl<T: Clone + PartialEq, I: Interp<T>> EasedChannel<T, I> {
pub(super) fn init(&mut self, value: T) {
self.current = value.clone();
self.target = value;
self.runner = None;
}
pub(super) fn arm(&mut self, target: T, spec: &ChannelTransition) {
self.interp.arm(&self.current, &target);
self.target = target;
self.runner = Some(build_runner(&spec.to_driver(1.0), 0.0));
}
pub(super) fn tick(&mut self, dt: f32) -> Option<bool> {
let r = self.runner.as_mut()?;
let (p, done) = r.step(dt);
self.current = if done {
self.runner = None;
self.interp.settle(&self.target)
} else {
self.interp.sample(p, &self.target)
};
Some(done)
}
pub(super) fn drive(&mut self, target: T, spec: Option<&ChannelTransition>, dt: f32) -> T {
if target != self.target {
match spec {
Some(s) => self.arm(target, s),
None => self.init(target),
}
}
self.tick(dt);
self.current.clone()
}
}
#[derive(Default)]
pub(super) struct LerpInterp<T> {
start: T,
}
impl<T: Lerp> Interp<T> for LerpInterp<T> {
fn arm(&mut self, from: &T, _to: &T) {
self.start = *from;
}
fn sample(&self, p: f32, target: &T) -> T {
self.start.lerp(*target, p)
}
fn settle(&self, target: &T) -> T {
*target
}
}
pub(super) type ProgressChannel<T> = EasedChannel<T, LerpInterp<T>>;
impl Lerp for Length {
fn lerp(self, other: Self, t: f32) -> Self {
use Length::*;
let lerp = |x: f32, y: f32| x + (y - x) * t;
match (self, other) {
(Px(x), Px(y)) => Px(lerp(x, y)),
(Percent(x), Percent(y)) => Percent(lerp(x, y)),
(Vw(x), Vw(y)) => Vw(lerp(x, y)),
(Vh(x), Vh(y)) => Vh(lerp(x, y)),
(VMin(x), VMin(y)) => VMin(lerp(x, y)),
(VMax(x), VMax(y)) => VMax(lerp(x, y)),
_ => other,
}
}
}