Commit de6d78411a031212c40ed1f0967e3198d6b2c919

Authored by Georg Hopp
1 parent ed113306

split geometry in several source files

@@ -35,9 +35,5 @@ extern crate lazy_static; @@ -35,9 +35,5 @@ extern crate lazy_static;
35 pub type Error = &'static str; 35 pub type Error = &'static str;
36 36
37 pub mod easel; 37 pub mod easel;
38 -pub mod transform;  
39 -pub mod trigonometry;  
40 -pub mod vector;  
41 -pub mod geometry;  
42 -  
43 -use vector::Vector; 38 +pub mod space;
  39 +pub mod math;
  1 +//
  2 +// Math needed for 3D transformations and projection.
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2020 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +// generics for needed trigonometric stuff. As well as an implementation for
  23 +// f64.
  24 +pub mod trigonometry;
  25 +
  26 +// generics for 3D math stuff.
  27 +pub mod vector;
  28 +pub mod transform;
@@ -21,8 +21,8 @@ @@ -21,8 +21,8 @@
21 use std::ops::{Add, Sub, Neg, Mul, Div}; 21 use std::ops::{Add, Sub, Neg, Mul, Div};
22 use std::fmt::Debug; 22 use std::fmt::Debug;
23 23
24 -use crate::Vector;  
25 -use crate::trigonometry::Trig; 24 +use crate::math::vector::Vector;
  25 +use crate::math::trigonometry::Trig;
