Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use shader::{get_frame, View};
mod linear_alg;
mod shader;

fn main() {
fn main() -> std::io::Result<()> {
let height = 1.2;
let camera = Vector {
x: 0.0,
Expand Down Expand Up @@ -50,13 +50,18 @@ fn main() {
let mut theta_x = 0.0;

let mut next_frame = Instant::now();
let mut frame = shader::Frame::with_capacity(120, 120);

let stdout = std::io::stdout();
let mut stdout = stdout.lock();

loop {
thread::sleep_until(next_frame);
next_frame += Duration::from_secs_f32(1.0 / 30.0);
let [camera, top_left, top_right, bottom_left, bottom_right, light] =
coords.map(|c| rotate_y(rotate_z(c, theta_z), theta_x));

let frame = get_frame(
get_frame(
0.8,
1.5,
View {
Expand All @@ -65,27 +70,19 @@ fn main() {
top_right,
bottom_left,
bottom_right,
width: 190,
height: 90,
view: &mut frame,
},
light,
);

// let grey_scale =
// r##".'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"##.as_bytes();
let grey_scale = ".......::::::-----====+++**#%@".as_bytes();
for line in frame {
for char in line {
let char = char as usize;
if char > 0 {
let i = char * grey_scale.len() / (u8::MAX as usize + 1);
print!("{}", grey_scale[i] as char);
} else {
print!(" ");
}
use std::io::Write;
for line in frame.into_iter() {
for char in line.chars() {
write!(stdout, "{char}")?;
}
println!();
writeln!(stdout)?;
}
stdout.flush()?;

theta_x += theta_x_frame;
theta_z += theta_z_frame;
Expand Down
174 changes: 154 additions & 20 deletions src/shader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,146 @@ use roots::find_roots_quartic;

use crate::linear_alg::Vector;

pub struct View {
pub struct View<'a> {
pub camera: Vector,
pub top_left: Vector,
pub top_right: Vector,
pub bottom_left: Vector,
pub bottom_right: Vector,
pub width: usize,
pub height: usize,
pub view: &'a mut Frame,
}

pub struct Frame {
height: usize,
width: usize,
inner: Vec<u8>,
}

pub struct FrameIter<'a> {
iter: &'a Frame,
row: usize,
}

impl<'a> Iterator for FrameIter<'a> {
type Item = FrameRow<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.iter.height {
return None;
}

let out = FrameRow {
bytes: self.iter,
row: self.row,
idx: 0,
};
self.row += 1;

Some(out)
}
}

impl<'a> IntoIterator for &'a Frame {
type IntoIter = FrameIter<'a>;
type Item = FrameRow<'a>;
fn into_iter(self) -> Self::IntoIter {
FrameIter { iter: self, row: 0 }
}
}

pub struct FrameRow<'a> {
bytes: &'a Frame,
row: usize,
idx: usize,
}

impl<'a> FrameRow<'a> {
pub fn as_bytes(&self) -> &[u8] {
let start = self.row * self.bytes.height;
let end = start + self.bytes.width;
&self.bytes.inner[start..end]
}

pub fn row(&self) -> usize {
self.row
}

pub fn chars(self) -> FrameChars<'a> {
let Self { bytes, row, idx } = self;
FrameChars { bytes, row, idx }
}
}

pub struct FrameChars<'a> {
bytes: &'a Frame,
row: usize,
idx: usize,
}

// let grey_scale =
// r##".'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$"##.as_bytes();
const GREY_SCALE: &[u8] = b".......::::::-----====+++**#%@";

impl<'a> Iterator for FrameChars<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.bytes.width {
return None;
}
let start = self.row * self.bytes.width;
let idx = start + self.idx;
self.idx += 1;

let char = self.bytes.inner[idx] as usize;

let c = if char > 0 {
let i = char * GREY_SCALE.len() / (u8::MAX as usize + 1);
GREY_SCALE[i] as char
} else {
' '
};
Some(c)
}
}

impl<'a> Iterator for FrameRow<'a> {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.bytes.width {
return None;
}
let start = self.row * self.bytes.width;
let idx = start + self.idx;
self.idx += 1;
Some(self.bytes.inner[idx])
}
}

impl Frame {
pub fn with_capacity(width: usize, height: usize) -> Self {
let inner = Vec::with_capacity(width * height);
Self {
inner,
width,
height,
}
}

pub fn fill_char(&mut self, i: usize, j: usize, char: u8) {
let pos = j * self.width + i;
assert!(pos <= self.inner.len());

if pos == self.inner.len() {
self.push(char)
} else {
let a: &mut [u8] = self.inner.as_mut();
a[pos] = char;
}
}

pub fn push(&mut self, val: u8) {
self.inner.push(val)
}
}

pub fn get_frame(
Expand All @@ -21,33 +153,35 @@ pub fn get_frame(
top_right,
bottom_left,
bottom_right,
width,
height,
view,
}: View,
light: Vector,
) -> Vec<Vec<u8>> {
let mut frame = Vec::with_capacity(height);
) {
let height = view.height;
let width = view.width;

for i in 0..height {
let mut row = Vec::with_capacity(width);
for j in 0..width {
let bottom = i as f64 / (height as f64 - 1.0);
let top = 1.0 - bottom;
let right = j as f64 / (width as f64 - 1.0);
let left = 1.0 - right;
row.push(get_pixel(
inner_radius,
outer_radius,
camera,
top_left * top * left
+ top_right * top * right
+ bottom_left * bottom * left
+ bottom_right * bottom * right,
light,
));
view.fill_char(
j,
i,
get_pixel(
inner_radius,
outer_radius,
camera,
top_left * top * left
+ top_right * top * right
+ bottom_left * bottom * left
+ bottom_right * bottom * right,
light,
),
);
}
frame.push(row);
}
frame
}

/// The donut lay flat on the x-y plane centered around (0, 0, 0)
Expand Down