use glam::Vec3;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::Color;
use ratatui::widgets::Widget;
pub struct Framebuffer {
pub width: u16,
pub height: u16,
pub pixels: Vec<Vec3>,
}
impl Framebuffer {
pub fn new(width: u16, height: u16) -> Self {
Self {
width,
height,
pixels: vec![Vec3::ZERO; width as usize * height as usize],
}
}
pub fn resize(&mut self, width: u16, height: u16) {
if self.width == width && self.height == height {
return;
}
self.width = width;
self.height = height;
self.pixels.resize(width as usize * height as usize, Vec3::ZERO);
}
#[inline]
pub fn idx(&self, x: u16, y: u16) -> usize {
y as usize * self.width as usize + x as usize
}
#[allow(dead_code)]
pub fn clear(&mut self, color: Vec3) {
self.pixels.fill(color);
}
}
pub struct FramebufferView<'a> {
pub fb: &'a Framebuffer,
}
impl<'a> Widget for FramebufferView<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
let fb = self.fb;
let cols = area.width.min(fb.width);
let rows_cells = area.height.min(fb.height / 2);
for cy in 0..rows_cells {
let y_top = cy * 2;
let y_bot = y_top + 1;
for cx in 0..cols {
let top = fb.pixels[fb.idx(cx, y_top)];
let bot = fb.pixels[fb.idx(cx, y_bot)];
let cell = &mut buf[(area.x + cx, area.y + cy)];
cell.set_char('\u{2580}'); cell.set_fg(vec3_to_color(top));
cell.set_bg(vec3_to_color(bot));
}
}
}
}
#[inline]
fn vec3_to_color(c: Vec3) -> Color {
let r = (c.x.clamp(0.0, 1.0) * 255.0) as u8;
let g = (c.y.clamp(0.0, 1.0) * 255.0) as u8;
let b = (c.z.clamp(0.0, 1.0) * 255.0) as u8;
Color::Rgb(r, g, b)
}