Create the converter to PPM
This commit is contained in:
parent
3c8536deb6
commit
15d87fbde6
|
@ -1 +1,2 @@
|
|||
pub mod ppm;
|
||||
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
|
||||
}
|
||||
|
||||
pub fn pixel(&self, row: usize, column: usize) -> &Color {
|
||||
&self.pixels[self.addr(row, column)]
|
||||
pub fn pixel(&self, x: usize, y: usize) -> &Color {
|
||||
&self.pixels[self.addr(y, x)]
|
||||
}
|
||||
|
||||
pub fn pixel_mut<'a>(&'a mut self, row: usize, column: usize) -> &'a mut Color {
|
||||
let addr = self.addr(row, column);
|
||||
pub fn pixel_mut<'a>(&'a mut self, x: usize, y: usize) -> &'a mut Color {
|
||||
let addr = self.addr(y, x);
|
||||
&mut self.pixels[addr]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn addr(&self, row: usize, column: usize) -> usize {
|
||||
row * self.width() + column
|
||||
fn addr(&self, y: usize, x: usize) -> usize {
|
||||
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