// // 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; 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::trigonometry::Trig; use crate::vector::Vector; #[derive(Debug)] pub struct Polyeder where T: Add + Sub + Neg + Mul + Div + Copy + Trig { points :Vec>, faces :Vec>, } pub trait Primitives where T: Add + Sub + Neg + Mul + Div + 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, } impl Camera where T: Add + Sub + Neg + Mul + Div + Copy + Trig + From { 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 } } 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; Coordinate(T::round(&xs), T::round(&ys)) } } impl Polyeder where T: Add + Sub + Neg + Mul + Div + 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 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); // half the height in y let _yh :T = a / f6 * T::sqrt(f6).unwrap(); // half the deeps in z let _zh :T = T::sqrt(f3).unwrap() / f4 * a; Polyeder{ points: vec!( Vector( f0, yc, f0) , Vector(-ah, -yi, -zi) , Vector( ah, -yi, -zi) , Vector( f0, -yi, zc) ) , faces: vec!( vec!(1, 2, 3) , vec!(1, 0, 2) , vec!(3, 0, 1) , vec!(2, 0, 3) )} } pub fn cube(a :T) -> Polyeder { let ah :T = a / From::::from(2); Polyeder{ points: 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 , faces: vec!( vec!(0, 1, 2, 3) // front , vec!(7, 6, 5, 4) // back , vec!(1, 5, 6, 2) // top , vec!(0, 3, 7, 4) // bottom , vec!(0, 4, 5, 1) // left , vec!(2, 6, 7, 3) )} // right } } impl Primitives for Polyeder where T: Add + Sub + Neg + Mul + Div + 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 { fn polygon(c :I) -> Polygon where I: Iterator { Polygon(Coordinates(c.collect())) } let to_coord = |p :&usize| camera.project(self.points[*p]); let to_poly = |f :&Vec| polygon(f.iter().map(to_coord)); self.faces.iter().map(to_poly).collect() } }