tachyonfx 0.25.0

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

use crate::Motion;

#[derive(Clone, Copy, Debug)]
pub struct SlidingWindowAlpha {
    alpha_fn: fn(Position, Gradient, f32) -> f32,
    gradient: Gradient,
    alpha_per_cell: f32,
}

#[derive(Clone, Copy, Debug)]
struct Gradient {
    start: f32,
    end: f32,
}

#[bon::bon]
impl SlidingWindowAlpha {
    #[builder(finish_fn = build)]
    pub fn builder(direction: Motion, area: Rect, progress: f32, gradient_len: u16) -> Self {
        let alpha_fn = match direction {
            Motion::UpToDown => move_up_to_down,
            Motion::DownToUp => move_down_to_up,
            Motion::LeftToRight => move_left_to_right,
            Motion::RightToLeft => move_right_to_left,
        };

        let gradient = match direction {
            Motion::LeftToRight | Motion::RightToLeft => {
                gradient(progress, area.x, area.width, gradient_len)
            },
            Motion::UpToDown | Motion::DownToUp => {
                gradient(progress, area.y, area.height, gradient_len)
            },
        };

        let alpha_per_cell = 1.0 / (gradient.end - gradient.start);
        Self { alpha_fn, gradient, alpha_per_cell }
    }

    pub fn alpha(&self, position: Position) -> f32 {
        (self.alpha_fn)(position, self.gradient, self.alpha_per_cell)
    }
}

fn gradient(progress: f32, coordinate: u16, area_len: u16, gradient_len: u16) -> Gradient {
    let gradient_len = gradient_len as f32;
    let start = (coordinate as f32 - gradient_len) + ((area_len as f32 + gradient_len) * progress);
    let end = start + gradient_len;

    Gradient { start, end }
}

fn move_down_to_up(position: Position, gradient: Gradient, alpha_per_cell: f32) -> f32 {
    match position.y as f32 {
        y if y < gradient.start => 0.0,
        y if y > gradient.end => 1.0,
        y => alpha_per_cell * (y - gradient.start),
    }
}

fn move_up_to_down(position: Position, gradient: Gradient, alpha_per_cell: f32) -> f32 {
    1.0 - move_down_to_up(position, gradient, alpha_per_cell)
}

fn move_right_to_left(position: Position, gradient: Gradient, alpha_per_cell: f32) -> f32 {
    match position.x as f32 {
        x if x < gradient.start => 0.0,
        x if x > gradient.end => 1.0,
        x => alpha_per_cell * (x - gradient.start),
    }
}

fn move_left_to_right(position: Position, gradient: Gradient, alpha_per_cell: f32) -> f32 {
    1.0 - move_right_to_left(position, gradient, alpha_per_cell)
}