use std::sync::Arc;
pub struct Signal<T> {
eval: Arc<dyn Fn(f64) -> T + Send + Sync>,
}
impl<T> Signal<T> {
pub fn new<F>(f: F) -> Self
where
F: Fn(f64) -> T + Send + Sync + 'static,
{
Signal { eval: Arc::new(f) }
}
#[inline]
pub fn at(&self, time: f64) -> T {
(self.eval)(time)
}
pub fn map<U, F>(self, f: F) -> Signal<U>
where
F: Fn(T) -> U + Send + Sync + 'static,
T: Send + 'static,
{
let eval = self.eval;
Signal::new(move |t| f(eval(t)))
}
}
impl<T> Clone for Signal<T> {
fn clone(&self) -> Self {
Signal {
eval: self.eval.clone(),
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for Signal<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Signal")
.field("eval", &"<function>")
.finish()
}
}
pub fn of<T, F>(f: F) -> Signal<T>
where
F: Fn(f64) -> T + Send + Sync + 'static,
{
Signal::new(f)
}
pub fn constant<T: Clone + Send + Sync + 'static>(value: T) -> Signal<T> {
Signal::new(move |_| value.clone())
}
pub fn time() -> Signal<f64> {
Signal::new(|t| t)
}
pub fn lerp(from: f64, to: f64, duration: f64) -> Signal<f64> {
Signal::new(move |t| {
let progress = (t / duration).clamp(0.0, 1.0);
from + (to - from) * progress
})
}
pub fn ease<F>(easing_fn: F, from: f64, to: f64, duration: f64) -> Signal<f64>
where
F: Fn(f64) -> f64 + Send + Sync + 'static,
{
Signal::new(move |t| {
let progress = (t / duration).clamp(0.0, 1.0);
let eased = easing_fn(progress);
from + (to - from) * eased
})
}
pub fn zip<A, B, C, F>(a: Signal<A>, b: Signal<B>, f: F) -> Signal<C>
where
A: Send + 'static,
B: Send + 'static,
F: Fn(A, B) -> C + Send + Sync + 'static,
{
Signal::new(move |t| f(a.at(t), b.at(t)))
}
pub fn zip3<A, B, C, D, F>(a: Signal<A>, b: Signal<B>, c: Signal<C>, f: F) -> Signal<D>
where
A: Send + 'static,
B: Send + 'static,
C: Send + 'static,
F: Fn(A, B, C) -> D + Send + Sync + 'static,
{
Signal::new(move |t| f(a.at(t), b.at(t), c.at(t)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signal_new_and_at() {
let sig = Signal::new(|t| t * 2.0);
assert_eq!(sig.at(0.0), 0.0);
assert_eq!(sig.at(1.5), 3.0);
assert_eq!(sig.at(5.0), 10.0);
}
#[test]
fn test_signal_clone() {
let sig = Signal::new(|t| t * 3.0);
let cloned = sig.clone();
assert_eq!(sig.at(2.0), cloned.at(2.0));
}
#[test]
fn test_signal_map() {
let sig = Signal::new(|t| t);
let doubled = sig.map(|v| v * 2.0);
assert_eq!(doubled.at(5.0), 10.0);
}
#[test]
fn test_of() {
let sig = of(|t| t.powi(2));
assert_eq!(sig.at(3.0), 9.0);
}
#[test]
fn test_constant() {
let sig = constant(42.0);
assert_eq!(sig.at(0.0), 42.0);
assert_eq!(sig.at(100.0), 42.0);
}
#[test]
fn test_time() {
let sig = time();
assert_eq!(sig.at(1.5), 1.5);
assert_eq!(sig.at(99.0), 99.0);
}
#[test]
fn test_lerp() {
let sig = lerp(0.0, 100.0, 2.0);
assert_eq!(sig.at(0.0), 0.0);
assert!((sig.at(1.0) - 50.0).abs() < 1e-10);
assert_eq!(sig.at(2.0), 100.0);
}
#[test]
fn test_lerp_clamped() {
let sig = lerp(0.0, 100.0, 2.0);
assert_eq!(sig.at(-1.0), 0.0); assert_eq!(sig.at(5.0), 100.0); }
#[test]
fn test_ease() {
let sig = ease(|p| p, 0.0, 100.0, 2.0);
assert!((sig.at(1.0) - 50.0).abs() < 1e-10);
}
#[test]
fn test_ease_quad() {
let sig = ease(|p| p * p, 0.0, 100.0, 1.0);
assert!((sig.at(0.5) - 25.0).abs() < 1e-10); }
#[test]
fn test_zip() {
let a = constant(3.0_f64);
let b = constant(4.0_f64);
let c = zip(a, b, |x, y| (x * x + y * y).sqrt());
assert_eq!(c.at(0.0), 5.0); }
#[test]
fn test_zip3() {
let a = constant(1.0);
let b = constant(2.0);
let c = constant(3.0);
let sum = zip3(a, b, c, |x, y, z| x + y + z);
assert_eq!(sum.at(0.0), 6.0);
}
#[test]
fn test_signal_vec_data() {
let data_signal = of(|t| (0..5).map(|i| i as f64 + t).collect::<Vec<f64>>());
let at_0 = data_signal.at(0.0);
assert_eq!(at_0, vec![0.0, 1.0, 2.0, 3.0, 4.0]);
let at_10 = data_signal.at(10.0);
assert_eq!(at_10, vec![10.0, 11.0, 12.0, 13.0, 14.0]);
}
}