use super::crossterm::style::Color;
use super::crossterm::{execute, style};
use super::pixel;
use super::pixel::Pixel;
use std::io::Write;
#[derive(Clone)]
pub struct Screen {
width: u32,
height: u32,
screen: Vec<Pixel>,
empty: bool,
}
impl Screen {
pub fn new(width: u32, height: u32) -> Screen {
Self::new_fill(width, height, pixel::pxl(' '))
}
pub fn new_empty(width: u32, height: u32) -> Screen {
let mut scr = Self::new_fill(width, height, pixel::pxl('\u{0}'));
scr.empty = true;
scr
}
pub fn new_fill(width: u32, height: u32, pixel: Pixel) -> Screen {
Screen {
width,
height,
screen: vec![pixel; (width * height) as usize],
empty: false,
}
}
pub fn from_vec(vec: Vec<Pixel>, width: u32, height: u32) -> Screen {
assert!(vec.len() == (width*height) as usize, format!("The Vec structure must have the length corresponding to width*height (={}) but the given Vec has a length of {}.", width*height, vec.len()));
Screen {
width,
height,
screen: vec,
empty: false,
}
}
pub fn from_string(string: String, fg: Color, bg: Color, width: u32, height: u32) -> Screen {
assert!(string.chars().count() == (width*height) as usize, format!("The String must have the length corresponding to width*height (={}) but the given String has a length of {}.", width*height, string.chars().count()));
let vec: Vec<Pixel> = string
.chars()
.map(|chr| pixel::pxl_fbg(chr, fg, bg))
.collect();
Screen::from_vec(vec, width, height)
}
pub fn get_width(&self) -> u32 {
self.width
}
pub fn get_height(&self) -> u32 {
self.height
}
pub fn clear(&mut self) {
self.fill(pixel::pxl(' '));
}
pub fn fill(&mut self, pixel: Pixel) {
self.empty = pixel.chr == '\u{0}';
self.screen = vec![pixel; (self.width * self.height) as usize];
}
pub fn check_empty(&mut self) -> bool {
for pxl in self.screen.iter() {
if pxl.chr != '\u{0}' {
self.empty = false;
return false;
}
}
self.empty = true;
true
}
pub fn is_empty(&self) -> bool {
self.empty
}
pub fn print(&mut self, x: i32, y: i32, string: &str) {
self.print_fbg(x, y, string, Color::Reset, Color::Reset)
}
pub fn print_fbg(&mut self, x: i32, y: i32, string: &str, fg: Color, bg: Color) {
if y >= 0 && x < self.width as i32 && y < self.height as i32 {
let pos = self.coord_to_index(std::cmp::max(0, x), y);
let delta_x = if x < 0 { x.abs() as usize } else { 0usize };
let mut count = delta_x;
let char_vec: Vec<char> = string.chars().collect();
let origin_row = pos / self.get_width() as usize;
for i in pos..std::cmp::min(pos + char_vec.len() - delta_x, self.screen.capacity()) {
if origin_row != i / self.get_width() as usize {
break;
}
self.screen[i] = pixel::pxl_fbg(char_vec[count], fg, bg);
count += 1;
}
}
}
pub fn print_screen(&mut self, x: i32, y: i32, source: &Screen) {
for j in 0..source.get_height() as i32 {
for i in 0..source.get_width() as i32 {
self.set_pxl_ref(x + i, y + j, &source.get_pxl(i, j).unwrap());
}
}
}
pub fn print_screen_alpha(&mut self, x: i32, y: i32, source: &Screen, alpha_character: char) {
for j in 0..source.get_height() as i32 {
for i in 0..source.get_width() as i32 {
if source.get_pxl(i, j).unwrap().chr != alpha_character {
self.set_pxl_ref(x + i, y + j, &source.get_pxl(i, j).unwrap());
}
}
}
}
pub fn h_line(&mut self, start_x: i32, start_y: i32, end_x: i32, character: Pixel) {
let start = if start_x > end_x { end_x } else { start_x };
let end = if start_x > end_x {
start_x + 1
} else {
end_x + 1
};
for i in start..end {
self.set_pxl_ref(i, start_y, &character);
}
}
pub fn v_line(&mut self, start_x: i32, start_y: i32, end_y: i32, character: Pixel) {
let start = if start_y > end_y { end_y } else { start_y };
let end = if start_y > end_y {
start_y + 1
} else {
end_y + 1
};
for j in start..end {
self.set_pxl_ref(start_x, j, &character);
}
}
pub fn line(&mut self, start_x: i32, start_y: i32, end_x: i32, end_y: i32, character: Pixel) {
let delta_x = end_x - start_x;
let delta_y = end_y - start_y;
if delta_y == 0 {
self.h_line(start_x, start_y, end_x, character);
return;
}
if delta_x == 0 {
self.v_line(start_x, start_y, end_y, character);
return;
}
let line_low = |screen: &mut Screen, x0: i32, y0: i32, x1: i32, y1: i32| {
let dx: i32 = x1 - x0;
let mut dy: i32 = y1 - y0;
let mut yi = 1;
if dy < 0 {
yi = -1;
dy = -dy;
}
let mut d = 2 * dy - dx;
let mut y = y0;
for x in x0..x1 + 1 {
screen.set_pxl_ref(x, y, &character);
if d > 0 {
y += yi;
d -= 2 * dx;
}
d += 2 * dy;
}
};
let line_high = |screen: &mut Screen, x0: i32, y0: i32, x1: i32, y1: i32| {
let mut dx = x1 - x0;
let dy = y1 - y0;
let mut xi = 1;
if dx < 0 {
xi = -1;
dx = -dx;
}
let mut d = 2 * dx - dy;
let mut x = x0;
for y in y0..y1 + 1 {
screen.set_pxl_ref(x, y, &character);
if d > 0 {
x += xi;
d -= 2 * dy;
}
d += 2 * dx;
}
};
if (end_y - start_y).abs() < (end_x - start_x).abs() {
if start_x > end_x {
line_low(self, end_x, end_y, start_x, start_y);
} else {
line_low(self, start_x, start_y, end_x, end_y);
}
} else if start_y > end_y {
line_high(self, end_x, end_y, start_x, start_y);
} else {
line_high(self, start_x, start_y, end_x, end_y);
}
}
pub fn rect(&mut self, start_x: i32, start_y: i32, end_x: i32, end_y: i32, character: Pixel) {
self.h_line(start_x, start_y, end_x, character.clone());
self.v_line(end_x, start_y, end_y, character.clone());
self.h_line(end_x, end_y, start_x, character.clone());
self.v_line(start_x, end_y, start_y, character);
}
pub fn fill_rect(
&mut self,
start_x: i32,
start_y: i32,
end_x: i32,
end_y: i32,
character: Pixel,
) {
let y0 = if start_y < end_y { start_y } else { end_y };
let y1 = if start_y < end_y {
end_y + 1
} else {
start_y + 1
};
for y in y0..y1 {
self.h_line(start_x, y, end_x, character.clone());
}
}
pub fn circle(&mut self, x: i32, y: i32, radius: u32, character: Pixel) {
let mut relative_pos_x = 0 as i32;
let mut relative_pos_y = radius as i32;
let mut distance: i32 = 3 - 2 * radius as i32;
if radius == 0 {
return;
}
while relative_pos_y >= relative_pos_x {
self.set_pxl_ref(x + relative_pos_x, y - relative_pos_y, &character);
self.set_pxl_ref(x + relative_pos_y, y - relative_pos_x, &character);
self.set_pxl_ref(x + relative_pos_y, y + relative_pos_x, &character);
self.set_pxl_ref(x + relative_pos_x, y + relative_pos_y, &character);
self.set_pxl_ref(x - relative_pos_x, y + relative_pos_y, &character);
self.set_pxl_ref(x - relative_pos_y, y + relative_pos_x, &character);
self.set_pxl_ref(x - relative_pos_y, y - relative_pos_x, &character);
self.set_pxl_ref(x - relative_pos_x, y - relative_pos_y, &character);
if distance < 0 {
distance += 4 * relative_pos_x as i32 + 6;
relative_pos_x += 1;
} else {
distance += 4 * (relative_pos_x as i32 - relative_pos_y as i32) + 10;
relative_pos_x += 1;
relative_pos_y -= 1;
}
}
}
pub fn fill_circle(&mut self, x: i32, y: i32, radius: u32, character: Pixel) {
let mut relative_pos_x = 0 as i32;
let mut relative_pos_y = radius as i32;
let mut distance: i32 = 3 - 2 * radius as i32;
if radius == 0 {
return;
}
let mut drawline = |start_x: i32, end_x: i32, y: i32| {
for i in start_x..end_x + 1 {
self.set_pxl_ref(i, y, &character);
}
};
while relative_pos_y >= relative_pos_x {
drawline(x - relative_pos_x, x + relative_pos_x, y - relative_pos_y);
drawline(x - relative_pos_y, x + relative_pos_y, y - relative_pos_x);
drawline(x - relative_pos_x, x + relative_pos_x, y + relative_pos_y);
drawline(x - relative_pos_y, x + relative_pos_y, y + relative_pos_x);
if distance < 0 {
distance += 4 * relative_pos_x + 6;
relative_pos_x += 1;
} else {
distance += 4 * (relative_pos_x - relative_pos_y) + 10;
relative_pos_x += 1;
relative_pos_y -= 1;
}
}
}
pub fn triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
character: Pixel,
) {
self.line(x1, y1, x2, y2, character.clone());
self.line(x2, y2, x3, y3, character.clone());
self.line(x3, y3, x1, y1, character);
}
pub fn fill_triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
character: Pixel,
) {
self.triangle(
x1 as i32,
y1 as i32,
x2 as i32,
y2 as i32,
x3 as i32,
y3 as i32,
character.clone(),
);
let v0 = (x1 as i32, y1 as i32);
let mut v1 = (x2 as i32, y2 as i32);
let mut v2 = (x3 as i32, y3 as i32);
let cross = (v1.1 - v0.1) * (v2.0 - v1.0) - (v1.0 - v0.0) * (v2.1 - v1.1);
if cross > 0 {
std::mem::swap(&mut v1, &mut v2)
}
let min_x = std::cmp::max(std::cmp::min(std::cmp::min(v0.0, v1.0), v2.0), 0);
let max_x = std::cmp::min(
std::cmp::max(std::cmp::max(v0.0, v1.0), v2.0),
self.get_width() as i32 - 1,
);
let min_y = std::cmp::max(std::cmp::min(std::cmp::min(v0.1, v1.1), v2.1), 0);
let max_y = std::cmp::min(
std::cmp::max(std::cmp::max(v0.1, v1.1), v2.1),
self.get_height() as i32 - 1,
);
let a01 = v0.1 - v1.1;
let b01 = v1.0 - v0.0;
let a12 = v1.1 - v2.1;
let b12 = v2.0 - v1.0;
let a20 = v2.1 - v0.1;
let b20 = v0.0 - v2.0;
let is_top_left = |v0: (i32, i32), v1: (i32, i32)| -> bool { v0.1 > v1.1 };
let bias0 = if is_top_left(v1, v2) { 0 } else { -1 };
let bias1 = if is_top_left(v2, v0) { 0 } else { -1 };
let bias2 = if is_top_left(v0, v1) { 0 } else { -1 };
let orient2d = |a: (i32, i32), b: (i32, i32), c: (i32, i32)| -> i32 {
(b.0 - a.0) * (c.1 - a.1) - (b.1 - a.1) * (c.0 - a.0)
};
let mut p = (min_x, min_y);
let mut w0_row = orient2d(v1, v2, p) + bias0;
let mut w1_row = orient2d(v2, v0, p) + bias1;
let mut w2_row = orient2d(v0, v1, p) + bias2;
for y in min_y..max_y {
p.1 = y;
let mut w0 = w0_row;
let mut w1 = w1_row;
let mut w2 = w2_row;
for x in min_x..max_x {
p.0 = x;
if (w0 | w1 | w2) >= 0 {
self.set_pxl_ref(p.0, p.1, &character);
}
w0 += a12;
w1 += a20;
w2 += a01;
}
w0_row += b12;
w1_row += b20;
w2_row += b01;
}
}
fn set_pxl_ref(&mut self, x: i32, y: i32, character: &Pixel) {
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
let index = self.coord_to_index(x, y);
self.screen[index] = character.clone();
}
}
pub fn set_pxl(&mut self, x: i32, y: i32, character: Pixel) {
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
let index = self.coord_to_index(x, y);
self.screen[index] = character;
}
}
pub fn get_pxl(&self, x: i32, y: i32) -> Result<Pixel, String> {
if x >= 0 && y >= 0 && x < self.width as i32 && y < self.height as i32 {
return Ok(self.screen[self.coord_to_index(x, y)].clone());
}
Err(format!(
"Attempted to get_pxl out of bounds (coords: [{}, {}], bounds: [{}, {}]",
x,
y,
self.width - 1,
self.height - 1
))
}
pub fn resize(&mut self, new_width: u32, new_height: u32) {
let mut new_screen = vec![pixel::pxl(' '); (new_width * new_height) as usize];
for j in 0..std::cmp::min(self.height, new_height) {
for i in 0..std::cmp::min(self.width, new_width) {
if (i as u32) < self.width && (j as u32) < self.height {
new_screen[((j * new_width) + i) as usize] =
self.screen[((j * self.width) + i) as usize].clone();
}
}
}
self.screen = new_screen;
self.width = new_width;
self.height = new_height;
}
pub fn draw(&self) {
let mut output = std::io::stdout();
crossterm::terminal::enable_raw_mode().unwrap();
for i in 0..self.width * self.height {
let pixel = &self.screen[i as usize];
execute!(
output,
style::SetForegroundColor(pixel.fg),
style::SetBackgroundColor(pixel.bg),
style::Print(pixel.chr)
)
.unwrap();
if i != self.width * self.height - 1 && i % self.width == self.width - 1 {
execute!(output, style::Print("\n\r")).unwrap();
}
}
crossterm::terminal::disable_raw_mode().unwrap();
}
fn coord_to_index(&self, x: i32, y: i32) -> usize {
((y * self.width as i32) + x) as usize
}
}