use rand::Rng;
use rand_chacha::rand_core::SeedableRng;
use ratatui::{buffer::Cell, layout::Flex, prelude::*};
use unicode_width::UnicodeWidthStr;
use crate::big_text::{BigTextBuilder, PixelSize};
const DELAY: usize = 240;
const DRIP_SPEED: usize = 50;
const TEXT_DELAY: usize = 240;
pub fn destroy(frame: &mut Frame<'_>) {
let frame_count = frame.count().saturating_sub(DELAY);
if frame_count == 0 {
return;
}
let area = frame.size();
let buf = frame.buffer_mut();
drip(frame_count, area, buf);
text(frame_count, area, buf);
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss
)]
fn drip(frame_count: usize, area: Rect, buf: &mut Buffer) {
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(10);
let ramp_frames = 450;
let fractional_speed = frame_count as f64 / f64::from(ramp_frames);
let variable_speed = DRIP_SPEED as f64 * fractional_speed * fractional_speed * fractional_speed;
let pixel_count = (frame_count as f64 * variable_speed).floor() as usize;
for _ in 0..pixel_count {
let src_x = rng.gen_range(0..area.width);
let src_y = rng.gen_range(1..area.height - 2);
let src = buf.get_mut(src_x, src_y).clone();
if rng.gen_ratio(1, 100) {
let dest_x = rng
.gen_range(src_x.saturating_sub(5)..src_x.saturating_add(5))
.clamp(area.left(), area.right() - 1);
let dest_y = area.top() + 1;
let dest = buf.get_mut(dest_x, dest_y);
if rng.gen_ratio(1, 10) {
*dest = src;
} else {
*dest = Cell::default();
}
} else {
let dest_x = src_x;
let dest_y = src_y.saturating_add(1).min(area.bottom() - 2);
let dest = buf.get_mut(dest_x, dest_y);
*dest = src;
}
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
fn text(frame_count: usize, area: Rect, buf: &mut Buffer) {
let sub_frame = frame_count.saturating_sub(TEXT_DELAY);
if sub_frame == 0 {
return;
}
let line = "RATATUI";
let big_text = BigTextBuilder::default()
.lines([line.into()])
.pixel_size(PixelSize::Full)
.style(Style::new().fg(Color::Rgb(255, 0, 0)))
.build()
.unwrap();
let area = centered_rect(area, line.width() as u16 * 8, 8);
let mask_buf = &mut Buffer::empty(area);
big_text.render(area, mask_buf);
let percentage = (sub_frame as f64 / 480.0).clamp(0.0, 1.0);
for row in area.rows() {
for col in row.columns() {
let cell = buf.get_mut(col.x, col.y);
let mask_cell = mask_buf.get(col.x, col.y);
cell.set_symbol(mask_cell.symbol());
let cell_color = cell.style().bg.unwrap_or(Color::Rgb(0, 0, 0));
let mask_color = mask_cell.style().fg.unwrap_or(Color::Rgb(255, 0, 0));
let color = blend(mask_color, cell_color, percentage);
cell.set_style(Style::new().fg(color));
}
}
}
fn blend(mask_color: Color, cell_color: Color, percentage: f64) -> Color {
let Color::Rgb(mask_red, mask_green, mask_blue) = mask_color else {
return mask_color;
};
let Color::Rgb(cell_red, cell_green, cell_blue) = cell_color else {
return mask_color;
};
let remain = 1.0 - percentage;
let red = f64::from(mask_red).mul_add(percentage, f64::from(cell_red) * remain);
let green = f64::from(mask_green).mul_add(percentage, f64::from(cell_green) * remain);
let blue = f64::from(mask_blue).mul_add(percentage, f64::from(cell_blue) * remain);
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Color::Rgb(red as u8, green as u8, blue as u8)
}
fn centered_rect(area: Rect, width: u16, height: u16) -> Rect {
let horizontal = Layout::horizontal([width]).flex(Flex::Center);
let vertical = Layout::vertical([height]).flex(Flex::Center);
let [area] = vertical.areas(area);
let [area] = horizontal.areas(area);
area
}