26 26
27 #[derive(Debug, Clone, Copy)] 27 #[derive(Debug, Clone, Copy)]
28 pub struct TMatrix<T>( (T, T, T, T) 28 pub struct TMatrix<T>( (T, T, T, T)
@@ -21,8 +21,8 @@ @@ -21,8 +21,8 @@
21 use std::fmt::{Debug, Display, Formatter, Result}; 21 use std::fmt::{Debug, Display, Formatter, Result};
22 use std::ops::{Add, Sub, Neg, Mul, Div}; 22 use std::ops::{Add, Sub, Neg, Mul, Div};
23 23
24 -use crate::trigonometry::Trig;  
25 -use crate::transform::{TMatrix, Transformable}; 24 +use crate::math::trigonometry::Trig;
  25 +use crate::math::transform::{TMatrix, Transformable};
26 26
27 #[derive(Debug, Eq, Clone, Copy)] 27 #[derive(Debug, Eq, Clone, Copy)]
28 pub struct Vector<T>(pub T, pub T, pub T) 28 pub struct Vector<T>(pub T, pub T, pub T)
  1 +//
  2 +// Basic geometric things...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
  24 +
  25 +use crate::easel::canvas::{Canvas, Vertex};
  26 +use crate::math::transform::{TMatrix, Transformable};
  27 +use crate::math::trigonometry::Trig;
  28 +use crate::math::vector::Vector;
  29 +
  30 +use super::point::Point;
  31 +
  32 +#[derive(Debug, Clone, Copy)]
  33 +pub struct Camera<T>
  34 +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
  35 + width :T,
  36 + height :T,
  37 + distance :T,
  38 + project :TMatrix<T>,
  39 +}
  40 +
  41 +impl<T> Camera<T>
  42 +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
  43 + + Mul<Output = T> + Div<Output = T>
  44 + + PartialEq + Debug + Copy + Trig + From<i32> {
  45 + // This code assumes that the size of the viewport is always
  46 + // equal to the size of the physical screen… e.g. window/canvas thus some
  47 + // effects can't be done. See book for examples with different viewport
  48 + // and screen sizes.
  49 + pub fn new(c :&dyn Canvas<T>, angle :i32) -> Self {
  50 + let width :T = (c.width() as i32).into();
  51 + let height :T = (c.height() as i32).into();
  52 + let d :T = 1.into();
  53 + let fov = T::cot(angle) * width;
  54 + let wh = width / 2.into();
  55 + let hh = height / 2.into();
  56 +
  57 + Camera { width: width
  58 + , height: height
  59 + , distance: d
  60 + , project: TMatrix::new(
  61 + ( fov, 0.into(), wh, 0.into())
  62 + , (0.into(), fov, hh, 0.into())
  63 + , (0.into(), 0.into(), d, 1.into())
  64 + , (0.into(), 0.into(), 1.into(), 0.into()) ) }
  65 + }
  66 +
  67 + pub fn get_distance(&self) -> T {
  68 + self.distance
  69 + }
  70 +
  71 + pub fn get_projection(&self) -> TMatrix<T> {
  72 + self.project
  73 + }
  74 +
  75 + pub fn project(&self, p :Point<T>) -> Vertex<T> {
  76 + let v :Vector<T> = p.transform(&self.project).into();
  77 + Vertex::new( T::round(&v.x())
  78 + , T::round(&v.y())
  79 + , v.z() - self.distance )
  80 + }
  81 +}
  1 +//
  2 +// Basic geometric things...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
  24 +
  25 +use crate::math::trigonometry::Trig;
  26 +use crate::math::vector::Vector;
  27 +
  28 +use super::point::Point;
  29 +
  30 +#[derive(Debug, Clone)]
  31 +pub struct Face<T>
  32 +where T: Add + Sub + Neg + Mul + Div + Copy + Trig {
  33 + corners :Vec<usize>,
  34 + normal :Option<Vector<T>>,
  35 +}
  36 +
  37 +impl<'a, T> Face<T>
  38 +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
  39 + + Mul<Output = T> + Div<Output = T>
  40 + + PartialEq + Debug + Copy + Trig + From<i32> {
  41 + pub fn new(corners :Vec<usize>, ps :&[Point<T>]) -> Self {
  42 + let mut f = Face{ corners: corners, normal: None };
  43 + f.update_normal(ps);
  44 + f
  45 + }
  46 +
  47 + #[inline]
  48 + pub fn corners(&self) -> &[usize] {
  49 + &self.corners
  50 + }
  51 +
  52 + #[inline]
  53 + pub fn normal(&self) -> Option<&Vector<T>> {
  54 + (&self.normal).as_ref()
  55 + }
  56 +
  57 + pub fn update_normal(&mut self, ps :&[Point<T>]) {
  58 + let edge10 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[0]]).into();
  59 + let edge12 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[2]]).into();
  60 + self.normal = Some(edge10 * edge12);
  61 + }
  62 +}
  1 +//
  2 +// Basic geometric things...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
  24 +
  25 +use crate::math::transform::{TMatrix, Transformable};
  26 +use crate::math::trigonometry::Trig;
  27 +use crate::math::vector::Vector;
  28 +
  29 +pub struct DirectLight<T>
  30 +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
  31 + direction: Vector<T>,
  32 +}
  33 +
  34 +impl<T> DirectLight<T>
  35 +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
  36 + + Mul<Output = T> + Div<Output = T>
  37 + + Debug + Copy + Trig + From<i32> {
  38 + pub fn new(v :Vector<T>) -> Self {
  39 + DirectLight{ direction: v }
  40 + }
  41 +
  42 + pub fn transform(&self, m :&TMatrix<T>) -> Self {
  43 + let DirectLight{ direction: v } = self;
  44 + DirectLight { direction: v.transform(m) }
  45 + }
  46 +
  47 + pub fn dir(&self) -> Vector<T> {
  48 + self.direction
  49 + }
  50 +}
  1 +//
  2 +// This holds 3D primitives.
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2020 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +pub mod face;
  23 +pub mod point;
  24 +pub mod polyeder;
  25 +pub mod primitives;
  26 +pub mod camera;
  27 +pub mod light;
