mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
use core::fmt::{self, Debug, Formatter};

/// The whole of one cycle.
const WHOLE: f32 = 1.0;

/// How far along what it plays a machine is, which a state reads to decide
/// where to go next.
///
/// A cycle is one run of the clip, from its first key to its last. A
/// looping motion and a blend count cycles without end; one that holds at
/// its last key stops at the first. No read of it changes anything, so the
/// machine may read a state again over a tick's own span to reach the
/// instant inside it a transition started at.
#[derive(Clone, Copy)]
pub struct Progress {
    /// The cycles run, counted on past the end of a motion that holds
    /// there.
    run: f32,
    /// Whether the motion holds at the end of one cycle.
    ends: bool,
}

impl Progress {
    /// The progress `run` cycles into a motion, which holds at the end of
    /// the first where `ends`.
    pub(crate) fn new(run: f32, ends: bool) -> Self {
        Self {
            run: match run.is_finite() {
                true => run.max(0.0),
                false => 0.0,
            },
            ends,
        }
    }

    /// Whether the motion has run its whole cycle and holds there, which a
    /// looping motion and a blend never do.
    pub fn ended(&self) -> bool {
        self.ends && self.run >= WHOLE
    }

    /// Whether it has gone past `fraction` of the cycle it is in.
    pub fn past(&self, fraction: f32) -> bool {
        self.fraction() >= fraction
    }

    /// The cycles of it that have run whole.
    pub fn cycle(&self) -> u32 {
        self.counted().floor().max(0.0) as u32
    }

    /// How far into the cycle it is in it lies, a fraction in `0.0..=1.0`.
    pub fn fraction(&self) -> f32 {
        match self.ended() {
            true => WHOLE,
            false => self.counted().fract().clamp(0.0, WHOLE),
        }
    }

    /// The cycles run, held to the one a motion that holds at its last key
    /// stops in.
    fn counted(&self) -> f32 {
        match self.ends {
            true => self.run.min(WHOLE),
            false => self.run,
        }
    }
}

impl Debug for Progress {
    /// The cycles run and how far into the one it is in, which is what a
    /// state reads it for.
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("Progress")
            .field("cycle", &self.cycle())
            .field("fraction", &self.fraction())
            .field("ended", &self.ended())
            .finish()
    }
}