diff --git a/fractional/src/geometry.rs b/fractional/src/geometry.rs index ede5968..3319ca5 100644 --- a/fractional/src/geometry.rs +++ b/fractional/src/geometry.rs @@ -18,12 +18,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // -use std::convert::From; +use std::convert::{From, Into}; use std::ops::{Add,Sub,Neg,Mul,Div}; use std::fmt::Debug; use crate::easel::{Canvas,Coordinate,Coordinates,Polygon}; -use crate::transform::TMatrix; +use crate::transform::{TMatrix, Transformable}; use crate::trigonometry::Trig; use crate::vector::Vector; @@ -34,10 +34,113 @@ where T: Add + Sub + Neg + Mul + Div + Copy + Trig { normal :Option>, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Point(pub Vector, T) + where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig; + +impl Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy + From { + pub fn new(x :T, y :T, z :T) -> Self { + Self(Vector(x, y, z), 1.into()) + } +} + +impl Add for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy { + type Output = Self; + + fn add(self, other :Self) -> Self { + let Point(v1, w1) = self; + let Point(v2, w2) = other; + Self(v1 + v2, w1 + w2) + } +} + +impl Neg for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy { + type Output = Self; + + fn neg(self) -> Self { + let Point(v, w) = self; + Self(-v, -w) + } +} + +impl Sub for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy { + type Output = Self; + + fn sub(self, other :Self) -> Self { + self + -other + } +} + +impl Mul for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy + From { + type Output = Self; + + fn mul(self, other :Self) -> Self { + let a :Vector = self.into(); + let b :Vector = other.into(); + + Point(a * b, 1.into()) + } +} + +impl From> for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy + From { + fn from(v :Vector) -> Self { + Point(v, 1.into()) + } +} + +impl Into> for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Trig + Copy + From { + fn into(self) -> Vector { + let Point(v, w) = self; + + if w == 0.into() { + v + } else { + v.mul(&w.recip()) + } + } +} + +impl Transformable for Point +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Debug + Trig + Copy + From { + fn transform(&self, m :&TMatrix) -> Self { + let Point(v, w) = *self; + let (v, w) = m.apply(&v, w); + + if w == 0.into() { + v.into() + } else { + v.mul(&w.recip()).into() + } + } +} + #[derive(Debug)] pub struct Polyeder -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { - points :Vec>, +where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig { + points :Vec>, faces :Vec>, } @@ -65,7 +168,7 @@ where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { impl Camera where T: Add + Sub + Neg + Mul + Div - + Debug + Copy + Trig + From { + + PartialEq + 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 @@ -91,9 +194,9 @@ where T: Add + Sub + Neg self.project } - pub fn project(&self, v :Vector) -> Coordinate { - let p = self.project.apply(&v); - Coordinate(T::round(&p.x()), T::round(&p.y())) + pub fn project(&self, p :Point) -> Coordinate { + let Point(v, _) = p.transform(&self.project); + Coordinate(T::round(&v.x()), T::round(&v.y())) } } @@ -113,16 +216,16 @@ where T: Add + Sub + Neg impl Face where T: Add + Sub + Neg + Mul + Div - + Debug + Copy + Trig + From { - fn new(corners :Vec, ps :&[Vector]) -> Self { + + PartialEq + Debug + Copy + Trig + From { + fn new(corners :Vec, ps :&[Point]) -> Self { let mut f = Face{ corners: corners, normal: None }; f.update_normal(ps); f } - fn update_normal(&mut self, ps :&[Vector]) { - let edge10 = ps[self.corners[1]] - ps[self.corners[0]]; - let edge12 = ps[self.corners[1]] - ps[self.corners[2]]; + fn update_normal(&mut self, ps :&[Point]) { + let edge10 :Vector = (ps[self.corners[1]] - ps[self.corners[0]]).into(); + let edge12 :Vector = (ps[self.corners[1]] - ps[self.corners[2]]).into(); self.normal = Some(edge10 * edge12); } } @@ -130,7 +233,7 @@ where T: Add + Sub + Neg impl Polyeder where T: Add + Sub + Neg + Mul + Div - + Debug + Copy + Trig + From { + + PartialEq + Debug + Copy + Trig + From { fn update_normals(&mut self) { for f in self.faces.iter_mut() { f.update_normal(&self.points); @@ -156,10 +259,10 @@ where T: Add + Sub + Neg // half the deeps in z let _zh :T = T::sqrt(f3).unwrap() / f4 * a; - let ps = vec!( Vector( f0, yc, f0) - , Vector(-ah, -yi, -zi) - , Vector( ah, -yi, -zi) - , Vector( f0, -yi, zc) ); + let ps = vec!( Point::new( f0, yc, f0) + , Point::new(-ah, -yi, -zi) + , Point::new( ah, -yi, -zi) + , Point::new( f0, -yi, zc) ); let fs = vec!( Face::new(vec!(1, 2, 3), &ps) , Face::new(vec!(1, 0, 2), &ps) @@ -177,9 +280,9 @@ where T: Add + Sub + Neg let zc :T = T::sqrt(f3).unwrap() / f3 * a; let ah :T = a / 2.into(); - let ps = vec!( Vector(-ah, f0, -zi) - , Vector( f0, f0, zc) - , Vector( ah, f0, -zi) ); + let ps = vec!( Point::new(-ah, f0, -zi) + , Point::new( f0, f0, zc) + , Point::new( ah, f0, -zi) ); let fs = vec!(Face::new(vec!(0, 1, 2), &ps)); @@ -191,14 +294,14 @@ where T: Add + Sub + Neg pub fn cube(a :T) -> Polyeder { let ah :T = a / From::::from(2); - let ps = vec!( Vector(-ah, ah, -ah) // 0 => front 1 - , Vector(-ah, -ah, -ah) // 1 => front 2 - , Vector( ah, -ah, -ah) // 2 => front 3 - , Vector( ah, ah, -ah) // 3 => front 4 - , Vector(-ah, ah, ah) // 4 => back 1 - , Vector(-ah, -ah, ah) // 5 => back 2 - , Vector( ah, -ah, ah) // 6 => back 3 - , Vector( ah, ah, ah) ); // 7 => back 4 + let ps = vec!( Point::new(-ah, ah, -ah) // 0 => front 1 + , Point::new(-ah, -ah, -ah) // 1 => front 2 + , Point::new( ah, -ah, -ah) // 2 => front 3 + , Point::new( ah, ah, -ah) // 3 => front 4 + , Point::new(-ah, ah, ah) // 4 => back 1 + , Point::new(-ah, -ah, ah) // 5 => back 2 + , Point::new( ah, -ah, ah) // 6 => back 3 + , Point::new( ah, ah, ah) ); // 7 => back 4 let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front , Face::new(vec!(7, 6, 5, 4), &ps) // back @@ -218,8 +321,10 @@ where T: Add + Sub + Neg fn transform(&self, m :&TMatrix) -> Self { let Polyeder{ points: ps, faces: fs } = self; - let mut p = Polyeder{ points: ps.iter().map(|p| m.apply(p)).collect() - , faces: fs.to_vec() }; + let mut p = Polyeder{ + points: ps.iter().map(|p| p.transform(m)).collect() + , faces: fs.to_vec() + }; // TODO alternatively we could rotate the normals too, but this cannot // done with the original matrix… the question is, what is faster. diff --git a/fractional/src/main.rs b/fractional/src/main.rs index 139e69a..6f09ef1 100644 --- a/fractional/src/main.rs +++ b/fractional/src/main.rs @@ -34,7 +34,7 @@ use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline use fractional::fractional::{Fractional, from_vector}; use fractional::trigonometry::Trig; use fractional::vector::Vector; -use fractional::transform::TMatrix; +use fractional::transform::{TMatrix, Transformable}; use fractional::xcb::XcbEasel; use fractional::easel::Canvas; @@ -204,7 +204,8 @@ fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) + Debug + From + Copy + Display { println!("{:>14} : {}", "Vector v1", v1); - println!("{:>14} : {}", "translate v1", TMatrix::translate(v).apply(&v1)); + println!( "{:>14} : {}", "translate v1" + , v.transform(&TMatrix::translate(v))); println!(); fn _rot( o :&str , n :&str , v :&Vector @@ -218,7 +219,7 @@ fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) let mi = fs.iter().map(|f| f(*d as i32)); println!( "{:>14} : {}" , format!("{} {} {}", o, d, n) - , TMatrix::combine(mi).apply(v) ); + , v.transform(&TMatrix::combine(mi)) ); } } @@ -237,7 +238,7 @@ fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) , 210, 225, 240, 270, 300, 315, 330 ].iter() { println!( "{:>14} : {}" , format!("rot_v {} v2", d) - , TMatrix::rotate_v(&v, *d as i32).apply(&v2)); + , v2.transform(&TMatrix::rotate_v(&v, *d as i32)) ); } } @@ -273,9 +274,9 @@ fn _line() { let k = Vector(Fractional(-30,1), Fractional( 30,1), Fractional(0,1)); let rot :TMatrix = TMatrix::rotate_z(20); - let Vector(ix, iy, _) = rot.apply(&i); - let Vector(jx, jy, _) = rot.apply(&j); - let Vector(kx, ky, _) = rot.apply(&k); + let Vector(ix, iy, _) = i.transform(&rot); + let Vector(jx, jy, _) = j.transform(&rot); + let Vector(kx, ky, _) = k.transform(&rot); fn to_i32(x :Fractional) -> i32 { let Fractional(n, d) = x; @@ -294,9 +295,9 @@ fn _line() { let k = Vector(-30.0, 30.0, 0.0); let rot :TMatrix = TMatrix::rotate_z(20); - let Vector(ix, iy, _) = rot.apply(&i); - let Vector(jx, jy, _) = rot.apply(&j); - let Vector(kx, ky, _) = rot.apply(&k); + let Vector(ix, iy, _) = i.transform(&rot); + let Vector(jx, jy, _) = j.transform(&rot); + let Vector(kx, ky, _) = k.transform(&rot); fn to_i32_2(x :f64) -> i32 { x.round() as i32 @@ -334,24 +335,15 @@ fn _democanvas( xcb :&XcbEasel let step = Duration::from_millis(25); let mut last = Instant::now(); - let t :TMatrix = TMatrix::translate(Vector( 0.into() - , 0.into() - , 150.into() )); - // We do not need this here… it is used within projection… - // let p :TMatrix = camera.get_projection(); + let t = TMatrix::translate(Vector(0.into() , 0.into() , 150.into())); loop { let deg = ((start.elapsed() / 25).as_millis() % 360) as i32; - let rz :TMatrix = TMatrix::rotate_z(deg); - let rx :TMatrix = TMatrix::rotate_x(-deg*2); - let ry :TMatrix = TMatrix::rotate_y(-deg*2); + let rz = TMatrix::rotate_z(deg); + let rx = TMatrix::rotate_x(-deg*2); + let ry = TMatrix::rotate_y(-deg*2); - // I can not apply the projection in one turn, as I generate the - // normals always… and this is no longer possible after the - // projection… - // let rot1 = TMatrix::combine(vec!(rz, rx, t, p)); - // let rot2 = TMatrix::combine(vec!(rz, ry, t, p)); let rot1 = TMatrix::combine(vec!(rz, rx, t)); let rot2 = TMatrix::combine(vec!(rz, ry, t)); diff --git a/fractional/src/transform.rs b/fractional/src/transform.rs index db67249..e7e3142 100644 --- a/fractional/src/transform.rs +++ b/fractional/src/transform.rs @@ -31,6 +31,11 @@ pub struct TMatrix( (T, T, T, T) , (T, T, T, T) ) where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From + Copy; +pub trait Transformable +where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From + Copy { + fn transform(&self, m :&TMatrix) -> Self; +} + impl TMatrix where T: Add + Sub + Neg + Mul + Div @@ -127,19 +132,20 @@ where T: Add + Sub + Neg mi.into_iter().fold(Self::unit(), |acc, x| x * acc) } - pub fn apply(&self, v :&Vector) -> Vector { + pub fn apply(&self, v :&Vector, w :T) -> (Vector, T) { let TMatrix( (a11, a12, a13, a14) , (a21, a22, a23, a24) , (a31, a32, a33, a34) , (a41, a42, a43, a44) ) = *self; let Vector(x, y, z) = *v; - let v = Vector( a11 * x + a12 * y + a13 * z + a14 - , a21 * x + a22 * y + a23 * z + a24 - , a31 * x + a32 * y + a33 * z + a34 ); - let w = a41 * x + a42 * y + a43 * z + a44; + let v = Vector( a11 * x + a12 * y + a13 * z + a14 * w + , a21 * x + a22 * y + a23 * z + a24 * w + , a31 * x + a32 * y + a33 * z + a34 * w ); + let w = a41 * x + a42 * y + a43 * z + a44 * w; - v.mul(&w.recip()) + //v.mul(&w.recip()) + (v, w) } } diff --git a/fractional/src/vector.rs b/fractional/src/vector.rs index 30d126a..28ad18c 100644 --- a/fractional/src/vector.rs +++ b/fractional/src/vector.rs @@ -18,10 +18,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // -use std::fmt::{Formatter, Display, Result}; +use std::fmt::{Debug, Display, Formatter, Result}; use std::ops::{Add, Sub, Neg, Mul, Div}; use crate::trigonometry::Trig; +use crate::transform::{TMatrix, Transformable}; #[derive(Debug, Eq, Clone, Copy)] pub struct Vector(pub T, pub T, pub T) @@ -126,3 +127,13 @@ where T: Add + Sub + Neg , ax * by - ay * bx ) } } + +impl Transformable for Vector +where T: Add + Sub + Neg + + Mul + Div + + Trig + Copy + Debug + From { + fn transform(&self, m :&TMatrix) -> Self { + let (v, _) = m.apply(self, 0.into()); + v + } +}