@@ -18,22 +18,13 @@ @@ -18,22 +18,13 @@
18 // You should have received a copy of the GNU General Public License 18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>. 19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // 20 //
21 -use std::convert::{From, Into};  
22 -use std::ops::{Add,Sub,Neg,Mul,Div};  
23 -use std::fmt::Debug;  
24 21
25 -use crate::easel::canvas::{Canvas, Vertex};  
26 -use crate::easel::polygon::Polygon;  
27 -use crate::transform::{TMatrix, Transformable};  
28 -use crate::trigonometry::Trig;  
29 -use crate::vector::Vector; 22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
30 24
31 -#[derive(Debug, Clone)]  
32 -pub struct Face<T>  
33 -where T: Add + Sub + Neg + Mul + Div + Copy + Trig {  
34 - corners :Vec<usize>,  
35 - normal :Option<Vector<T>>,  
36 -} 25 +use crate::math::transform::{TMatrix, Transformable};
  26 +use crate::math::trigonometry::Trig;
  27 +use crate::math::vector::Vector;
37 28
38 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 29 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
39 pub struct Point<T>(pub Vector<T>, T) 30 pub struct Point<T>(pub Vector<T>, T)
@@ -137,237 +128,3 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> @@ -137,237 +128,3 @@ where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
137 } 128 }
138 } 129 }
139 } 130 }
140 -  
141 -#[derive(Debug)]  
142 -pub struct Polyeder<T>  
143 -where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig {  
144 - points :Vec<Point<T>>,  
145 - faces :Vec<Face<T>>,  
146 -}  
147 -  
148 -pub trait Primitives<T>  
149 -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {  
150 - fn transform(&self, m :&TMatrix<T>) -> Self;  
151 - fn project( &self  
152 - , camera :&Camera<T>  
153 - , light :&DirectLight<T>  
154 - , col :u32 ) -> Vec<(Polygon<T>, u32)>;  
155 -}  
156 -  
157 -#[derive(Debug, Clone, Copy)]  
158 -pub struct Camera<T>  
159 -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {  
160 - width :T,  
161 - height :T,  
162 - distance :T,  
163 - project :TMatrix<T>,  
164 -}  
165 -  
166 -pub struct DirectLight<T>  
167 -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {  
168 - direction: Vector<T>,  
169 -}  
170 -  
171 -impl<T> Camera<T>  
172 -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>  
173 - + Mul<Output = T> + Div<Output = T>  
174 - + PartialEq + Debug + Copy + Trig + From<i32> {  
175 - // This code assumes that the size of the viewport is always  
176 - // equal to the size of the physical screen… e.g. window/canvas thus some  
177 - // effects can't be done. See book for examples with different viewport  
178 - // and screen sizes.  
179 - pub fn new(c :&dyn Canvas<T>, angle :i32) -> Self {  
180 - let width :T = (c.width() as i32).into();  
181 - let height :T = (c.height() as i32).into();  
182 - let d :T = 1.into();  
183 - let fov = T::cot(angle) * width;  
184 - let wh = width / 2.into();  
185 - let hh = height / 2.into();  
186 -  
187 - Camera { width: width  
188 - , height: height  
189 - , distance: d  
190 - , project: TMatrix::new(  
191 - ( fov, 0.into(), wh, 0.into())  
192 - , (0.into(), fov, hh, 0.into())  
193 - , (0.into(), 0.into(), d, 1.into())  
194 - , (0.into(), 0.into(), 1.into(), 0.into()) ) }  
195 - }  
196 -  
197 - pub fn get_distance(&self) -> T {  
198 - self.distance  
199 - }  
200 -  
201 - pub fn get_projection(&self) -> TMatrix<T> {  
202 - self.project  
203 - }  
204 -  
205 - pub fn project(&self, p :Point<T>) -> Point<T> {  
206 - p.transform(&self.project)  
207 - }  
208 -}  
209 -  
210 -impl<T> DirectLight<T>  
211 -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>  
212 - + Mul<Output = T> + Div<Output = T>  
213 - + Debug + Copy + Trig + From<i32> {  
214 - pub fn new(v :Vector<T>) -> Self {  
215 - DirectLight{ direction: v }  
216 - }  
217 -  
218 - pub fn dir(&self) -> Vector<T> {  
219 - self.direction  
220 - }  
221 -}  
222 -  
223 -impl<T> Face<T>  
224 -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>  
225 - + Mul<Output = T> + Div<Output = T>  
226 - + PartialEq + Debug + Copy + Trig + From<i32> {  
227 - fn new(corners :Vec<usize>, ps :&[Point<T>]) -> Self {  
228 - let mut f = Face{ corners: corners, normal: None };  
229 - f.update_normal(ps);  
230 - f  
231 - }  
232 -  
233 - fn update_normal(&mut self, ps :&[Point<T>]) {  
234 - let edge10 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[0]]).into();  
235 - let edge12 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[2]]).into();  
236 - self.normal = Some(edge10 * edge12);  
237 - }  
238 -}  
239 -  
240 -impl<T> Polyeder<T>  
241 -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>  
242 - + Mul<Output = T> + Div<Output = T>  
243 - + PartialEq + Debug + Copy + Trig + From<i32> {  
244 - fn update_normals(&mut self) {  
245 - for f in self.faces.iter_mut() {  
246 - f.update_normal(&self.points);  
247 - }  
248 - }  
249 -  
250 - // construct via cube, see polyhedra.pdf  
251 - pub fn tetrahedron(a :T) -> Polyeder<T> {  
252 - let f2 :T = 2.into();  
253 - let ch = a / (f2 * T::sqrt(f2).unwrap());  
254 -  
255 - let ps = vec!( Point::new(-ch, -ch, ch)  
256 - , Point::new(-ch, ch, -ch)  
257 - , Point::new( ch, -ch, -ch)  
258 - , Point::new( ch, ch, ch) );  
259 -  
260 - let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom  
261 - , Face::new(vec!(3, 2, 0), &ps)  
262 - , Face::new(vec!(0, 1, 3), &ps)  
263 - , Face::new(vec!(1, 2, 3), &ps) );  
264 -  
265 - Polyeder{ points: ps, faces: fs }  
266 - }  
267 -  
268 - pub fn triangle(a :T) -> Polyeder<T> {  
269 - let f0 :T = 0.into();  
270 - let f3 :T = 3.into();  
271 - let f6 :T = 6.into();  
272 - let zi :T = T::sqrt(f3).unwrap() / f6 * a;  
273 - let zc :T = T::sqrt(f3).unwrap() / f3 * a;  
274 - let ah :T = a / 2.into();  
275 -  
276 - let ps = vec!( Point::new(-ah, f0, -zi)  
277 - , Point::new( f0, f0, zc)  
278 - , Point::new( ah, f0, -zi) );  
279 -  
280 - let fs = vec!(Face::new(vec!(0, 1, 2), &ps));  
281 -  
282 - Polyeder{ points: ps, faces: fs }  
283 - }  
284 -  
285 - pub fn cube(a :T) -> Polyeder<T> {  
286 - let ah :T = a / From::<i32>::from(2);  
287 -  
288 - let ps = vec!( Point::new(-ah, ah, -ah) // 0 => front 1  
289 - , Point::new(-ah, -ah, -ah) // 1 => front 2  
290 - , Point::new( ah, -ah, -ah) // 2 => front 3  
291 - , Point::new( ah, ah, -ah) // 3 => front 4  
292 - , Point::new(-ah, ah, ah) // 4 => back 1  
293 - , Point::new(-ah, -ah, ah) // 5 => back 2  
294 - , Point::new( ah, -ah, ah) // 6 => back 3  
295 - , Point::new( ah, ah, ah) ); // 7 => back 4  
296 -  
297 - let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front  
298 - , Face::new(vec!(7, 6, 5, 4), &ps) // back  
299 - , Face::new(vec!(1, 5, 6, 2), &ps) // top  
300 - , Face::new(vec!(0, 3, 7, 4), &ps) // bottom  
301 - , Face::new(vec!(0, 4, 5, 1), &ps) // left  
302 - , Face::new(vec!(2, 6, 7, 3), &ps) ); // right  
303 -  
304 - Polyeder{ points: ps, faces: fs }  
305 - }  
306 -}  
307 -  
308 -impl<T> Primitives<T> for Polyeder<T>  
309 -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>  
310 - + Mul<Output = T> + Div<Output = T>  
311 - + Debug + Copy + Trig + From<i32> + PartialOrd {  
312 - // TODO Maybe this should also be an instance of Transformable…  
313 - fn transform(&self, m :&TMatrix<T>) -> Self {  
314 - let Polyeder{ points: ps, faces: fs } = self;  
315 -  
316 - let mut p = Polyeder{  
317 - points: ps.iter().map(|p| p.transform(m)).collect()  
318 - , faces: fs.to_vec()  
319 - };  
320 -  
321 - // TODO alternatively we could rotate the normals too, but this cannot  
322 - // done with the original matrix… the question is, what is faster.  
323 - p.update_normals();  
324 - p  
325 - }  
326 -  
327 - fn project( &self  
328 - , camera :&Camera<T>  
329 - , light :&DirectLight<T>  
330 - , color :u32 ) -> Vec<(Polygon<T>, u32)> {  
331 - // Helper to create a Polygon from Coordinates…  
332 - // TODO probably there needs to be a Polygon constructor for this.  
333 - fn polygon<I, T>(c :I) -> Polygon<T>  
334 - where I: Iterator<Item = Vertex<T>> {  
335 - Polygon(c.collect())  
336 - }  
337 -  
338 - // this one does the projection... as the projection was the last  
339 - // matrix we do not need to do it here.  
340 - let to_coord = |p :&usize| {  
341 - let Point(v, _) = camera.project(self.points[*p]);  
342 - Vertex::new(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into())  
343 - };  
344 - let to_poly = |f :&Face<T>| {  
345 - let pg = polygon(f.corners.iter().map(to_coord));  
346 - let mut r :T = (((color >> 16) & 0xFF) as i32).into();  
347 - let mut g :T = (((color >> 8) & 0xFF) as i32).into();  
348 - let mut b :T = (((color ) & 0xFF) as i32).into();  
349 - let lf :T = match f.normal {  
350 - None => 1.into(),  
351 - Some(n) => n.dot(light.dir())  
352 - / (n.mag() * light.dir().mag()),  
353 - };  
354 -  
355 - // this "if" represents a first simple backface culling  
356 - // approach. We only return face that face towards us.  
357 - if lf < 0.into() {  
358 - r = r * -lf;  
359 - g = g * -lf;  
360 - b = b * -lf;  
361 -  
362 - let c :u32 = (r.round() as u32) << 16  
363 - | (g.round() as u32) << 8  
364 - | (b.round() as u32);  
365 -  
366 - Some((pg, c))  
367 - } else {  
368 - None  
369 - }};  
370 -  
371 - self.faces.iter().filter_map(to_poly).collect()  
372 - }  
373 -}  
  1 +//
  2 +// Basic geometric things...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
  24 +
  25 +use crate::easel::polygon::Polygon;
  26 +use crate::easel::canvas::Vertex;
  27 +
  28 +use crate::math::transform::{TMatrix, Transformable};
  29 +use crate::math::trigonometry::Trig;
  30 +use crate::math::vector::Vector;
  31 +
  32 +use super::camera::Camera;
  33 +use super::face::Face;
  34 +use super::light::DirectLight;
  35 +use super::point::Point;
  36 +use super::primitives::Primitives;
  37 +
  38 +#[derive(Debug)]
  39 +pub struct Polyeder<T>
  40 +where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig {
  41 + points :Vec<Point<T>>,
  42 + faces :Vec<Face<T>>,
  43 +}
  44 +
  45 +impl<T> Polyeder<T>
  46 +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
  47 + + Mul<Output = T> + Div<Output = T>
  48 + + PartialEq + Debug + Copy + Trig + From<i32> {
  49 + fn update_normals(&mut self) {
  50 + for f in self.faces.iter_mut() {
  51 + f.update_normal(&self.points);
  52 + }
  53 + }
  54 +
  55 + // construct via cube, see polyhedra.pdf
  56 + pub fn tetrahedron(a :T) -> Polyeder<T> {
  57 + let f2 :T = 2.into();
  58 + let ch = a / (f2 * T::sqrt(f2).unwrap());
  59 +
  60 + let ps = vec!( Point::new(-ch, -ch, ch)
  61 + , Point::new(-ch, ch, -ch)
  62 + , Point::new( ch, -ch, -ch)
  63 + , Point::new( ch, ch, ch) );
  64 +
  65 + let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom
  66 + , Face::new(vec!(3, 2, 0), &ps)
  67 + , Face::new(vec!(0, 1, 3), &ps)
  68 + , Face::new(vec!(1, 2, 3), &ps) );
  69 +
  70 + Polyeder{ points: ps, faces: fs }
  71 + }
  72 +
  73 + pub fn triangle(a :T) -> Polyeder<T> {
  74 + let f0 :T = 0.into();
  75 + let f3 :T = 3.into();
  76 + let f6 :T = 6.into();
  77 + let zi :T = T::sqrt(f3).unwrap() / f6 * a;
  78 + let zc :T = T::sqrt(f3).unwrap() / f3 * a;
  79 + let ah :T = a / 2.into();
  80 +
  81 + let ps = vec!( Point::new(-ah, f0, -zi)
  82 + , Point::new( f0, f0, zc)
  83 + , Point::new( ah, f0, -zi) );
  84 +
  85 + let fs = vec!(Face::new(vec!(0, 1, 2), &ps));
  86 +
  87 + Polyeder{ points: ps, faces: fs }
  88 + }
  89 +
  90 + pub fn cube(a :T) -> Polyeder<T> {
  91 + let ah :T = a / From::<i32>::from(2);
  92 +
  93 + let ps = vec!( Point::new(-ah, ah, -ah) // 0 => front 1
  94 + , Point::new(-ah, -ah, -ah) // 1 => front 2
  95 + , Point::new( ah, -ah, -ah) // 2 => front 3
  96 + , Point::new( ah, ah, -ah) // 3 => front 4
  97 + , Point::new(-ah, ah, ah) // 4 => back 1
  98 + , Point::new(-ah, -ah, ah) // 5 => back 2
  99 + , Point::new( ah, -ah, ah) // 6 => back 3
  100 + , Point::new( ah, ah, ah) ); // 7 => back 4
  101 +
  102 + let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front
  103 + , Face::new(vec!(7, 6, 5, 4), &ps) // back
  104 + , Face::new(vec!(1, 5, 6, 2), &ps) // top
  105 + , Face::new(vec!(0, 3, 7, 4), &ps) // bottom
  106 + , Face::new(vec!(0, 4, 5, 1), &ps) // left
  107 + , Face::new(vec!(2, 6, 7, 3), &ps) ); // right
  108 +
  109 + Polyeder{ points: ps, faces: fs }
  110 + }
  111 +}
  112 +
  113 +impl<T> Primitives<T> for Polyeder<T>
  114 +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
  115 + + Mul<Output = T> + Div<Output = T>
  116 + + Debug + Copy + Trig + From<i32> + From<f64> + PartialOrd {
  117 + // TODO Maybe this should also be an instance of Transformable…
  118 + fn transform(&self, m :&TMatrix<T>) -> Self {
  119 + let Polyeder{ points: ps, faces: fs } = self;
  120 +
  121 + let mut p = Polyeder{
  122 + points: ps.iter().map(|p| p.transform(m)).collect()
  123 + , faces: fs.to_vec()
  124 + };
  125 +
  126 + // TODO alternatively we could rotate the normals too, but this cannot
  127 + // done with the original matrix… the question is, what is faster.
  128 + p.update_normals();
  129 + p
  130 + }
  131 +
  132 + fn project( &self
  133 + , camera :&Camera<T>
  134 + , light :&DirectLight<T>
  135 + , color :u32 ) -> Vec<(Polygon<T>, u32)> {
  136 + // Helper to create a Polygon from Coordinates…
  137 + // TODO probably there needs to be a Polygon constructor for this.
  138 + fn polygon<I, T>(c :I) -> Polygon<T>
  139 + where I: Iterator<Item = Vertex<T>> {
  140 + Polygon(c.collect())
  141 + }
  142 +
  143 + // currently our cam has only one direction...
  144 + let cam_dir = Vector(0.into(), 0.into(), 1.into());
  145 +
  146 + // this one does the projection... as the projection was the last
  147 + // matrix we do not need to do it here.
  148 + let to_coord = |p :&usize| camera.project(self.points[*p]);
  149 + let to_poly = |f :&Face<T>| {
  150 + let pg = polygon(f.corners().iter().map(to_coord));
  151 + let mut r :T = (((color >> 16) & 0xFF) as i32).into();
  152 + let mut g :T = (((color >> 8) & 0xFF) as i32).into();
  153 + let mut b :T = (((color ) & 0xFF) as i32).into();
  154 + let lf :T = match f.normal() {
  155 + None => 1.into(),
  156 + Some(n) => n.dot(light.dir())
  157 + / (n.mag() * light.dir().mag()),
  158 + };
  159 + let view_f :T = match f.normal() {
  160 + None => 1.into(),
  161 + Some(n) => n.dot(cam_dir)
  162 + / (n.mag() * cam_dir.mag()),
  163 + };
  164 +
  165 + // this "if" represents a first simple backface culling
  166 + // approach. We only return face that face towards us.
  167 + if view_f >= 0.into() {
  168 + None
  169 + } else {
  170 + if lf < (-0.1).into() {
  171 + r = r * -lf;
  172 + g = g * -lf;
  173 + b = b * -lf;
  174 + } else {
  175 + r = r * 0.1.into();
  176 + g = g * 0.1.into();
  177 + b = b * 0.1.into();
  178 + }
  179 +
  180 + let c :u32 = (r.round() as u32) << 16
  181 + | (g.round() as u32) << 8
  182 + | (b.round() as u32);
  183 +
  184 + Some((pg, c))
  185 + }};
  186 +
  187 + self.faces.iter().filter_map(to_poly).collect()
  188 + }
  189 +}
  1 +//
  2 +// Basic geometric things...
  3 +//
  4 +// Georg Hopp <georg@steffers.org>
  5 +//
  6 +// Copyright © 2019 Georg Hopp
  7 +//
  8 +// This program is free software: you can redistribute it and/or modify
  9 +// it under the terms of the GNU General Public License as published by
  10 +// the Free Software Foundation, either version 3 of the License, or
  11 +// (at your option) any later version.
  12 +//
  13 +// This program is distributed in the hope that it will be useful,
  14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 +// GNU General Public License for more details.
  17 +//
  18 +// You should have received a copy of the GNU General Public License
  19 +// along with this program. If not, see <http://www.gnu.org/licenses/>.
  20 +//
  21 +
  22 +use std::fmt::Debug;
  23 +use std::ops::{Add, Div, Mul, Neg, Sub};
  24 +
  25 +use crate::easel::polygon::Polygon;
  26 +use crate::math::transform::TMatrix;
  27 +use crate::math::trigonometry::Trig;
  28 +
  29 +use super::camera::Camera;
  30 +use super::light::DirectLight;
  31 +
  32 +pub trait Primitives<T>
  33 +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
  34 + fn transform(&self, m :&TMatrix<T>) -> Self;
  35 + fn project( &self
  36 + , camera :&Camera<T>
  37 + , light :&DirectLight<T>
  38 + , col :u32 ) -> Vec<(Polygon<T>, u32)>;
  39 +}
Please register or login to post a comment