From de6d78411a031212c40ed1f0967e3198d6b2c919 Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Sun, 12 Jan 2020 16:58:50 +0100 Subject: [PATCH] split geometry in several source files --- src/geometry.rs | 373 --------------------------------- src/lib.rs | 8 +- src/math/mod.rs | 28 +++ src/{ => math}/transform.rs | 4 +- src/{ => math}/trigonometry.rs | 0 src/{ => math}/vector.rs | 4 +- src/space/camera.rs | 81 +++++++ src/space/face.rs | 62 ++++++ src/space/light.rs | 50 +++++ src/space/mod.rs | 27 +++ src/space/point.rs | 130 ++++++++++++ src/space/polyeder.rs | 189 +++++++++++++++++ src/space/primitives.rs | 39 ++++ 13 files changed, 612 insertions(+), 383 deletions(-) delete mode 100644 src/geometry.rs create mode 100644 src/math/mod.rs rename src/{ => math}/transform.rs (98%) rename src/{ => math}/trigonometry.rs (100%) rename src/{ => math}/vector.rs (97%) create mode 100644 src/space/camera.rs create mode 100644 src/space/face.rs create mode 100644 src/space/light.rs create mode 100644 src/space/mod.rs create mode 100644 src/space/point.rs create mode 100644 src/space/polyeder.rs create mode 100644 src/space/primitives.rs diff --git a/src/geometry.rs b/src/geometry.rs deleted file mode 100644 index 80ee0df..0000000 --- a/src/geometry.rs +++ /dev/null @@ -1,373 +0,0 @@ -// -// Basic geometric things... -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::convert::{From, Into}; -use std::ops::{Add,Sub,Neg,Mul,Div}; -use std::fmt::Debug; - -use crate::easel::canvas::{Canvas, Vertex}; -use crate::easel::polygon::Polygon; -use crate::transform::{TMatrix, Transformable}; -use crate::trigonometry::Trig; -use crate::vector::Vector; - -#[derive(Debug, Clone)] -pub struct Face -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { - corners :Vec, - 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 + PartialEq + Copy + Trig { - points :Vec>, - faces :Vec>, -} - -pub trait Primitives -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - fn transform(&self, m :&TMatrix) -> Self; - fn project( &self - , camera :&Camera - , light :&DirectLight - , col :u32 ) -> Vec<(Polygon, u32)>; -} - -#[derive(Debug, Clone, Copy)] -pub struct Camera -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - width :T, - height :T, - distance :T, - project :TMatrix, -} - -pub struct DirectLight -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - direction: Vector, -} - -impl Camera -where T: Add + Sub + Neg - + Mul + Div - + 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 - // and screen sizes. - pub fn new(c :&dyn Canvas, angle :i32) -> Self { - 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 - , distance: d - , 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 get_distance(&self) -> T { - self.distance - } - - pub fn get_projection(&self) -> TMatrix { - self.project - } - - pub fn project(&self, p :Point) -> Point { - p.transform(&self.project) - } -} - -impl DirectLight -where T: Add + Sub + Neg - + Mul + Div - + Debug + Copy + Trig + From { - pub fn new(v :Vector) -> Self { - DirectLight{ direction: v } - } - - pub fn dir(&self) -> Vector { - self.direction - } -} - -impl Face -where T: Add + Sub + Neg - + Mul + Div - + 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 :&[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); - } -} - -impl Polyeder -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Debug + Copy + Trig + From { - fn update_normals(&mut self) { - for f in self.faces.iter_mut() { - f.update_normal(&self.points); - } - } - - // construct via cube, see polyhedra.pdf - pub fn tetrahedron(a :T) -> Polyeder { - let f2 :T = 2.into(); - let ch = a / (f2 * T::sqrt(f2).unwrap()); - - let ps = vec!( Point::new(-ch, -ch, ch) - , Point::new(-ch, ch, -ch) - , Point::new( ch, -ch, -ch) - , Point::new( ch, ch, ch) ); - - let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom - , Face::new(vec!(3, 2, 0), &ps) - , Face::new(vec!(0, 1, 3), &ps) - , Face::new(vec!(1, 2, 3), &ps) ); - - Polyeder{ points: ps, faces: fs } - } - - pub fn triangle(a :T) -> Polyeder { - let f0 :T = 0.into(); - let f3 :T = 3.into(); - let f6 :T = 6.into(); - let zi :T = T::sqrt(f3).unwrap() / f6 * a; - let zc :T = T::sqrt(f3).unwrap() / f3 * a; - let ah :T = a / 2.into(); - - 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)); - - Polyeder{ points: ps, faces: fs } - } - - pub fn cube(a :T) -> Polyeder { - let ah :T = a / From::::from(2); - - 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 - , Face::new(vec!(1, 5, 6, 2), &ps) // top - , Face::new(vec!(0, 3, 7, 4), &ps) // bottom - , Face::new(vec!(0, 4, 5, 1), &ps) // left - , Face::new(vec!(2, 6, 7, 3), &ps) ); // right - - Polyeder{ points: ps, faces: fs } - } -} - -impl Primitives for Polyeder -where T: Add + Sub + Neg - + Mul + Div - + Debug + Copy + Trig + From + PartialOrd { - // TODO Maybe this should also be an instance of Transformable… - fn transform(&self, m :&TMatrix) -> Self { - let Polyeder{ points: ps, faces: fs } = self; - - 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. - p.update_normals(); - p - } - - fn project( &self - , camera :&Camera - , light :&DirectLight - , color :u32 ) -> Vec<(Polygon, u32)> { - // Helper to create a Polygon from Coordinates… - // TODO probably there needs to be a Polygon constructor for this. - fn polygon(c :I) -> Polygon - where I: Iterator> { - Polygon(c.collect()) - } - - // 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| { - let Point(v, _) = camera.project(self.points[*p]); - Vertex::new(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into()) - }; - let to_poly = |f :&Face| { - let pg = polygon(f.corners.iter().map(to_coord)); - let mut r :T = (((color >> 16) & 0xFF) as i32).into(); - let mut g :T = (((color >> 8) & 0xFF) as i32).into(); - let mut b :T = (((color ) & 0xFF) as i32).into(); - let lf :T = match f.normal { - None => 1.into(), - Some(n) => n.dot(light.dir()) - / (n.mag() * light.dir().mag()), - }; - - // this "if" represents a first simple backface culling - // approach. We only return face that face towards us. - if lf < 0.into() { - r = r * -lf; - g = g * -lf; - b = b * -lf; - - let c :u32 = (r.round() as u32) << 16 - | (g.round() as u32) << 8 - | (b.round() as u32); - - Some((pg, c)) - } else { - None - }}; - - self.faces.iter().filter_map(to_poly).collect() - } -} diff --git a/src/lib.rs b/src/lib.rs index 2e2eb49..b47a0a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,9 +35,5 @@ extern crate lazy_static; pub type Error = &'static str; pub mod easel; -pub mod transform; -pub mod trigonometry; -pub mod vector; -pub mod geometry; - -use vector::Vector; +pub mod space; +pub mod math; diff --git a/src/math/mod.rs b/src/math/mod.rs new file mode 100644 index 0000000..6e5fd54 --- /dev/null +++ b/src/math/mod.rs @@ -0,0 +1,28 @@ +// +// Math needed for 3D transformations and projection. +// +// Georg Hopp +// +// Copyright © 2020 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// generics for needed trigonometric stuff. As well as an implementation for +// f64. +pub mod trigonometry; + +// generics for 3D math stuff. +pub mod vector; +pub mod transform; diff --git a/src/transform.rs b/src/math/transform.rs similarity index 98% rename from src/transform.rs rename to src/math/transform.rs index e7e3142..3a6356d 100644 --- a/src/transform.rs +++ b/src/math/transform.rs @@ -21,8 +21,8 @@ use std::ops::{Add, Sub, Neg, Mul, Div}; use std::fmt::Debug; -use crate::Vector; -use crate::trigonometry::Trig; +use crate::math::vector::Vector; +use crate::math::trigonometry::Trig; #[derive(Debug, Clone, Copy)] pub struct TMatrix( (T, T, T, T) diff --git a/src/trigonometry.rs b/src/math/trigonometry.rs similarity index 100% rename from src/trigonometry.rs rename to src/math/trigonometry.rs diff --git a/src/vector.rs b/src/math/vector.rs similarity index 97% rename from src/vector.rs rename to src/math/vector.rs index 28ad18c..a0b84a2 100644 --- a/src/vector.rs +++ b/src/math/vector.rs @@ -21,8 +21,8 @@ use std::fmt::{Debug, Display, Formatter, Result}; use std::ops::{Add, Sub, Neg, Mul, Div}; -use crate::trigonometry::Trig; -use crate::transform::{TMatrix, Transformable}; +use crate::math::trigonometry::Trig; +use crate::math::transform::{TMatrix, Transformable}; #[derive(Debug, Eq, Clone, Copy)] pub struct Vector(pub T, pub T, pub T) diff --git a/src/space/camera.rs b/src/space/camera.rs new file mode 100644 index 0000000..5c3bb77 --- /dev/null +++ b/src/space/camera.rs @@ -0,0 +1,81 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::easel::canvas::{Canvas, Vertex}; +use crate::math::transform::{TMatrix, Transformable}; +use crate::math::trigonometry::Trig; +use crate::math::vector::Vector; + +use super::point::Point; + +#[derive(Debug, Clone, Copy)] +pub struct Camera +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { + width :T, + height :T, + distance :T, + project :TMatrix, +} + +impl Camera +where T: Add + Sub + Neg + + Mul + Div + + 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 + // and screen sizes. + pub fn new(c :&dyn Canvas, angle :i32) -> Self { + 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 + , distance: d + , 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 get_distance(&self) -> T { + self.distance + } + + pub fn get_projection(&self) -> TMatrix { + self.project + } + + pub fn project(&self, p :Point) -> Vertex { + let v :Vector = p.transform(&self.project).into(); + Vertex::new( T::round(&v.x()) + , T::round(&v.y()) + , v.z() - self.distance ) + } +} diff --git a/src/space/face.rs b/src/space/face.rs new file mode 100644 index 0000000..4c54005 --- /dev/null +++ b/src/space/face.rs @@ -0,0 +1,62 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::math::trigonometry::Trig; +use crate::math::vector::Vector; + +use super::point::Point; + +#[derive(Debug, Clone)] +pub struct Face +where T: Add + Sub + Neg + Mul + Div + Copy + Trig { + corners :Vec, + normal :Option>, +} + +impl<'a, T> Face +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Debug + Copy + Trig + From { + pub fn new(corners :Vec, ps :&[Point]) -> Self { + let mut f = Face{ corners: corners, normal: None }; + f.update_normal(ps); + f + } + + #[inline] + pub fn corners(&self) -> &[usize] { + &self.corners + } + + #[inline] + pub fn normal(&self) -> Option<&Vector> { + (&self.normal).as_ref() + } + + pub 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); + } +} diff --git a/src/space/light.rs b/src/space/light.rs new file mode 100644 index 0000000..f77fed4 --- /dev/null +++ b/src/space/light.rs @@ -0,0 +1,50 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::math::transform::{TMatrix, Transformable}; +use crate::math::trigonometry::Trig; +use crate::math::vector::Vector; + +pub struct DirectLight +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { + direction: Vector, +} + +impl DirectLight +where T: Add + Sub + Neg + + Mul + Div + + Debug + Copy + Trig + From { + pub fn new(v :Vector) -> Self { + DirectLight{ direction: v } + } + + pub fn transform(&self, m :&TMatrix) -> Self { + let DirectLight{ direction: v } = self; + DirectLight { direction: v.transform(m) } + } + + pub fn dir(&self) -> Vector { + self.direction + } +} diff --git a/src/space/mod.rs b/src/space/mod.rs new file mode 100644 index 0000000..ff8f286 --- /dev/null +++ b/src/space/mod.rs @@ -0,0 +1,27 @@ +// +// This holds 3D primitives. +// +// Georg Hopp +// +// Copyright © 2020 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +pub mod face; +pub mod point; +pub mod polyeder; +pub mod primitives; +pub mod camera; +pub mod light; diff --git a/src/space/point.rs b/src/space/point.rs new file mode 100644 index 0000000..6f0bf9f --- /dev/null +++ b/src/space/point.rs @@ -0,0 +1,130 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::math::transform::{TMatrix, Transformable}; +use crate::math::trigonometry::Trig; +use crate::math::vector::Vector; + +#[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() + } + } +} diff --git a/src/space/polyeder.rs b/src/space/polyeder.rs new file mode 100644 index 0000000..22101b5 --- /dev/null +++ b/src/space/polyeder.rs @@ -0,0 +1,189 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::easel::polygon::Polygon; +use crate::easel::canvas::Vertex; + +use crate::math::transform::{TMatrix, Transformable}; +use crate::math::trigonometry::Trig; +use crate::math::vector::Vector; + +use super::camera::Camera; +use super::face::Face; +use super::light::DirectLight; +use super::point::Point; +use super::primitives::Primitives; + +#[derive(Debug)] +pub struct Polyeder +where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig { + points :Vec>, + faces :Vec>, +} + +impl Polyeder +where T: Add + Sub + Neg + + Mul + Div + + PartialEq + Debug + Copy + Trig + From { + fn update_normals(&mut self) { + for f in self.faces.iter_mut() { + f.update_normal(&self.points); + } + } + + // construct via cube, see polyhedra.pdf + pub fn tetrahedron(a :T) -> Polyeder { + let f2 :T = 2.into(); + let ch = a / (f2 * T::sqrt(f2).unwrap()); + + let ps = vec!( Point::new(-ch, -ch, ch) + , Point::new(-ch, ch, -ch) + , Point::new( ch, -ch, -ch) + , Point::new( ch, ch, ch) ); + + let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom + , Face::new(vec!(3, 2, 0), &ps) + , Face::new(vec!(0, 1, 3), &ps) + , Face::new(vec!(1, 2, 3), &ps) ); + + Polyeder{ points: ps, faces: fs } + } + + pub fn triangle(a :T) -> Polyeder { + let f0 :T = 0.into(); + let f3 :T = 3.into(); + let f6 :T = 6.into(); + let zi :T = T::sqrt(f3).unwrap() / f6 * a; + let zc :T = T::sqrt(f3).unwrap() / f3 * a; + let ah :T = a / 2.into(); + + 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)); + + Polyeder{ points: ps, faces: fs } + } + + pub fn cube(a :T) -> Polyeder { + let ah :T = a / From::::from(2); + + 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 + , Face::new(vec!(1, 5, 6, 2), &ps) // top + , Face::new(vec!(0, 3, 7, 4), &ps) // bottom + , Face::new(vec!(0, 4, 5, 1), &ps) // left + , Face::new(vec!(2, 6, 7, 3), &ps) ); // right + + Polyeder{ points: ps, faces: fs } + } +} + +impl Primitives for Polyeder +where T: Add + Sub + Neg + + Mul + Div + + Debug + Copy + Trig + From + From + PartialOrd { + // TODO Maybe this should also be an instance of Transformable… + fn transform(&self, m :&TMatrix) -> Self { + let Polyeder{ points: ps, faces: fs } = self; + + 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. + p.update_normals(); + p + } + + fn project( &self + , camera :&Camera + , light :&DirectLight + , color :u32 ) -> Vec<(Polygon, u32)> { + // Helper to create a Polygon from Coordinates… + // TODO probably there needs to be a Polygon constructor for this. + fn polygon(c :I) -> Polygon + where I: Iterator> { + Polygon(c.collect()) + } + + // currently our cam has only one direction... + let cam_dir = Vector(0.into(), 0.into(), 1.into()); + + // 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_poly = |f :&Face| { + let pg = polygon(f.corners().iter().map(to_coord)); + let mut r :T = (((color >> 16) & 0xFF) as i32).into(); + let mut g :T = (((color >> 8) & 0xFF) as i32).into(); + let mut b :T = (((color ) & 0xFF) as i32).into(); + let lf :T = match f.normal() { + None => 1.into(), + Some(n) => n.dot(light.dir()) + / (n.mag() * light.dir().mag()), + }; + let view_f :T = match f.normal() { + None => 1.into(), + Some(n) => n.dot(cam_dir) + / (n.mag() * cam_dir.mag()), + }; + + // this "if" represents a first simple backface culling + // approach. We only return face that face towards us. + if view_f >= 0.into() { + None + } else { + if lf < (-0.1).into() { + r = r * -lf; + g = g * -lf; + b = b * -lf; + } else { + r = r * 0.1.into(); + g = g * 0.1.into(); + b = b * 0.1.into(); + } + + let c :u32 = (r.round() as u32) << 16 + | (g.round() as u32) << 8 + | (b.round() as u32); + + Some((pg, c)) + }}; + + self.faces.iter().filter_map(to_poly).collect() + } +} diff --git a/src/space/primitives.rs b/src/space/primitives.rs new file mode 100644 index 0000000..18dfc14 --- /dev/null +++ b/src/space/primitives.rs @@ -0,0 +1,39 @@ +// +// Basic geometric things... +// +// Georg Hopp +// +// Copyright © 2019 Georg Hopp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; + +use crate::easel::polygon::Polygon; +use crate::math::transform::TMatrix; +use crate::math::trigonometry::Trig; + +use super::camera::Camera; +use super::light::DirectLight; + +pub trait Primitives +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { + fn transform(&self, m :&TMatrix) -> Self; + fn project( &self + , camera :&Camera + , light :&DirectLight + , col :u32 ) -> Vec<(Polygon, u32)>; +}