use std::io::{self, Write};
use crate::{
utils::{
cube_utils::Color,
geometry::{Point2D, Point3D, Triangle}
},
cube::{
slice::FaceSlice,
cube::Face
}
};
const PRINT_CHAR: &str = "██";
const ANSI_RESET: &str = "\x1b[0m";
pub enum AnyFace {
Face(Face),
FaceSlice(FaceSlice),
}
impl AnyFace {
pub fn avg_z(&self) -> f32 {
match self {
AnyFace::Face(f) => f.avg_z(),
AnyFace::FaceSlice(fs) => fs.avg_z(),
}
}
}
pub trait Renderable {
fn get_visible_faces(&self) -> Vec<AnyFace>;
fn dist(&self) -> f32;
}
pub struct Screen {
size_x: usize,
size_y: usize,
screen: Vec<Vec<Option<Color>>>,
zp: f32,
projection_scale: f32,
}
impl Screen {
pub fn new(size_x: usize, size_y: usize, zp: f32, projection_scale: f32) -> Screen {
Screen {
size_x,
size_y,
screen: vec![vec![None; size_x]; size_y],
zp,
projection_scale,
}
}
pub fn color_at(&self, x: i16, y: i16) -> Option<Color> {
self.screen[self.size_y - y as usize - 1][x as usize]
}
fn project_point(&self, p: Point3D) -> Point2D {
let screen_x_offset = (self.size_x / 2) as isize;
let screen_y_offset = (self.size_y / 2) as isize;
let multiplier = self.zp / p.z;
let xp = p.x * multiplier;
let yp = p.y * multiplier;
let x_proj = (xp * self.projection_scale) as isize + screen_x_offset;
let y_proj = (yp * self.projection_scale) as isize + screen_y_offset;
Point2D {x: x_proj, y: y_proj}
}
fn rasterize_triangle(&mut self, tri: Triangle, color: Color) {
let mut pts = [tri.0, tri.1, tri.2];
pts.sort_by_key(|p| p.y);
let (v1, v2, v3) = (pts[0], pts[1], pts[2]);
let inv_slope_1 = if v2.y != v1.y {
(v2.x - v1.x) as f32 / (v2.y - v1.y) as f32
} else { 0.0 };
let inv_slope_2 = if v3.y != v1.y {
(v3.x - v1.x) as f32 / (v3.y - v1.y) as f32
} else { 0.0 };
let inv_slope_3 = if v3.y != v2.y {
(v3.x - v2.x) as f32 / (v3.y - v2.y) as f32
} else { 0.0 };
let mut curx1 = v1.x as f32;
let mut curx2 = v1.x as f32;
for y in v1.y..=v2.y {
if y >= 0 && y < self.size_y as isize {
let x_start = curx1.min(curx2).max(0.0) as isize;
let x_end = curx1.max(curx2).min(self.size_x as f32 - 1.0) as isize;
for x in x_start..=x_end {
self.screen[y as usize][x as usize] = Some(color);
}
}
curx1 += inv_slope_1;
curx2 += inv_slope_2;
}
let mut curx1 = v2.x as f32;
let mut curx2 = v1.x as f32 + inv_slope_2 * (v2.y - v1.y) as f32;
for y in v2.y..=v3.y {
if y >= 0 && y < self.size_y as isize {
let x_start = curx1.min(curx2).max(0.0) as isize;
let x_end = curx1.max(curx2).min(self.size_x as f32 - 1.0) as isize;
for x in x_start..=x_end {
self.screen[y as usize][x as usize] = Some(color);
}
}
curx1 += inv_slope_3;
curx2 += inv_slope_2;
}
}
fn render_face(&mut self, face: &Face) {
let projected_markers: Vec<Point2D> = face.markers
.iter()
.map(|&p| self.project_point(p))
.collect();
for row in 0..3 {
for col in 0..3 {
let tris = [
Triangle(projected_markers.get(row * 4 + col).unwrap().clone(),
projected_markers.get(row * 4 + col + 1).unwrap().clone(),
projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone()
),
Triangle(projected_markers.get(row * 4 + col).unwrap().clone(),
projected_markers.get((row + 1) * 4 + col + 1).unwrap().clone(),
projected_markers.get((row + 1) * 4 + col).unwrap().clone()
),
];
let color = face.grid_face.grid[row][col];
for tri in tris {
self.rasterize_triangle(tri, color);
}
}
}
}
fn render_face_slice(&mut self, face_slice: &FaceSlice) {
let projected_markers: Vec<Point2D> = face_slice.markers
.iter()
.map(|&p| self.project_point(p))
.collect();
for row in 0..3 {
let tris = [
Triangle(projected_markers.get(row * 2).unwrap().clone(),
projected_markers.get(row * 2 + 1).unwrap().clone(),
projected_markers.get((row + 1) * 2 + 1).unwrap().clone()
),
Triangle(projected_markers.get(row * 2).unwrap().clone(),
projected_markers.get((row + 1) * 2 + 1).unwrap().clone(),
projected_markers.get((row + 1) * 2).unwrap().clone()
),
];
let color = face_slice.colors[row];
for tri in tris {
self.rasterize_triangle(tri, color);
}
}
}
pub fn render(&mut self, mut renderables: Vec<&dyn Renderable>) {
renderables.sort_by(|a, b| a.dist().partial_cmp(&b.dist()).unwrap());
for renderable in renderables.into_iter().rev() {
let faces = renderable.get_visible_faces();
for face in faces.into_iter().take(3).rev() {
match face {
AnyFace::Face(f) => self.render_face(&f),
AnyFace::FaceSlice(fs) => self.render_face_slice(&fs),
};
}
}
}
pub fn print_screen(&self) {
for y in (0..(self.size_y)).rev() {
for x in 0..(self.size_x) {
match self.screen[y][x] {
Some(color) => print!("{}{}{}", color.to_ansi(), PRINT_CHAR, ANSI_RESET),
_ => print!(" ")
};
}
println!();
}
io::stdout().flush().unwrap();
}
pub fn reset_terminal() {
print!("{esc}c", esc = 27 as char);
io::stdout().flush().unwrap();
}
pub fn clear_screen(&mut self) {
for row in self.screen.iter_mut() {
for cell in row.iter_mut() {
*cell = None;
}
}
}
}