use crate::Color;
use crate::Dimensions;
use crate::Graphics2D;
use crate::Rect;
use crate::Result;
use crate::Scaling;
use crate::SpriteBatch;
use crate::SpriteMap;
use crate::SpriteSheet;
use crate::Translation;
use std::rc::Rc;
pub struct TextGrid {
smap: SpriteMap,
dim: [u32; 2],
raw_char_dim: Dimensions,
}
impl TextGrid {
const CHAR_MAP_LAYOUT_DIM: [u32; 2] = [3, 32];
const CHAR_WIDTH_TO_HEIGHT_RATIO: f32 = 3.0 / 4.0;
pub const PADDING_FACTOR: [f32; 2] = [0.30, 0.10];
pub const CELL_WIDTH_TO_HEIGHT_RATIO: f32 = Self::CHAR_WIDTH_TO_HEIGHT_RATIO
* (1.0 - Self::PADDING_FACTOR[0])
/ (1.0 - Self::PADDING_FACTOR[1]);
pub(crate) fn courier_sprite_sheet(graphics: &mut Graphics2D) -> Result<Rc<SpriteSheet>> {
SpriteSheet::from_bytes(graphics, crate::res::COURIER_CHARMAP)
}
pub(crate) fn new(sheet: Rc<SpriteSheet>, char_width: f32, dim: [u32; 2]) -> Self {
let char_height = char_width / Self::CELL_WIDTH_TO_HEIGHT_RATIO;
let mut smap = SpriteMap::new(
sheet,
Self::CHAR_MAP_LAYOUT_DIM,
[char_width, char_height],
Self::PADDING_FACTOR,
);
let [nrows, ncols] = dim;
let empty_cell_index = Self::char_to_cell_index(' ');
for r in 0..nrows {
let y = char_height * (r as f32 + 0.5);
for c in 0..ncols {
let x = char_width * (c as f32 + 0.5);
smap.add([x, y], empty_cell_index);
}
}
Self {
smap,
dim,
raw_char_dim: [char_width, char_height].into(),
}
}
pub fn char_width(&self) -> f32 {
self.char_dim().width
}
pub fn char_height(&self) -> f32 {
self.char_dim().height
}
pub fn char_dim(&self) -> Dimensions {
self.raw_char_dim * self.scale()
}
pub fn rect_for_coord(&self, row_col: [u32; 2]) -> Rect {
let [row, col] = row_col;
let [char_width, char_height] = self.char_dim().to_array();
let [offset_x, offset_y] = self.smap.translation();
let x = char_width * col as f32 + offset_x;
let y = char_height * row as f32 + offset_y;
[x, y, x + char_width, y + char_height].into()
}
pub fn write_str(&mut self, coord: [u32; 2], s: &str) {
self.write_color_str(coord, s, [1.0, 1.0, 1.0, 1.0])
}
pub fn write_color_str<C: Into<Color>>(&mut self, coord: [u32; 2], s: &str, color: C) {
let color = color.into();
let [mut row, mut col] = coord;
let [nrows, ncols] = self.dimensions();
let mut chars = s.chars();
while let Some(ch) = chars.next() {
if row >= nrows {
break;
}
if col < ncols {
self.write_color_ch([row, col], ch, color);
}
match ch {
'\n' => {
row += 1;
col = 0;
}
_ => {
col += 1;
}
}
}
}
pub fn write_ch(&mut self, coord: [u32; 2], ch: char) {
self.write_color_ch(coord, ch, [1.0, 1.0, 1.0, 1.0])
}
pub fn write_color_ch<C: Into<Color>>(&mut self, coord: [u32; 2], ch: char, color: C) {
let [row, col] = coord;
let instance_index = self.coordinates_to_instance_index([row, col]);
let cell_index = Self::char_to_cell_index(ch);
self.smap.set_cell(instance_index, cell_index);
self.smap.get_mut(instance_index).set_color_factor(color);
}
pub fn set_color<C: Into<Color>>(&mut self, coord: [u32; 2], color: C) {
let [row, col] = coord;
let instance_index = self.coordinates_to_instance_index([row, col]);
self.smap.get_mut(instance_index).set_color_factor(color);
}
fn char_to_cell_index(c: char) -> u32 {
let c = c as u32;
if c < '!' as u32 || c >= 127 {
let [max_r, max_c] = Self::CHAR_MAP_LAYOUT_DIM;
max_r * max_c - 1
} else {
c - '!' as u32
}
}
pub fn nrows(&self) -> u32 {
self.dim[0]
}
pub fn ncols(&self) -> u32 {
self.dim[1]
}
fn coordinates_to_instance_index(&self, coord: [u32; 2]) -> usize {
let [row, col] = coord;
(row * self.ncols() + col) as usize
}
pub fn dimensions(&self) -> [u32; 2] {
self.dim
}
pub fn batch(&self) -> &SpriteBatch {
self.smap.batch()
}
pub fn set_translation(&mut self, translation: Translation) {
self.smap.set_translation(translation);
}
pub fn translation(&self) -> Translation {
self.smap.translation()
}
pub fn set_scale(&mut self, scale: Scaling) {
self.smap.set_scale(scale);
}
pub fn scale(&self) -> Scaling {
self.smap.scale()
}
}