use fixed::types::I22F10;
use std::f32::consts;
use twmap::{CurveKind, Env, Envelope, Position, Volume};
use vek::az::{Az, UnwrappedAs};
use vek::Rgba;
trait IntoEnvValue {
fn into_env_val(self) -> Rgba<f32>;
}
impl IntoEnvValue for Volume {
fn into_env_val(self) -> Rgba<f32> {
Rgba::new(self.0.az(), 0., 0., 0.)
}
}
impl IntoEnvValue for Position {
fn into_env_val(self) -> Rgba<f32> {
Rgba::new(
self.offset.x.az(),
self.offset.y.az(),
self.rotation.az::<f32>() / 180. * consts::PI,
0.,
)
}
}
impl IntoEnvValue for Rgba<I22F10> {
fn into_env_val(self) -> Rgba<f32> {
self.az()
}
}
pub fn interpolate<T>(frac: f32, curve: CurveKind<T>) -> f32 {
use CurveKind::*;
match curve {
Step => 0.,
Linear => frac,
Slow => frac.powi(3),
Fast => 1. - (1. - frac).powi(3),
Smooth | Bezier(_) => 3. * frac.powi(2) - 2. * frac.powi(3),
Unknown(_) => 0.5,
}
}
pub trait EnvelopeSampling {
fn sample(&self, client_micros: i64, server_micros: i64, offset: i32) -> Rgba<f32>;
}
impl<T: Copy + IntoEnvValue> EnvelopeSampling for Env<T> {
fn sample(&self, client_micros: i64, server_micros: i64, offset: i32) -> Rgba<f32> {
match self.points.as_slice() {
[] => return Rgba::zero(),
[one] => return one.content.into_env_val(),
_ => {}
}
let mut micros: i64 = match self.synchronized {
true => server_micros,
false => client_micros,
};
micros += i64::from(offset) * 1000;
let modulo = self.points.last().unwrap().time;
if modulo != 0 {
micros %= i64::from(modulo) * 1000;
}
let millis: i32 = (micros / 1000).unwrapped_as();
if self.points[0].time > millis {
return self.points.last().unwrap().content.into_env_val();
}
let interpolation_points = self
.points
.windows(2)
.find(|tuple| tuple[0].time <= millis && tuple[1].time >= millis)
.unwrap();
let left_micros = i64::from(interpolation_points[0].time) * 1000;
let right_micros = i64::from(interpolation_points[1].time) * 1000;
let time_span_micros = right_micros - left_micros;
let time_span_moment = micros - left_micros;
let frac = time_span_moment as f32 / time_span_micros as f32;
let left = interpolation_points[0].content.into_env_val();
let right = interpolation_points[1].content.into_env_val();
left + (right - left) * interpolate(frac, interpolation_points[0].curve)
}
}
impl EnvelopeSampling for Envelope {
fn sample(&self, client_micros: i64, server_micros: i64, offset: i32) -> Rgba<f32> {
use Envelope::*;
match self {
Position(env) => env.sample(client_micros, server_micros, offset),
Color(env) => env.sample(client_micros, server_micros, offset),
Sound(env) => env.sample(client_micros, server_micros, offset),
}
}
}