#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RampingMode {
Step,
Linear,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RampingProfile {
pub mode: RampingMode,
pub steps: usize,
}
impl RampingProfile {
#[must_use]
pub const fn immediate() -> Self {
Self {
mode: RampingMode::Step,
steps: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct RampingF32 {
profile: RampingProfile,
initial_value: f32,
target_value: f32,
step_value: f32,
current_step: usize,
}
const MAX_LOSSLESS_STEPS_F32: usize = (1usize << 23) - 1;
#[expect(clippy::cast_precision_loss)]
#[inline]
fn steps_as_f32(steps: usize) -> f32 {
debug_assert!(steps <= MAX_LOSSLESS_STEPS_F32);
steps as f32
}
impl RampingF32 {
#[must_use]
pub const fn new(value: f32) -> Self {
Self {
profile: RampingProfile::immediate(),
initial_value: value,
target_value: value,
step_value: 0f32,
current_step: 0,
}
}
#[must_use]
pub const fn profile(&self) -> RampingProfile {
self.profile
}
pub fn reset(&mut self, target_value: f32) {
self.reset_profile(target_value, self.profile);
}
pub fn reset_profile(&mut self, target_value: f32, profile: RampingProfile) {
self.profile = profile;
self.initial_value = self.current_value();
self.target_value = target_value;
let RampingProfile { mode, steps } = profile;
self.step_value = if steps > 0 {
match mode {
RampingMode::Step => 0f32,
RampingMode::Linear => (target_value - self.initial_value) / steps_as_f32(steps),
}
} else {
0f32
};
self.current_step = 0;
}
#[must_use]
pub fn current_value(&self) -> f32 {
let RampingProfile { mode, steps } = self.profile;
if self.current_step < steps {
match mode {
RampingMode::Step => self.initial_value,
RampingMode::Linear => {
self.initial_value + self.step_value * steps_as_f32(self.current_step)
}
}
} else {
self.target_value
}
}
#[must_use]
pub const fn target_value(&self) -> f32 {
self.target_value
}
#[must_use]
pub fn remaining_steps(&self) -> usize {
debug_assert!(self.current_step <= self.profile.steps);
self.profile.steps - self.current_step
}
pub fn advance(&mut self, steps: usize) {
if steps < self.remaining_steps() {
self.current_step += steps;
} else {
self.current_step = self.profile.steps;
};
}
}
impl Iterator for RampingF32 {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
let current_value = self.current_value();
self.advance(1);
Some(current_value)
}
}