diff --git a/fractional/src/geometry.rs b/fractional/src/geometry.rs index 8e3e0e0..fbdbc27 100644 --- a/fractional/src/geometry.rs +++ b/fractional/src/geometry.rs @@ -29,68 +29,76 @@ use crate::vector::Vector; #[derive(Debug)] pub struct Polyeder -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig { points :Vec>, faces :Vec>, } pub trait Primitives -where T: Add + Sub + Neg + Mul + Div + Copy + Trig + From { +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { fn transform(&self, m :&TMatrix) -> Self; fn project(&self, camera :&Camera) -> Vec; } pub struct Camera -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { - width :T, - height :T, - fovx :T, - fovy :T, +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { + width :T, + height :T, + project :TMatrix, } impl Camera where T: Add + Sub + Neg + Mul + Div - + Copy + Trig + From { + + Debug + Copy + Trig + From { + // This code assumes that the size of the viewport is always + // equal to the size of the physical screen… e.g. window/canvas thus some + // effects can't be done. See book for examples with different viewport + // and screen sizes. pub fn new(c :&dyn Canvas, angle :i32) -> Self { - let width = >::from(c.width() as i32); - let height = >::from(c.height() as i32); - - // The calculations for fovx and fovy are taken from a book, but I - // have the impression, coming from my limited algebra knowledge, - // that they are always equal… - Camera { width: width - , height: height - , fovx: T::cot(angle) * width - , fovy: width / height * T::cot(angle) * height } + let width :T = (c.width() as i32).into(); + let height :T = (c.height() as i32).into(); + let d :T = 1.into(); + let fov = T::cot(angle) * width; + let wh = width / 2.into(); + let hh = height / 2.into(); + + Camera { width: width + , height: height + , project: TMatrix::new( + ( fov, 0.into(), wh, 0.into()) + , (0.into(), fov, hh, 0.into()) + , (0.into(), 0.into(), d, 1.into()) + , (0.into(), 0.into(), 1.into(), 0.into()) ) } } - pub fn project(&self, v :Vector) -> Coordinate { - let f2 = From::::from(2); - let xs = v.x() / v.z() * self.fovx + self.width / f2; - let ys = v.y() / v.z() * self.fovy + self.height / f2; + pub fn get_projection(&self) -> TMatrix { + self.project + } - Coordinate(T::round(&xs), T::round(&ys)) + pub fn project(&self, v :Vector) -> Coordinate { + let p = self.project.apply(&v); + Coordinate(T::round(&p.x()), T::round(&p.y())) } } impl Polyeder where T: Add + Sub + Neg + Mul + Div - + Copy + Trig + From { + + Debug + Copy + Trig + From { // https://rechneronline.de/pi/tetrahedron.php pub fn tetrahedron(a :T) -> Polyeder { - let f0 :T = From::::from(0); - let f3 :T = From::::from(3); - let f4 :T = From::::from(4); - let f6 :T = From::::from(6); - let f12 :T = From::::from(12); + let f0 :T = 0.into(); + let f3 :T = 3.into(); + let f4 :T = 4.into(); + let f6 :T = 6.into(); + let f12 :T = 12.into(); let yi :T = a / f12 * T::sqrt(f6).unwrap(); let yc :T = a / f4 * T::sqrt(f6).unwrap(); let zi :T = T::sqrt(f3).unwrap() / f6 * a; let zc :T = T::sqrt(f3).unwrap() / f3 * a; - let ah :T = a / From::::from(2); + let ah :T = a / 2.into(); // half the height in y let _yh :T = a / f6 * T::sqrt(f6).unwrap(); @@ -130,19 +138,27 @@ where T: Add + Sub + Neg impl Primitives for Polyeder where T: Add + Sub + Neg + Mul + Div - + Copy + Trig + From + From { + + Debug + Copy + Trig + From + From { fn transform(&self, m :&TMatrix) -> Self { Polyeder{ points: self.points.iter().map(|p| m.apply(p)).collect() , faces: self.faces.to_vec() } } - fn project(&self, camera :&Camera) -> Vec { + // TODO for now we assume already prejected vertices (points) + // in future we need to distinguish more clear between vertex and point + // and projected_point. + fn project(&self, _camera :&Camera) -> Vec { fn polygon(c :I) -> Polygon where I: Iterator { Polygon(Coordinates(c.collect())) } - let to_coord = |p :&usize| camera.project(self.points[*p]); + // this one does the projection... as the projection was the last + // matrix we do not need to do it here. + // let to_coord = |p :&usize| _camera.project(self.points[*p]); + let to_coord = |p :&usize| { + let v = self.points[*p]; + Coordinate(v.x().round(), v.y().round()) }; let to_poly = |f :&Vec| polygon(f.iter().map(to_coord)); self.faces.iter().map(to_poly).collect() diff --git a/fractional/src/main.rs b/fractional/src/main.rs index 3f2b98e..028732e 100644 --- a/fractional/src/main.rs +++ b/fractional/src/main.rs @@ -20,7 +20,7 @@ // use std::convert::{TryFrom, TryInto, Into}; use std::f64::consts::PI as FPI; -use std::fmt::Display; +use std::fmt::{Debug, Display}; use std::marker::Send; use std::num::TryFromIntError; use std::ops::{Add,Sub,Neg,Mul,Div}; @@ -201,7 +201,7 @@ fn _transform2() { fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) where T: Add + Sub + Neg + Mul + Div + Trig - + From + Copy + Display { + + Debug + From + Copy + Display { println!("{:>14} : {}", "Vector v1", v1); println!("{:>14} : {}", "translate v1", translate(v).apply(&v1)); @@ -211,7 +211,7 @@ fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) , fs :&[&dyn Fn(i32) -> TMatrix] ) where T: Add + Sub + Neg + Mul + Div + Trig - + From + Copy + Display { + + Debug + From + Copy + Display { for d in [ 30, 45, 60, 90, 120, 135, 150, 180 , 210, 225, 240, 270, 300, 315, 330 ].iter() { @@ -317,7 +317,7 @@ fn _democanvas( xcb :&XcbEasel , cube :Polyeder ) where T: 'static + Add + Sub + Neg + Mul + Div - + Copy + Trig + Send + From { + + Debug + Copy + Trig + Send + From { let mut canvas = xcb.canvas(151, 151).unwrap(); let camera = Camera::::new(&canvas, 45); // the orig. view angle @@ -333,14 +333,15 @@ fn _democanvas( xcb :&XcbEasel let mut last = Instant::now(); let t :TMatrix = translate(Vector(0.into(), 0.into(), 150.into())); + let p :TMatrix = camera.get_projection(); loop { let deg = ((start.elapsed() / 25).as_millis() % 360) as i32; let rz :TMatrix = rotate_z(deg); - let rot1 = TMatrix::combine(vec!(rz, rotate_x(-deg*2), t)); - let rot2 = TMatrix::combine(vec!(rz, rotate_y(-deg*2), t)); + let rot1 = TMatrix::combine(vec!(rz, rotate_x(-deg*2), t, p)); + let rot2 = TMatrix::combine(vec!(rz, rotate_y(-deg*2), t, p)); let objects = vec!( (tetrahedron.transform(&rot1), 0xFFFF00) , ( cube.transform(&rot2), 0x0000FF) );