renders a 'wireframe' triangle
This commit is contained in:
parent
9f4a8f03df
commit
20325e6a75
2 changed files with 119 additions and 76 deletions
89
src/main.rs
89
src/main.rs
|
@ -46,13 +46,10 @@ fn main() {
|
|||
let b = Point2i::new(550, 390).with_color(GREEN);
|
||||
let c = Point2i::new(230, 590).with_color(RED);
|
||||
|
||||
a.render(20, &mut fb);
|
||||
b.render(20, &mut fb);
|
||||
c.render(20, &mut fb);
|
||||
|
||||
let t = Triangle2i::new(a, b, c);
|
||||
t.render_filled(BLACK, &mut fb);
|
||||
fb.write_file("triangle.tga", true, true).unwrap();
|
||||
t.render_lines(20, BLACK, &mut fb);
|
||||
|
||||
fb.write_file("triangle.tga", true).unwrap();
|
||||
}
|
||||
|
||||
fn line(mut a: Point2i, mut b: Point2i, color: TGAColor, mut fb: &mut TGAImage) {
|
||||
|
@ -123,11 +120,7 @@ impl Triangle2i {
|
|||
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 Ok(mut fb) = fb.lock()
|
||||
{
|
||||
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
|
||||
|
@ -136,16 +129,80 @@ impl Triangle2i {
|
|||
} else {
|
||||
color
|
||||
};
|
||||
fb.set(x as u32, y as u32, 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 render_lines(&self, color: TGAColor, fb: &mut TGAImage) {
|
||||
line(self.a, self.b, color, fb);
|
||||
line(self.b, self.c, color, fb);
|
||||
line(self.c, self.a, color, fb);
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
106
src/tga.rs
106
src/tga.rs
|
@ -222,7 +222,7 @@ impl TGAImage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_file(&mut self, file: &str, do_vflip: bool, do_rle: bool) -> IoResult {
|
||||
pub fn write_file(&mut self, file: &str, do_vflip: bool) -> IoResult {
|
||||
let mut file = std::fs::File::create(file)?;
|
||||
|
||||
let mut header = TGAHeader::default();
|
||||
|
@ -231,10 +231,8 @@ impl TGAImage {
|
|||
header.height = self.height as u16;
|
||||
|
||||
header.datatype_code = match self.format {
|
||||
TGAFormat::Grayscale if do_rle => 11,
|
||||
TGAFormat::Grayscale => 3,
|
||||
_ if do_rle => 10,
|
||||
_ => 2,
|
||||
TGAFormat::Grayscale => 11,
|
||||
_ => 10,
|
||||
};
|
||||
|
||||
header.image_descriptor = if do_vflip { 0x00 } else { 0x20 };
|
||||
|
@ -243,10 +241,49 @@ impl TGAImage {
|
|||
|
||||
file.write_all(&mut header)?;
|
||||
|
||||
if do_rle {
|
||||
self.write_rle_file(&mut file)?;
|
||||
} else {
|
||||
file.write_all(&mut self.data)?;
|
||||
let max_chunk_len = 128;
|
||||
let num_pixels = (self.width * self.height) as usize;
|
||||
let mut current_pixel = 0;
|
||||
|
||||
let bpp = self.bytes_per_pixel() as usize;
|
||||
|
||||
while current_pixel < num_pixels {
|
||||
let chunk_start = current_pixel * bpp;
|
||||
let mut current_byte = current_pixel * bpp;
|
||||
|
||||
let mut run_length = 1;
|
||||
let mut is_raw = true;
|
||||
|
||||
while (current_pixel + run_length) < num_pixels && run_length < max_chunk_len {
|
||||
let mut succ_eq = true;
|
||||
let mut t = 0;
|
||||
while succ_eq && t < bpp {
|
||||
succ_eq = self.data[current_byte + t] == self.data[current_byte + t + bpp];
|
||||
t += 1;
|
||||
}
|
||||
current_byte += bpp;
|
||||
if 1 == run_length {
|
||||
is_raw = !succ_eq;
|
||||
}
|
||||
if is_raw && succ_eq {
|
||||
run_length -= 1;
|
||||
break;
|
||||
}
|
||||
if !(is_raw || succ_eq) {
|
||||
break;
|
||||
}
|
||||
run_length += 1;
|
||||
}
|
||||
current_pixel += run_length;
|
||||
let mut out = if is_raw {
|
||||
[run_length as u8 - 1]
|
||||
} else {
|
||||
[run_length as u8 + 127]
|
||||
};
|
||||
|
||||
file.write_all(&mut out)?;
|
||||
let chunk_end = chunk_start + if is_raw { run_length * bpp } else { bpp };
|
||||
file.write_all(&mut self.data[chunk_start..chunk_end])?;
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
|
@ -352,55 +389,4 @@ impl TGAImage {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_rle_file(&mut self, file: &mut File) -> IoResult {
|
||||
let max_chunk_len = 128;
|
||||
let num_pixels = (self.width * self.height) as usize;
|
||||
let mut current_pixel = 0;
|
||||
|
||||
let bpp = self.bytes_per_pixel() as usize;
|
||||
|
||||
while current_pixel < num_pixels {
|
||||
let chunk_start = current_pixel * bpp;
|
||||
let mut current_byte = current_pixel * bpp;
|
||||
|
||||
let mut run_length = 1;
|
||||
let mut is_raw = true;
|
||||
|
||||
while (current_pixel + run_length) < num_pixels && run_length < max_chunk_len {
|
||||
let mut succ_eq = true;
|
||||
let mut t = 0;
|
||||
while succ_eq && t < bpp {
|
||||
succ_eq = self.data[current_byte + t] == self.data[current_byte + t + bpp];
|
||||
t += 1;
|
||||
}
|
||||
current_byte += bpp;
|
||||
if 1 == run_length {
|
||||
is_raw = !succ_eq;
|
||||
}
|
||||
if is_raw && succ_eq {
|
||||
run_length -= 1;
|
||||
break;
|
||||
}
|
||||
if !(is_raw || succ_eq) {
|
||||
break;
|
||||
}
|
||||
run_length += 1;
|
||||
}
|
||||
current_pixel += run_length;
|
||||
let mut out = if is_raw {
|
||||
[run_length as u8 - 1]
|
||||
} else {
|
||||
[run_length as u8 + 127]
|
||||
};
|
||||
|
||||
file.write_all(&mut out)?;
|
||||
let chunk_end = chunk_start + if is_raw { run_length * bpp } else { bpp };
|
||||
file.write_all(&mut self.data[chunk_start..chunk_end])?;
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue