use crate::error::Result;
use crate::themes;
use colorgrad::{Color, Gradient};
use std::f32::consts::PI;
#[derive(Debug, Clone)]
pub struct GradientConfig {
pub diagonal: bool,
pub angle: i32,
pub cycle: bool,
}
pub struct GradientEngine {
gradient: Box<dyn Gradient + Send + Sync>,
config: GradientConfig,
total_lines: usize,
current_line: usize,
cached_sin: f32,
cached_cos: f32,
}
impl GradientEngine {
#[inline]
pub fn new(gradient: Box<dyn Gradient + Send + Sync>, config: GradientConfig) -> Self {
let (cached_sin, cached_cos) = if config.diagonal {
let angle_rad = (config.angle as f32) * (PI / 180.0);
(angle_rad.sin(), angle_rad.cos())
} else {
(0.0, 0.0)
};
Self {
gradient,
config,
total_lines: 0,
current_line: 0,
cached_sin,
cached_cos,
}
}
#[inline]
pub fn from_theme(theme_name: &str, config: GradientConfig) -> Result<Self> {
let theme = themes::get_theme(theme_name)?;
let gradient = theme.create_gradient()?;
Ok(Self::new(gradient, config))
}
#[inline]
pub fn set_total_lines(&mut self, total_lines: usize) {
self.total_lines = total_lines;
}
#[inline]
pub fn set_current_line(&mut self, line: usize) {
self.current_line = line;
}
#[inline]
pub fn get_color_at(&self, char_index: usize, line_length: usize) -> Result<Color> {
let t = if self.config.diagonal && self.total_lines > 1 {
self.calculate_diagonal_position(char_index, line_length)
} else {
self.calculate_horizontal_position(char_index, line_length)
};
Ok(self.gradient.at(t))
}
#[inline(always)]
fn calculate_horizontal_position(&self, char_index: usize, line_length: usize) -> f32 {
if line_length <= 1 {
return 0.0;
}
let mut t = char_index as f32 / (line_length - 1) as f32;
if self.config.cycle {
t = (t * PI).sin() * 0.5 + 0.5;
}
t.clamp(0.0, 1.0)
}
#[inline(always)]
fn calculate_diagonal_position(&self, char_index: usize, line_length: usize) -> f32 {
if self.total_lines <= 1 || line_length <= 1 {
return 0.0;
}
let x = char_index as f32 / (line_length - 1) as f32;
let y = self.current_line as f32 / (self.total_lines - 1) as f32;
let mut t = x * self.cached_cos + y * self.cached_sin;
if self.config.cycle {
t = (t * PI).sin() * 0.5 + 0.5;
}
t.clamp(0.0, 1.0)
}
}
impl Default for GradientConfig {
fn default() -> Self {
Self {
diagonal: false,
angle: 45,
cycle: false,
}
}
}