#[cfg(test)]
mod test;
use std::time::Duration;
use crate::{
StartTime, Tween, Tweenable, Value,
command::{CommandReader, ValueChangeCommand},
info::{Info, WhenToStart},
};
#[derive(Clone)]
pub struct Parameter<T: Tweenable = f64> {
state: State<T>,
raw_value: T,
previous_raw_value: T,
stagnant: bool,
}
impl<T: Tweenable> Parameter<T> {
#[must_use]
pub fn new(initial_value: Value<T>, default_raw_value: T) -> Self {
let raw_value = match initial_value {
Value::Fixed(value) => value,
Value::FromModulator { .. } => default_raw_value,
Value::FromListenerDistance { .. } => default_raw_value,
};
Self {
state: State::Idle {
value: initial_value,
},
raw_value,
previous_raw_value: raw_value,
stagnant: matches!(initial_value, Value::Fixed(_)),
}
}
#[must_use]
pub fn value(&self) -> T {
self.raw_value
}
#[must_use]
pub fn previous_value(&self) -> T {
self.previous_raw_value
}
#[must_use]
pub fn interpolated_value(&self, amount: f64) -> T {
T::interpolate(self.previous_raw_value, self.raw_value, amount)
}
pub fn set(&mut self, target: Value<T>, tween: Tween) {
self.stagnant = false;
self.state = State::Tweening {
start: self.value(),
target,
time: 0.0,
tween,
};
}
pub fn read_command(&mut self, command_reader: &mut CommandReader<ValueChangeCommand<T>>)
where
T: Send,
{
if let Some(ValueChangeCommand { target, tween }) = command_reader.read() {
self.set(target, tween);
}
}
pub fn update(&mut self, dt: f64, info: &Info) -> JustFinishedTween {
self.previous_raw_value = self.raw_value;
if self.stagnant {
return false;
}
let just_finished_tween = self.update_tween(dt, info);
if let Some(raw_value) = self.calculate_new_raw_value(info) {
self.raw_value = raw_value;
}
just_finished_tween
}
fn update_tween(&mut self, dt: f64, info: &Info) -> JustFinishedTween {
if let State::Tweening {
target,
time,
tween,
..
} = &mut self.state
{
let started = match &mut tween.start_time {
StartTime::Immediate => true,
StartTime::Delayed(time_remaining) => {
if time_remaining.is_zero() {
true
} else {
*time_remaining =
time_remaining.saturating_sub(Duration::from_secs_f64(dt));
false
}
}
StartTime::ClockTime(clock_time) => {
info.when_to_start(*clock_time) == WhenToStart::Now
}
};
if !started {
return false;
}
*time += dt;
if *time >= tween.duration.as_secs_f64() {
if matches!(target, Value::Fixed(_)) {
self.stagnant = true;
}
self.state = State::Idle { value: *target };
return true;
}
}
false
}
fn calculate_new_raw_value(&self, info: &Info) -> Option<T> {
match &self.state {
State::Idle { value } => value.raw_value(info),
State::Tweening {
start,
target,
time,
tween,
} => {
if tween.duration.is_zero() {
return None;
}
target
.raw_value(info)
.map(|target| T::interpolate(*start, target, tween.value(*time)))
}
}
}
}
#[derive(Clone)]
enum State<T: Tweenable> {
Idle {
value: Value<T>,
},
Tweening {
start: T,
target: Value<T>,
time: f64,
tween: Tween,
},
}
type JustFinishedTween = bool;