view interpreters/src/th06/interpolator.rs @ 787:7f9b3f5001c2

interpreters: Make Interpolator generic over N This was a workaround for Rust < 1.51 which didn’t support const generics yet, but we’ve had tat for close to five years now!
author Link Mauve <linkmauve@linkmauve.fr>
date Mon, 15 Dec 2025 11:34:58 +0100
parents 21b186be2590
children
line wrap: on
line source

//! Animation runner.

#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum Formula {
    Linear,
    Power2,
    InvertPower2,
}

impl Formula {
    fn apply(&self, x: f32) -> f32 {
        match self {
            Formula::Linear => x,
            Formula::Power2 => x * x,
            Formula::InvertPower2 => 2. * x - x * x,
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct Interpolator<T, const N: usize> {
    start_values: [T; N],
    end_values: [T; N],
    start_frame: u32,
    end_frame: u32,
    formula: Formula,
}

impl<T, const N: usize> Interpolator<T, N>
where f32: From<T>,
      T: From<f32>,
      T: std::ops::Sub<Output = T>,
      T: std::ops::Add<Output = T>,
      T: Copy,
      T: Default,
{
    pub fn new(start_values: [T; N], start_frame: u32, end_values: [T; N], end_frame: u32, formula: Formula) -> Interpolator<T, N> {
        Interpolator {
            start_values,
            end_values,
            start_frame,
            end_frame,
            formula,
        }
    }

    pub fn set_start(&mut self, frame: u32, values: [T; N]) {
        self.start_values = values;
        self.start_frame = frame;
    }

    pub fn set_end(&mut self, frame: u32, values: [T; N]) {
        self.end_values = values;
        self.end_frame = frame;
    }

    pub fn set_end_values(&mut self, values: [T; N]) {
        self.end_values = values;
    }

    pub fn set_end_frame(&mut self, frame: u32) {
        self.end_frame = frame;
    }

    // XXX: Make it return [T; N] instead, we don’t want to only do f32 here.
    pub fn values(&self, frame: u32) -> [f32; N] {
        if frame + 1 >= self.end_frame {
            // XXX: skip the last interpolation step.
            // This bug is replicated from the original game.
            //self.start_frame = self.end_frame;
            //self.end_values
            let mut values: [f32; N] = [Default::default(); N];
            for (i, value) in self.end_values.iter().enumerate() {
                values[i] = f32::from(*value);
            }
            values
        } else {
            let mut coeff = (frame - self.start_frame) as f32 / (self.end_frame - self.start_frame) as f32;
            coeff = self.formula.apply(coeff);
            let mut values: [f32; N] = [Default::default(); N];
            for (i, (start, end)) in self.start_values.iter().zip(&self.end_values).enumerate() {
                values[i] = f32::from(*start + T::from(coeff * f32::from(*end - *start)));
            }
            values
        }
    }
}