Create the converter to PPM
This commit is contained in:
parent
3c8536deb6
commit
15d87fbde6
|
@ -1 +1,2 @@
|
||||||
|
pub mod ppm;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
use crate::types::Canvas;
|
||||||
|
|
||||||
|
fn color_float_to_int(val: f64) -> u8 {
|
||||||
|
(val * 256.).clamp(0., 255.) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn join_to_line_limit(data: impl IntoIterator<Item = String>) -> Vec<String> {
|
||||||
|
let mut lines = vec![];
|
||||||
|
|
||||||
|
let mut line = String::new();
|
||||||
|
let mut iter = data.into_iter();
|
||||||
|
while let Some(element) = iter.next() {
|
||||||
|
if line.is_empty() {
|
||||||
|
line = line + &element;
|
||||||
|
} else if line.len() + 1 + element.len() < 70 {
|
||||||
|
line = line + " " + &element;
|
||||||
|
} else {
|
||||||
|
lines.push(line);
|
||||||
|
line = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !line.is_empty() {
|
||||||
|
lines.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", lines);
|
||||||
|
|
||||||
|
lines
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PPM(String);
|
||||||
|
|
||||||
|
impl From<Canvas> for PPM {
|
||||||
|
fn from(c: Canvas) -> Self {
|
||||||
|
// let v = vec![0.; c.width() * c.height() * 3];
|
||||||
|
let header = format!("P3\n{} {}\n255\n", c.width(), c.height());
|
||||||
|
|
||||||
|
let mut data = vec![];
|
||||||
|
for y in 0..c.height() {
|
||||||
|
let mut row = vec![];
|
||||||
|
for x in 0..c.width() {
|
||||||
|
let pixel = c.pixel(x, y);
|
||||||
|
row.push(color_float_to_int(pixel.red()).to_string());
|
||||||
|
row.push(color_float_to_int(pixel.green()).to_string());
|
||||||
|
row.push(color_float_to_int(pixel.blue()).to_string());
|
||||||
|
}
|
||||||
|
let mut lines = join_to_line_limit(row);
|
||||||
|
data.append(&mut lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = data.join("\n");
|
||||||
|
|
||||||
|
Self(format!("{header}{data}\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for PPM {
|
||||||
|
type Target = String;
|
||||||
|
fn deref(&self) -> &String {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::types::Color;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn construct_ppm_header() {
|
||||||
|
let c = Canvas::new(5, 3);
|
||||||
|
let ppm = PPM::from(c);
|
||||||
|
|
||||||
|
assert!(ppm.starts_with(
|
||||||
|
"P3\n\
|
||||||
|
5 3\n\
|
||||||
|
255\n"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn construct_full_ppm_data() {
|
||||||
|
let mut c = Canvas::new(5, 3);
|
||||||
|
*c.pixel_mut(0, 0) = Color::new(1.5, 0., 0.);
|
||||||
|
*c.pixel_mut(2, 1) = Color::new(0., 0.5, 0.);
|
||||||
|
*c.pixel_mut(4, 2) = Color::new(-0.5, 0., 1.);
|
||||||
|
|
||||||
|
let expected = "P3\n\
|
||||||
|
5 3\n\
|
||||||
|
255\n\
|
||||||
|
255 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n\
|
||||||
|
0 0 0 0 0 0 0 128 0 0 0 0 0 0 0\n\
|
||||||
|
0 0 0 0 0 0 0 0 0 0 0 0 0 0 255\n";
|
||||||
|
|
||||||
|
let ppm = PPM::from(c);
|
||||||
|
assert_eq!(*ppm, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ppm_line_length_limit() {
|
||||||
|
let mut c = Canvas::new(10, 2);
|
||||||
|
for y in 0..2 {
|
||||||
|
for x in 0..10 {
|
||||||
|
*c.pixel_mut(x, y) = Color::new(1., 0.8, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected = "P3\n\
|
||||||
|
10 2\n\
|
||||||
|
255\n\
|
||||||
|
255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n\
|
||||||
|
153 255 204 153 255 204 153 255 204 153 255 204 153\n\
|
||||||
|
255 204 153 255 204 153 255 204 153 255 204 153 255 204 153 255 204\n\
|
||||||
|
153 255 204 153 255 204 153 255 204 153 255 204 153\n";
|
||||||
|
|
||||||
|
let ppm = PPM::from(c);
|
||||||
|
assert_eq!(*ppm, expected);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,17 +25,31 @@ impl Canvas {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pixel(&self, row: usize, column: usize) -> &Color {
|
pub fn pixel(&self, x: usize, y: usize) -> &Color {
|
||||||
&self.pixels[self.addr(row, column)]
|
&self.pixels[self.addr(y, x)]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pixel_mut<'a>(&'a mut self, row: usize, column: usize) -> &'a mut Color {
|
pub fn pixel_mut<'a>(&'a mut self, x: usize, y: usize) -> &'a mut Color {
|
||||||
let addr = self.addr(row, column);
|
let addr = self.addr(y, x);
|
||||||
&mut self.pixels[addr]
|
&mut self.pixels[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn addr(&self, row: usize, column: usize) -> usize {
|
fn addr(&self, y: usize, x: usize) -> usize {
|
||||||
row * self.width() + column
|
y * self.width() + x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn addresses() {
|
||||||
|
let c = Canvas::new(5, 3);
|
||||||
|
|
||||||
|
assert_eq!(c.addr(0, 0), 0);
|
||||||
|
assert_eq!(c.addr(1, 0), 5);
|
||||||
|
assert_eq!(c.addr(2, 0), 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue