tachyonfx 0.25.0

A ratatui library for creating shader-like effects in TUIs.
Documentation
use ratatui_core::layout::Rect;

use crate::{RangeSampler, SimpleRng};

/// Specifies the direction of movement for visual effects like sweeps and slides.
///
/// This enum defines the four cardinal directions that effects can move in. It is used by
/// various effects to determine their direction of animation, such as sweep effects,
/// slide transitions, and other directional visual effects.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum Motion {
    /// Movement from left to right
    LeftToRight,
    /// Movement from right to left
    RightToLeft,
    /// Movement from top to bottom
    UpToDown,
    /// Movement from bottom to top
    DownToUp,
}

impl Motion {
    /// Returns the opposite direction of the current motion.
    ///
    /// This is used internally by effects that need to reverse their direction,
    /// such as when implementing ping-pong animations or transitioning between states.
    ///
    /// # Returns
    ///
    /// The opposite direction of the current motion
    pub(crate) fn flipped(self) -> Self {
        match self {
            Self::LeftToRight => Self::RightToLeft,
            Self::RightToLeft => Self::LeftToRight,
            Self::UpToDown => Self::DownToUp,
            Self::DownToUp => Self::UpToDown,
        }
    }

    /// Determines whether this motion direction requires timer reversal.
    ///
    /// Some motions (RightToLeft and DownToUp) require the effect timer to be reversed
    /// to maintain consistent animation behavior. This method is used internally by
    /// effects to determine if they should reverse their timer.
    ///
    /// # Returns
    ///
    /// `true` if the motion requires timer reversal (RightToLeft or DownToUp),
    /// `false` otherwise.
    pub(crate) fn flips_timer(self) -> bool {
        self == Motion::RightToLeft || self == Motion::DownToUp
    }
}

/// Generates random variances for directional effects.
pub(crate) struct DirectionalVariance {
    rng: SimpleRng,
    direction: Motion,
    max: i16,
}

impl DirectionalVariance {
    /// Creates a new `DirectionalVariance` instance.
    ///
    /// This method initializes a `DirectionalVariance` with a seed based on the given
    /// area's dimensions, the specified direction for the sliding effect, and the
    /// maximum variance allowed.
    ///
    /// # Arguments
    ///
    /// * `area` - The `Rect` representing the area of the effect. Used to seed the RNG.
    /// * `direction` - The `Direction` of the sliding effect.
    /// * `max` - The maximum variance that can be generated.
    ///
    /// # Returns
    ///
    /// A new `DirectionalVariance` instance.
    #[allow(dead_code)]
    pub(super) fn from(area: Rect, direction: Motion, max: u16) -> Self {
        Self {
            rng: SimpleRng::new(((area.width as u32) << 16) | area.height as u32),
            direction,
            max: max as i16,
        }
    }

    /// Creates a new `DirectionalVariance` instance with a provided RNG.
    ///
    /// # Arguments
    ///
    /// * `rng` - The `SimpleRng` to use for generating variances.
    /// * `direction` - The `Direction` of the sliding effect.
    /// * `max` - The maximum variance that can be generated.
    ///
    /// # Returns
    ///
    /// A new `DirectionalVariance` instance.
    pub(super) fn with_rng(rng: SimpleRng, direction: Motion, max: u16) -> Self {
        Self { rng, direction, max: max as i16 }
    }

    /// Generates the next variance value.
    ///
    /// This method produces a tuple representing an (x, y) offset based on the
    /// configured direction and maximum variance. The generated variance is always
    /// within the range [-max, max] for the relevant axis, and 0 for the other axis.
    ///
    /// # Returns
    ///
    /// A tuple `(i16, i16)` representing the (x, y) variance to be applied.
    /// If the maximum variance is set to 0, it always returns (0, 0).
    pub(crate) fn next(&mut self) -> (i16, i16) {
        if self.max == 0 {
            return (0, 0);
        }

        let variance = self.rng.gen_range(0..self.max);
        match self.direction {
            Motion::LeftToRight => (variance, 0),
            Motion::RightToLeft => (-variance, 0),
            Motion::UpToDown => (0, variance),
            Motion::DownToUp => (0, -variance),
        }
    }
}