Compare commits
No commits in common. "12c2382327b038305775b6a54ac85ac52edaaf2f" and "b8df830460e3145b74b0ed0797207e05972b14a6" have entirely different histories.
12c2382327
...
b8df830460
@ -1,51 +0,0 @@
|
|||||||
use std::f32::consts::PI;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use rtchallenge::{
|
|
||||||
canvas::Canvas,
|
|
||||||
matrices::Matrix4x4,
|
|
||||||
tuples::{Color, Tuple},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn draw_dot(c: &mut Canvas, x: usize, y: usize) {
|
|
||||||
let red = Color::new(1., 0., 0.);
|
|
||||||
c.set(x.saturating_sub(1), y.saturating_sub(1), red);
|
|
||||||
c.set(x, y.saturating_sub(1), red);
|
|
||||||
c.set(x + 1, y.saturating_sub(1), red);
|
|
||||||
c.set(x.saturating_sub(1), y, red);
|
|
||||||
c.set(x, y, red);
|
|
||||||
c.set(x + 1, y, red);
|
|
||||||
c.set(x.saturating_sub(1), y + 1, red);
|
|
||||||
c.set(x, y + 1, red);
|
|
||||||
c.set(x + 1, y + 1, red);
|
|
||||||
}
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let w = 200;
|
|
||||||
let h = w;
|
|
||||||
let mut c = Canvas::new(w, h);
|
|
||||||
let t = Matrix4x4::translate(0., 0.4, 0.);
|
|
||||||
let p = Tuple::point(0., 0., 0.);
|
|
||||||
let rot_hour = Matrix4x4::rotation_z(-PI / 6.);
|
|
||||||
|
|
||||||
let mut p = t * p;
|
|
||||||
let w = w as f32;
|
|
||||||
let h = h as f32;
|
|
||||||
let h_w = w / 2.0;
|
|
||||||
let h_h = h / 2.0;
|
|
||||||
// The 'world' exists between -0.5 - 0.5 in X-Y plane.
|
|
||||||
// To convert to screen space, we translate by 0.5, scale to canvas size,
|
|
||||||
// and invert the Y-axis.
|
|
||||||
let world_to_screen =
|
|
||||||
Matrix4x4::scaling(w as f32, -h as f32, 1.0) * Matrix4x4::translate(0.5, -0.5, 0.);
|
|
||||||
for _ in 0..12 {
|
|
||||||
let canvas_pixel = world_to_screen * p;
|
|
||||||
draw_dot(&mut c, canvas_pixel.x as usize, canvas_pixel.y as usize);
|
|
||||||
p = rot_hour * p;
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = "/tmp/eoc4.png";
|
|
||||||
println!("saving output to {}", path);
|
|
||||||
c.write_to_file(path)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@ -1,6 +1,3 @@
|
|||||||
pub mod canvas;
|
pub mod canvas;
|
||||||
pub mod matrices;
|
pub mod matrices;
|
||||||
pub mod tuples;
|
pub mod tuples;
|
||||||
|
|
||||||
/// Value considered close enough for PartialEq implementations.
|
|
||||||
pub const EPSILON: f32 = 0.00001;
|
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Index, IndexMut, Mul};
|
use std::ops::{Index, IndexMut, Mul};
|
||||||
|
|
||||||
use crate::{tuples::Tuple, EPSILON};
|
// Implement a PartialEq that does approx_eq internally.
|
||||||
|
//use float_cmp::{ApproxEq, F32Margin};
|
||||||
|
|
||||||
|
use crate::tuples::Tuple;
|
||||||
|
|
||||||
|
/// Value considered close enough for PartialEq implementations.
|
||||||
|
const EPSILON: f32 = 0.00001;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Matrix2x2 {
|
pub struct Matrix2x2 {
|
||||||
@ -164,36 +170,6 @@ impl PartialEq for Matrix3x3 {
|
|||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
/// Matrix4x4 represents a 4x4 matrix in row-major form. So, element `m[i][j]` corresponds to m<sub>i,j</sub>
|
/// Matrix4x4 represents a 4x4 matrix in row-major form. So, element `m[i][j]` corresponds to m<sub>i,j</sub>
|
||||||
/// where `i` is the row number and `j` is the column number.
|
/// where `i` is the row number and `j` is the column number.
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```
|
|
||||||
/// use std::f32::consts::PI;
|
|
||||||
///
|
|
||||||
/// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple};
|
|
||||||
///
|
|
||||||
/// // Individual transformations are applied in sequence.
|
|
||||||
/// let p = Tuple::point(1., 0., 1.);
|
|
||||||
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
|
||||||
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
|
||||||
/// let c = Matrix4x4::translate(10., 5., 7.);
|
|
||||||
/// // Apply rotation first.
|
|
||||||
/// let p2 = a * p;
|
|
||||||
/// assert_eq!(p2, Tuple::point(1., -1., 0.));
|
|
||||||
/// // Then apply scaling.
|
|
||||||
/// let p3 = b * p2;
|
|
||||||
/// assert_eq!(p3, Tuple::point(5., -5., 0.));
|
|
||||||
/// // Then apply translation.
|
|
||||||
/// let p4 = c * p3;
|
|
||||||
/// assert_eq!(p4, Tuple::point(15., 0., 7.));
|
|
||||||
///
|
|
||||||
/// // Chained transformations must be applied in reverse order.
|
|
||||||
/// let p = Tuple::point(1., 0., 1.);
|
|
||||||
/// let a = Matrix4x4::rotation_x(PI / 2.);
|
|
||||||
/// let b = Matrix4x4::scaling(5., 5., 5.);
|
|
||||||
/// let c = Matrix4x4::translate(10., 5., 7.);
|
|
||||||
/// let t = c * b * a;
|
|
||||||
/// assert_eq!(t * p, Tuple::point(15., 0., 7.));
|
|
||||||
/// ```
|
|
||||||
pub struct Matrix4x4 {
|
pub struct Matrix4x4 {
|
||||||
m: [[f32; 4]; 4],
|
m: [[f32; 4]; 4],
|
||||||
}
|
}
|
||||||
@ -426,50 +402,6 @@ impl Matrix4x4 {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a transform matrix that will shear (skew) points.
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rtchallenge::{matrices::Matrix4x4, tuples::Tuple};
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves x in proportion to y.
|
|
||||||
/// let transform = Matrix4x4::shearing(1.,0.,0.,0.,0.,0.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(5.,3.,4.));
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves x in proportion to z.
|
|
||||||
/// let transform = Matrix4x4::shearing(0.,1.,0.,0.,0.,0.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(6.,3.,4.));
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves y in proportion to x.
|
|
||||||
/// let transform = Matrix4x4::shearing(0.,0.,1.,0.,0.,0.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(2.,5.,4.));
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves y in proportion to z.
|
|
||||||
/// let transform = Matrix4x4::shearing(0.,0.,0.,1.,0.,0.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(2.,7.,4.));
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves z in proportion to x.
|
|
||||||
/// let transform = Matrix4x4::shearing(0.,0.,0.,0.,1.,0.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(2.,3.,6.));
|
|
||||||
///
|
|
||||||
/// // A shearing transform moves z in proportion to y.
|
|
||||||
/// let transform = Matrix4x4::shearing(0.,0.,0.,0.,0.,1.);
|
|
||||||
/// let p = Tuple::point(2.,3.,4.);
|
|
||||||
/// assert_eq!(transform * p, Tuple::point(2.,3.,7.));
|
|
||||||
|
|
||||||
pub fn shearing(xy: f32, xz: f32, yx: f32, yz: f32, zx: f32, zy: f32) -> Matrix4x4 {
|
|
||||||
Matrix4x4::new(
|
|
||||||
[1., xy, xz, 0.],
|
|
||||||
[yx, 1., yz, 0.],
|
|
||||||
[zx, zy, 1., 0.],
|
|
||||||
[0., 0., 0., 1.],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new matrix that is the inverse of self. If self is A, inverse returns A<sup>-1</sup>, where
|
/// Returns a new matrix that is the inverse of self. If self is A, inverse returns A<sup>-1</sup>, where
|
||||||
/// AA<sup>-1</sup> = I.
|
/// AA<sup>-1</sup> = I.
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
use crate::EPSILON;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Tuple {
|
pub struct Tuple {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
@ -118,10 +116,10 @@ impl Sub for Tuple {
|
|||||||
|
|
||||||
impl PartialEq for Tuple {
|
impl PartialEq for Tuple {
|
||||||
fn eq(&self, rhs: &Tuple) -> bool {
|
fn eq(&self, rhs: &Tuple) -> bool {
|
||||||
((self.x - rhs.x).abs() < EPSILON)
|
((self.x - rhs.x).abs() < f32::EPSILON)
|
||||||
&& ((self.y - rhs.y).abs() < EPSILON)
|
&& ((self.y - rhs.y).abs() < f32::EPSILON)
|
||||||
&& ((self.z - rhs.z).abs() < EPSILON)
|
&& ((self.z - rhs.z).abs() < f32::EPSILON)
|
||||||
&& ((self.w - rhs.w).abs() < EPSILON)
|
&& ((self.w - rhs.w).abs() < f32::EPSILON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn dot(a: Tuple, b: Tuple) -> f32 {
|
pub fn dot(a: Tuple, b: Tuple) -> f32 {
|
||||||
@ -223,7 +221,7 @@ impl Sub for Color {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{cross, dot, Color, Tuple, EPSILON};
|
use super::{cross, dot, Color, Tuple};
|
||||||
#[test]
|
#[test]
|
||||||
fn is_point() {
|
fn is_point() {
|
||||||
// A tuple with w = 1 is a point
|
// A tuple with w = 1 is a point
|
||||||
@ -328,7 +326,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn vector_normalize_magnitude() {
|
fn vector_normalize_magnitude() {
|
||||||
let len = Tuple::vector(1., 2., 3.).normalize().magnitude();
|
let len = Tuple::vector(1., 2., 3.).normalize().magnitude();
|
||||||
assert!((1. - len).abs() < EPSILON);
|
assert!((1. - len).abs() < f32::EPSILON);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn dot_two_tuples() {
|
fn dot_two_tuples() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user