Compare commits
5 Commits
b8df830460
...
12c2382327
| Author | SHA1 | Date | |
|---|---|---|---|
| 12c2382327 | |||
| 5df2917668 | |||
| af5e61136c | |||
| 4b0d882b84 | |||
| 83799a02a9 |
51
rtchallenge/examples/eoc4.rs
Normal file
51
rtchallenge/examples/eoc4.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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,3 +1,6 @@
|
|||||||
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,13 +1,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::{Index, IndexMut, Mul};
|
use std::ops::{Index, IndexMut, Mul};
|
||||||
|
|
||||||
// Implement a PartialEq that does approx_eq internally.
|
use crate::{tuples::Tuple, EPSILON};
|
||||||
//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 {
|
||||||
@ -170,6 +164,36 @@ 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],
|
||||||
}
|
}
|
||||||
@ -402,6 +426,50 @@ 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,5 +1,7 @@
|
|||||||
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,
|
||||||
@ -116,10 +118,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() < f32::EPSILON)
|
((self.x - rhs.x).abs() < EPSILON)
|
||||||
&& ((self.y - rhs.y).abs() < f32::EPSILON)
|
&& ((self.y - rhs.y).abs() < EPSILON)
|
||||||
&& ((self.z - rhs.z).abs() < f32::EPSILON)
|
&& ((self.z - rhs.z).abs() < EPSILON)
|
||||||
&& ((self.w - rhs.w).abs() < f32::EPSILON)
|
&& ((self.w - rhs.w).abs() < EPSILON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn dot(a: Tuple, b: Tuple) -> f32 {
|
pub fn dot(a: Tuple, b: Tuple) -> f32 {
|
||||||
@ -221,7 +223,7 @@ impl Sub for Color {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{cross, dot, Color, Tuple};
|
use super::{cross, dot, Color, Tuple, EPSILON};
|
||||||
#[test]
|
#[test]
|
||||||
fn is_point() {
|
fn is_point() {
|
||||||
// A tuple with w = 1 is a point
|
// A tuple with w = 1 is a point
|
||||||
@ -326,7 +328,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() < f32::EPSILON);
|
assert!((1. - len).abs() < EPSILON);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn dot_two_tuples() {
|
fn dot_two_tuples() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user