use std::{fmt::Debug, iter::zip};
use itertools::Itertools;
use tui::{style::Color, symbols};
#[derive(Debug)]
pub(super) struct Layer {
pub(super) contents: Vec<LayerCell>,
}
#[derive(Debug)]
pub(super) struct LayerCell {
pub(super) symbol: Option<char>,
pub(super) fg: Option<Color>,
pub(super) bg: Option<Color>,
}
pub(super) trait Grid: Debug {
fn resolution(&self) -> (f64, f64);
fn paint(&mut self, x: usize, y: usize, color: Color);
fn save(&self) -> Layer;
fn reset(&mut self);
}
#[derive(Copy, Clone, Debug, Default)]
struct PatternCell {
pattern: u8,
color: Option<Color>,
}
#[derive(Debug)]
pub(super) struct PatternGrid<const W: usize, const H: usize> {
width: u16,
height: u16,
cells: Vec<PatternCell>,
char_table: &'static [char],
}
impl<const W: usize, const H: usize> PatternGrid<W, H> {
const _PATTERN_DIMENSION_CHECK: usize = u8::BITS as usize - W * H;
pub(super) fn new(width: u16, height: u16, char_table: &'static [char]) -> Self {
let _ = Self::_PATTERN_DIMENSION_CHECK;
let length = usize::from(width) * usize::from(height);
Self {
width,
height,
cells: vec![PatternCell::default(); length],
char_table,
}
}
}
impl<const W: usize, const H: usize> Grid for PatternGrid<W, H> {
fn resolution(&self) -> (f64, f64) {
(
f64::from(self.width) * W as f64,
f64::from(self.height) * H as f64,
)
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
let index = y
.saturating_div(H)
.saturating_mul(self.width as usize)
.saturating_add(x.saturating_div(W));
if let Some(cell) = self.cells.get_mut(index) {
if let Some(curr_color) = &mut cell.color {
if *curr_color != color {
*curr_color = color;
cell.pattern = 1u8 << ((x % W) + W * (y % H));
} else {
cell.pattern |= 1u8 << ((x % W) + W * (y % H));
}
} else {
cell.color = Some(color);
cell.pattern = 1u8 << ((x % W) + W * (y % H));
}
}
}
fn save(&self) -> Layer {
let contents = self
.cells
.iter()
.map(|&cell| {
let symbol = match cell.pattern {
0 => None,
idx => Some(self.char_table[idx as usize]),
};
LayerCell {
symbol,
fg: cell.color,
bg: None,
}
})
.collect();
Layer { contents }
}
fn reset(&mut self) {
self.cells.fill_with(Default::default);
}
}
#[derive(Debug)]
pub(super) struct CharGrid {
width: u16,
height: u16,
cells: Vec<Option<Color>>,
cell_char: char,
apply_color_to_bg: bool,
}
impl CharGrid {
pub(super) fn new(width: u16, height: u16, cell_char: char) -> Self {
let length = usize::from(width) * usize::from(height);
Self {
width,
height,
cells: vec![None; length],
cell_char,
apply_color_to_bg: false,
}
}
pub(super) fn apply_color_to_bg(self) -> Self {
Self {
apply_color_to_bg: true,
..self
}
}
}
impl Grid for CharGrid {
fn resolution(&self) -> (f64, f64) {
(f64::from(self.width), f64::from(self.height))
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
let index = y.saturating_mul(self.width as usize).saturating_add(x);
if let Some(c) = self.cells.get_mut(index) {
*c = Some(color);
}
}
fn save(&self) -> Layer {
Layer {
contents: self
.cells
.iter()
.map(|&color| LayerCell {
symbol: color.map(|_| self.cell_char),
fg: color,
bg: color.filter(|_| self.apply_color_to_bg),
})
.collect(),
}
}
fn reset(&mut self) {
self.cells.fill(None);
}
}
#[derive(Debug)]
pub(super) struct HalfBlockGrid {
width: u16,
height: u16,
pixels: Vec<Vec<Option<Color>>>,
}
impl HalfBlockGrid {
pub(super) fn new(width: u16, height: u16) -> Self {
Self {
width,
height,
pixels: vec![vec![None; width as usize]; (height as usize) * 2],
}
}
}
impl Grid for HalfBlockGrid {
fn resolution(&self) -> (f64, f64) {
(f64::from(self.width), f64::from(self.height) * 2.0)
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
self.pixels[y][x] = Some(color);
}
fn save(&self) -> Layer {
let vertical_color_pairs = self
.pixels
.iter()
.tuples()
.flat_map(|(upper_row, lower_row)| zip(upper_row, lower_row));
let contents = vertical_color_pairs
.map(|(upper, lower)| {
let (symbol, fg, bg) = match (upper, lower) {
(None, None) => (None, None, None),
(None, Some(lower)) => (Some(symbols::half_block::LOWER), Some(*lower), None),
(Some(upper), None) => (Some(symbols::half_block::UPPER), Some(*upper), None),
(Some(upper), Some(lower)) if lower == upper => {
(Some(symbols::half_block::FULL), Some(*upper), Some(*lower))
}
(Some(upper), Some(lower)) => {
(Some(symbols::half_block::UPPER), Some(*upper), Some(*lower))
}
};
LayerCell { symbol, fg, bg }
})
.collect();
Layer { contents }
}
fn reset(&mut self) {
self.pixels.fill(vec![None; self.width as usize]);
}
}