diff --git a/src/main.rs b/src/main.rs index 3ca3806..8b55e6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,6 @@ #[macro_use] extern crate justerror; -use std::sync::{Arc, Mutex}; - -use rand::Rng; -use rayon::prelude::*; - mod tga; use tga::*; @@ -13,7 +8,9 @@ mod point; use point::*; mod model; -use model::*; + +mod triangle; +use triangle::*; const BLACK: TGAColor = TGAColor { bgra: [0u8; 4] }; @@ -75,137 +72,6 @@ fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage) } } -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct Triangle2i { - a: Point2i, - b: Point2i, - c: Point2i, -} - -impl Triangle2i { - pub fn new(a: Point2i, b: Point2i, c: Point2i) -> Self { - Self { a, b, c } - } - - pub fn signed_area(&self) -> f32 { - 0.5 * ((self.b.y - self.a.y) * (self.a.x + self.b.x) - + (self.c.y - self.b.y) * (self.c.x + self.b.x) - + (self.a.y - self.c.y) * (self.a.x + self.c.x)) as f32 - } - - pub fn bb(&self) -> AABB { - let xmax = self.a.x.max(self.b.x.max(self.c.x)); - let xmin = self.a.x.min(self.b.x.min(self.c.x)); - - let ymax = self.a.y.max(self.b.y.max(self.c.y)); - let ymin = self.a.y.min(self.b.y.min(self.c.y)); - AABB { - lower_left: Point2i::new(xmin, ymin), - upper_right: Point2i::new(xmax, ymax), - } - } - - pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage) { - let fb = Arc::new(Mutex::new(fb)); - let bb = self.bb(); - let total_area = self.signed_area(); - if total_area < 1.0 { - return; - } - let it = 1.0 / total_area; - (bb.xmin()..=bb.xmax()).into_par_iter().for_each(|x| { - (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { - let p = Point2i::new(x, y); - let alpha = Triangle2i::new(p, self.b, self.c).signed_area() * it; - let beta = Triangle2i::new(p, self.c, self.a).signed_area() * it; - let gamma = Triangle2i::new(p, self.a, self.b).signed_area() * it; - - if alpha.is_sign_positive() && beta.is_sign_positive() && gamma.is_sign_positive() { - let color = if let Some(ca) = self.a.color - && let Some(cb) = self.b.color - && let Some(cc) = self.c.color - { - color + (ca * alpha) + (cb * beta) + (cc * gamma) - } else { - color - }; - - if let Ok(mut fb) = fb.lock() { - fb.set(x as u32, y as u32, color); - } - }; - }); - }); - } - - pub fn render_lines(&self, thickness: i32, color: TGAColor, fb: &mut TGAImage) { - let fb = Arc::new(Mutex::new(fb)); - let bb = self.bb(); - let total_area = self.signed_area(); - if total_area < 1.0 { - return; - } - let it = 1.0 / total_area; - (bb.xmin()..=bb.xmax()).into_par_iter().for_each(|x| { - (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { - let p = Point2i::new(x, y); - let alpha = Triangle2i::new(p, self.b, self.c).signed_area() * it; - let beta = Triangle2i::new(p, self.c, self.a).signed_area() * it; - let gamma = Triangle2i::new(p, self.a, self.b).signed_area() * it; - - if alpha.is_sign_positive() && beta.is_sign_positive() && gamma.is_sign_positive() { - let ad = ((self.b.y - self.a.y) * x - (self.b.x - self.a.x) * y - + self.b.x * self.a.y - - self.b.y * self.a.x) - .abs() - / ((self.b.y - self.a.y).pow(2) + (self.b.x - self.a.x).pow(2)).isqrt(); - let bd = ((self.c.y - self.b.y) * x - (self.c.x - self.b.x) * y - + self.c.x * self.b.y - - self.c.y * self.b.x) - .abs() - / ((self.c.y - self.b.y).pow(2) + (self.c.x - self.b.x).pow(2)).isqrt(); - let cd = ((self.a.y - self.c.y) * x - (self.a.x - self.c.x) * y - + self.a.x * self.c.y - - self.a.y * self.c.x) - .abs() - / ((self.a.y - self.c.y).pow(2) + (self.a.x - self.c.x).pow(2)).isqrt(); - - if ad <= thickness || bd <= thickness || cd <= thickness { - let color = if let Some(ca) = self.a.color - && let Some(cb) = self.b.color - && let Some(cc) = self.c.color - { - color + (ca * alpha) + (cb * beta) + (cc * gamma) - } else { - color - }; - - if let Ok(mut fb) = fb.lock() { - fb.set(x as u32, y as u32, color); - } - } - } - }); - }); - } - - pub fn centroid(&self) -> Point2i { - let x = self.a.x / 3 + self.b.x / 3 + self.c.x / 3; - let y = self.a.y / 3 + self.b.y / 3 + self.c.y / 3; - let has_color = self.a.color.is_some() || self.b.color.is_some() || self.c.color.is_some(); - let denom = 1.0 / 3.0; - let color = if has_color { - let c = self.a.color.unwrap_or(BLACK) * denom - + self.b.color.unwrap_or(BLACK) * denom - + self.c.color.unwrap_or(BLACK) * denom; - Some(c) - } else { - None - }; - Point2i { x, y, color } - } -} - #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct AABB { lower_left: Point2i, diff --git a/src/triangle.rs b/src/triangle.rs new file mode 100644 index 0000000..a477b7e --- /dev/null +++ b/src/triangle.rs @@ -0,0 +1,141 @@ +use std::sync::{Arc, Mutex}; + +use rayon::iter::{IntoParallelIterator, ParallelIterator}; + +use crate::{ + AABB, BLACK, + point::Point2i, + tga::{TGAColor, TGAImage}, +}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Triangle2i { + a: Point2i, + b: Point2i, + c: Point2i, +} + +impl Triangle2i { + pub fn new(a: Point2i, b: Point2i, c: Point2i) -> Self { + Self { a, b, c } + } + + pub fn signed_area(&self) -> f32 { + 0.5 * ((self.b.y - self.a.y) * (self.a.x + self.b.x) + + (self.c.y - self.b.y) * (self.c.x + self.b.x) + + (self.a.y - self.c.y) * (self.a.x + self.c.x)) as f32 + } + + pub fn bb(&self) -> AABB { + let xmax = self.a.x.max(self.b.x.max(self.c.x)); + let xmin = self.a.x.min(self.b.x.min(self.c.x)); + + let ymax = self.a.y.max(self.b.y.max(self.c.y)); + let ymin = self.a.y.min(self.b.y.min(self.c.y)); + AABB { + lower_left: Point2i::new(xmin, ymin), + upper_right: Point2i::new(xmax, ymax), + } + } + + pub fn render_filled(&self, color: TGAColor, fb: &mut TGAImage) { + let fb = Arc::new(Mutex::new(fb)); + let bb = self.bb(); + let total_area = self.signed_area(); + if total_area < 1.0 { + return; + } + let it = 1.0 / total_area; + (bb.xmin()..=bb.xmax()).into_par_iter().for_each(|x| { + (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { + let p = Point2i::new(x, y); + let alpha = Triangle2i::new(p, self.b, self.c).signed_area() * it; + let beta = Triangle2i::new(p, self.c, self.a).signed_area() * it; + let gamma = Triangle2i::new(p, self.a, self.b).signed_area() * it; + + if alpha.is_sign_positive() && beta.is_sign_positive() && gamma.is_sign_positive() { + let color = if let Some(ca) = self.a.color + && let Some(cb) = self.b.color + && let Some(cc) = self.c.color + { + color + (ca * alpha) + (cb * beta) + (cc * gamma) + } else { + color + }; + + if let Ok(mut fb) = fb.lock() { + fb.set(x as u32, y as u32, color); + } + }; + }); + }); + } + + pub fn render_lines(&self, thickness: i32, color: TGAColor, fb: &mut TGAImage) { + let fb = Arc::new(Mutex::new(fb)); + let bb = self.bb(); + let total_area = self.signed_area(); + if total_area < 1.0 { + return; + } + let it = 1.0 / total_area; + (bb.xmin()..=bb.xmax()).into_par_iter().for_each(|x| { + (bb.ymin()..=bb.ymax()).into_par_iter().for_each(|y| { + let p = Point2i::new(x, y); + let alpha = Triangle2i::new(p, self.b, self.c).signed_area() * it; + let beta = Triangle2i::new(p, self.c, self.a).signed_area() * it; + let gamma = Triangle2i::new(p, self.a, self.b).signed_area() * it; + + if alpha.is_sign_positive() && beta.is_sign_positive() && gamma.is_sign_positive() { + // get the distance to the sides + let ad = ((self.b.y - self.a.y) * x - (self.b.x - self.a.x) * y + + self.b.x * self.a.y + - self.b.y * self.a.x) + .abs() + / ((self.b.y - self.a.y).pow(2) + (self.b.x - self.a.x).pow(2)).isqrt(); + let bd = ((self.c.y - self.b.y) * x - (self.c.x - self.b.x) * y + + self.c.x * self.b.y + - self.c.y * self.b.x) + .abs() + / ((self.c.y - self.b.y).pow(2) + (self.c.x - self.b.x).pow(2)).isqrt(); + let cd = ((self.a.y - self.c.y) * x - (self.a.x - self.c.x) * y + + self.a.x * self.c.y + - self.a.y * self.c.x) + .abs() + / ((self.a.y - self.c.y).pow(2) + (self.a.x - self.c.x).pow(2)).isqrt(); + + if ad <= thickness || bd <= thickness || cd <= thickness { + let color = if let Some(ca) = self.a.color + && let Some(cb) = self.b.color + && let Some(cc) = self.c.color + { + color + (ca * alpha) + (cb * beta) + (cc * gamma) + } else { + color + }; + + if let Ok(mut fb) = fb.lock() { + fb.set(x as u32, y as u32, color); + } + } + } + }); + }); + } + + pub fn centroid(&self) -> Point2i { + let x = self.a.x / 3 + self.b.x / 3 + self.c.x / 3; + let y = self.a.y / 3 + self.b.y / 3 + self.c.y / 3; + let has_color = self.a.color.is_some() || self.b.color.is_some() || self.c.color.is_some(); + let denom = 1.0 / 3.0; + let color = if has_color { + let c = self.a.color.unwrap_or(BLACK) * denom + + self.b.color.unwrap_or(BLACK) * denom + + self.c.color.unwrap_or(BLACK) * denom; + Some(c) + } else { + None + }; + Point2i { x, y, color } + } +}