Commit e78ff6b6132e602aa67ff751a0360d4083b00ea8
1 parent
e4b19add
reduce to easel3d-xcb demo program
Showing
56 changed files
with
163 additions
and
4581 deletions
Too many changes to show.
To preserve performance only 56 of 56+ files are displayed.
1 | 1 | [package] |
2 | -name = "xcb-test" | |
2 | +name = "easel3d-xcb" | |
3 | 3 | version = "0.1.0" |
4 | 4 | authors = ["Georg Hopp <georg@steffers.org>"] |
5 | 5 | edition = "2018" |
6 | 6 | |
7 | 7 | [dependencies] |
8 | +easel3d = { path = "../easel3d" } | |
8 | 9 | libc = "0.2" |
9 | -gl = "0.5.2" | |
10 | -x11 = { version = "2.3", features = ["glx"] } | |
11 | 10 | xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] } | ... | ... |
1 | -# Rust playground | |
1 | +# Easel3D-XCB | |
2 | 2 | |
3 | -Things I have recently done while learning the Rust programming language. | |
3 | +X windows demo application using Easel3D... | |
4 | 4 | |
5 | 5 | ## Synopsis |
6 | 6 | |
7 | -Change in one of the toplevel subdirectories and try `cargo build` or | |
8 | -`cargo run`. Maybe not everything is working oob. Feel free to fix whatever | |
9 | -you want. | |
7 | +Checkout this repository as well as the _easel3d_ repository to the same | |
8 | +destination directory. | |
10 | 9 | |
11 | -## Description | |
12 | - | |
13 | -Various small examples I have tried while learning rust. The biggest and | |
14 | -currently most active project is **fractional** which started as an | |
15 | -implamentation of a rational number data type and then switched to a 3D | |
16 | -math playground visualizing using **XCB** (in future it might also use | |
17 | -a **HTML5 Canvas** for drawing as WebAssembly application. | |
18 | -Using fractions with 3D math has several drawbacks: | |
10 | +In the root of this repository call `cargo run`. | |
19 | 11 | |
20 | -1. A huge part of 3D math is non rational, like sin, cos, tan and sqrt. | |
21 | -2. The numerator and denominator tend to become very huge while nearing to non | |
22 | - rational numbers and reduction is difficult and time consuming. | |
23 | -3. Because of 2 it is way slower than the floating point calculation (at least | |
24 | - with a decent coprocessor). | |
12 | +## Description | |
25 | 13 | |
26 | -Anyway, implementing the vector math stuff for both fractions and floating | |
27 | -point was a nice playground for generics and traits. In future I might add | |
28 | -another data type which implements the math as done by David Braben for the | |
29 | -elite computer game. | |
14 | +A demo application using easel3d to draw in an X-Window. | |
30 | 15 | |
31 | 16 | ## Requirements |
32 | 17 | |
33 | -### Always | |
34 | - | |
35 | 18 | - A recent version of the Rust programming language as well as tooling. |
36 | 19 | Currently I use Rust 1.39.0. |
20 | +- X11 (xcb, x11-shm) | |
37 | 21 | |
38 | -### For fractional | |
22 | +## Dependencies | |
39 | 23 | |
40 | -- A running X Server with **XCB** and **X11-SHM** extentions | |
24 | +### Rust crates. | |
41 | 25 | |
42 | -## Dependencies | |
26 | +- easel3d (from parent directory) | |
27 | +- libc >=0.2 | |
28 | +- xcb >=0.8 | |
43 | 29 | |
44 | -... | |
30 | +Along with the dependencies of the xcb crate. `cargo build` should take care | |
31 | +of having them available. | |
45 | 32 | |
46 | 33 | ## Contributing |
47 | 34 | ... | ... |
branches/Cargo.lock
deleted
100644 → 0
branches/Cargo.toml
deleted
100644 → 0
branches/src/main.rs
deleted
100644 → 0
1 | -// | |
2 | -// Control Flow Examples | |
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 | -fn main() { | |
23 | - let condition = true; | |
24 | - let number = if condition { | |
25 | - 5 | |
26 | - } else { | |
27 | - 6 | |
28 | - }; | |
29 | - | |
30 | - println!("The value of number is: {}", number); | |
31 | -} |
fractional/.swp
deleted
100644 → 0
No preview for this file type
fractional/Cargo.toml
deleted
100644 → 0
1 | -[package] | |
2 | -name = "fractional" | |
3 | -version = "0.1.0" | |
4 | -authors = ["Georg Hopp <georg@steffers.org>"] | |
5 | -edition = "2018" | |
6 | - | |
7 | -[dependencies] | |
8 | -lazy_static = "1.4.0" | |
9 | -libc = "0.2" | |
10 | -gl = "0.5.2" | |
11 | -x11 = { version = "2.3", features = ["glx"] } | |
12 | -xcb = { version = "0.8", features = ["dri2", "randr", "thread", "xlib_xcb", "shm"] } |
fractional/notes/09e-textures.pdf
deleted
100644 → 0
No preview for this file type
fractional/notes/frontier-map.url
deleted
100644 → 0
1 | -http://www.jongware.com/galaxy1.html |
fractional/notes/html5
deleted
100644 → 0
1 | -I really would like to see a HTML5 canvas as canvas for the 3D code. | |
2 | - | |
3 | -Some URLs: | |
4 | -- https://stackoverflow.com/questions/42806037/modify-canvas-from-wasm | |
5 | -- https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.HtmlCanvasElement.html | |
6 | -- https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.CanvasRenderingContext2d.html | |
7 | -- https://stackoverflow.com/questions/49935207/editing-canvas-pixel-data-in-webassembly-rust |
fractional/notes/math.url
deleted
100644 → 0
1 | -https://www.themathpage.com/Alg/reciprocals.htm |
fractional/notes/polyhedra.pdf
deleted
100644 → 0
No preview for this file type
fractional/notes/texmap.pdf
deleted
100644 → 0
No preview for this file type
fractional/notes/texture-mapping.url
deleted
100644 → 0
1 | -https://en.wikipedia.org/wiki/Texture_mapping | |
2 | -http://www.gamers.org/dEngine/quake/papers/checker_texmap.html | |
3 | -https://www.cs.uic.edu/~jbell/CourseNotes/ComputerGraphics/TextureMapping.html | |
4 | -http://www.decew.net/OSS/References/chapter_2_texture_mapping.pdf | |
5 | - | |
6 | -# example affine texture mapping | |
7 | -http://archive.gamedev.net/archive/reference/articles/article852.html | |
8 | - | |
9 | - | |
10 | -http://www.lysator.liu.se/~mikaelk/doc/perspectivetexture/ | |
11 | - | |
12 | -# Shader... This also describes z-Buffer... :) | |
13 | -https://people.ece.cornell.edu/land/OldStudentProjects/cs490-95to96/GUO/report.html | |
14 | -https://software.intel.com/en-us/articles/the-basics-of-the-art-of-lighting-part-3-lighting-and-shaders/ | |
15 | -https://docs.unity3d.com/Manual/Lighting.html |
fractional/src/continuous.rs
deleted
100644 → 0
1 | -// | |
2 | -// A «continued fraction» is a representation of a fraction as a vector | |
3 | -// of integrals… Irrational fractions will result in infinite most of the | |
4 | -// time repetitive vectors. They can be used to get a resonable approximation | |
5 | -// for sqrt on fractionals. | |
6 | -// | |
7 | -// Georg Hopp <georg@steffers.org> | |
8 | -// | |
9 | -// Copyright © 2019 Georg Hopp | |
10 | -// | |
11 | -// This program is free software: you can redistribute it and/or modify | |
12 | -// it under the terms of the GNU General Public License as published by | |
13 | -// the Free Software Foundation, either version 3 of the License, or | |
14 | -// (at your option) any later version. | |
15 | -// | |
16 | -// This program is distributed in the hope that it will be useful, | |
17 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | -// GNU General Public License for more details. | |
20 | -// | |
21 | -// You should have received a copy of the GNU General Public License | |
22 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
23 | -// | |
24 | -use crate::Fractional; | |
25 | - | |
26 | -#[derive(Debug)] | |
27 | -pub struct Continuous (Vec<i64>); | |
28 | - | |
29 | -impl Continuous { | |
30 | - // calculate a sqrt as continued fraction sequence. Taken from: | |
31 | - // https://en.wikipedia.org/wiki/Methods_of_computing_square_roots# | |
32 | - // Continued_fraction_expansion | |
33 | - pub fn sqrt(x :i64, a0 :i64) -> Self { | |
34 | - fn inner(v :&mut [i64], x :i64, a0 :i64, mn :i64, dn :i64, an :i64) { | |
35 | - let mn_1 = dn * an - mn; | |
36 | - let dn_1 = (x - mn_1 * mn_1) / dn; | |
37 | - let an_1 = (a0 + mn_1) / dn_1; | |
38 | - | |
39 | - v[0] = an; | |
40 | - // The convergence criteria „an_1 == 2 * a0“ is not good for | |
41 | - // very small x thus I decided to break the iteration at constant | |
42 | - // time. Which is the 5 below. | |
43 | - if v.len() > 1 { | |
44 | - inner(&mut v[1..], x, a0, mn_1, dn_1, an_1); | |
45 | - } | |
46 | - } | |
47 | - | |
48 | - let mut v :Vec<i64> = vec!(0; 5); | |
49 | - inner(&mut v, x, a0, 0, 1, a0); | |
50 | - Continuous(v) | |
51 | - } | |
52 | - | |
53 | - // general continous fraction form of a fractional... | |
54 | - pub fn from_prec(f :&Fractional, prec :Option<usize>) -> Self { | |
55 | - fn inner(v :&mut Vec<i64>, f :Fractional, prec :Option<usize>) { | |
56 | - let mut process = |prec :Option<usize>| { | |
57 | - let Fractional(n, d) = f; | |
58 | - let a = n / d; | |
59 | - let Fractional(_n, _d) = f.noreduce_sub(a.into()); | |
60 | - | |
61 | - v.push(a); | |
62 | - match _n { | |
63 | - 1 => v.push(_d), | |
64 | - 0 => {}, | |
65 | - _ => inner(v, Fractional(_d, _n), prec), | |
66 | - } | |
67 | - }; | |
68 | - | |
69 | - match prec { | |
70 | - Some(0) => {}, | |
71 | - None => process(None), | |
72 | - Some(p) => process(Some(p - 1)), | |
73 | - } | |
74 | - } | |
75 | - | |
76 | - let mut v = match prec { | |
77 | - None => Vec::with_capacity(100), | |
78 | - Some(p) => Vec::with_capacity(p + 1), | |
79 | - }; | |
80 | - | |
81 | - inner(&mut v, *f, prec); | |
82 | - Continuous(v) | |
83 | - } | |
84 | - | |
85 | - pub fn into_prec(&self, prec :Option<usize>) -> Fractional { | |
86 | - let Continuous(c) = self; | |
87 | - let p = match prec { | |
88 | - Some(p) => if p <= c.len() { p } else { c.len() }, | |
89 | - None => c.len(), | |
90 | - }; | |
91 | - | |
92 | - let to_frac = |acc :Fractional, x :&i64| { | |
93 | - let Fractional(an, ad) = acc.noreduce_add((*x).into()); | |
94 | - Fractional(ad, an) | |
95 | - }; | |
96 | - | |
97 | - let Fractional(n, d) = c[..p] | |
98 | - . into_iter() | |
99 | - . rev() | |
100 | - . fold(Fractional(0, 1), to_frac); | |
101 | - Fractional(d, n) | |
102 | - } | |
103 | -} | |
104 | - | |
105 | -impl From<&Fractional> for Continuous { | |
106 | - fn from(x :&Fractional) -> Self { | |
107 | - Self::from_prec(x, None) | |
108 | - } | |
109 | -} | |
110 | - | |
111 | -impl Into<Fractional> for &Continuous { | |
112 | - fn into(self) -> Fractional { | |
113 | - (&self).into_prec(None) | |
114 | - } | |
115 | -} |
fractional/src/easel.rs
deleted
100644 → 0
1 | -// | |
2 | -// This is an abstraction over a drawing environment. | |
3 | -// Future note: z-Buffer is described here: | |
4 | -// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes | |
5 | -// | |
6 | -// Georg Hopp <georg@steffers.org> | |
7 | -// | |
8 | -// Copyright © 2019 Georg Hopp | |
9 | -// | |
10 | -// This program is free software: you can redistribute it and/or modify | |
11 | -// it under the terms of the GNU General Public License as published by | |
12 | -// the Free Software Foundation, either version 3 of the License, or | |
13 | -// (at your option) any later version. | |
14 | -// | |
15 | -// This program is distributed in the hope that it will be useful, | |
16 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | -// GNU General Public License for more details. | |
19 | -// | |
20 | -// You should have received a copy of the GNU General Public License | |
21 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | -// | |
23 | -use std::cmp; | |
24 | -use std::fmt::{Formatter, Debug, Display, Result}; | |
25 | -use std::ops::{Add, Sub, Div}; | |
26 | -use std::sync::mpsc; | |
27 | - | |
28 | -pub trait Easel { | |
29 | - //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; | |
30 | -} | |
31 | - | |
32 | -pub trait Canvas<T> { | |
33 | - fn init_events(&self); | |
34 | - fn start_events(&self, tx :mpsc::Sender<i32>); | |
35 | - | |
36 | - fn width(&self) -> u16; | |
37 | - fn height(&self) -> u16; | |
38 | - | |
39 | - fn clear(&mut self); | |
40 | - fn draw(&mut self, c :&dyn Drawable<T>, ofs :Coordinate<T>, color :u32); | |
41 | - fn put_text(&self, ofs :Coordinate<T>, s :&str); | |
42 | - fn set_pixel(&mut self, c :Coordinate<T>, color :u32); | |
43 | - fn show(&self); | |
44 | -} | |
45 | - | |
46 | -pub trait Drawable<T> { | |
47 | - fn plot(&self) -> Coordinates<T>; | |
48 | -} | |
49 | - | |
50 | -pub trait Fillable<T> | |
51 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
52 | - + Debug + Copy + From<i32> { | |
53 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32); | |
54 | -} | |
55 | - | |
56 | -#[derive(Debug, Clone, Copy)] | |
57 | -pub struct Coordinate<T>(pub i32, pub i32, pub T); | |
58 | - | |
59 | -#[derive(Debug, Clone)] | |
60 | -pub struct Coordinates<T>(pub Vec<Coordinate<T>>); | |
61 | - | |
62 | -#[derive(Debug, Clone, Copy)] | |
63 | -pub struct LineIterator<T> where T: Debug { | |
64 | - a :Option<Coordinate<T>> | |
65 | - , b :Coordinate<T> | |
66 | - , dx :i32 | |
67 | - , dy :i32 | |
68 | - , dz :T | |
69 | - , sx :i32 | |
70 | - , sy :i32 | |
71 | - , err :i32 | |
72 | - , only_edges :bool | |
73 | -} | |
74 | - | |
75 | -impl<T> Iterator for LineIterator<T> | |
76 | -where T: Add<Output = T> + Debug + Copy + From<i32> { | |
77 | - type Item = Coordinate<T>; | |
78 | - | |
79 | - fn next(&mut self) -> Option<Self::Item> { | |
80 | - match self.a { | |
81 | - None => None, | |
82 | - Some(a) => { | |
83 | - let Coordinate(ax, ay, az) = a; | |
84 | - let Coordinate(bx, by, _) = self.b; | |
85 | - | |
86 | - if ax != bx || ay != by { | |
87 | - match (2 * self.err >= self.dy, 2 * self.err <= self.dx ) { | |
88 | - (true, false) => { | |
89 | - let r = self.a; | |
90 | - self.a = Some(Coordinate( ax + self.sx | |
91 | - , ay | |
92 | - , az + self.dz )); | |
93 | - self.err = self.err + self.dy; | |
94 | - if self.only_edges { self.next() } else { r } | |
95 | - }, | |
96 | - (false, true) => { | |
97 | - let r = self.a; | |
98 | - self.a = Some(Coordinate( ax | |
99 | - , ay + self.sy | |
100 | - , az + self.dz )); | |
101 | - self.err = self.err + self.dx; | |
102 | - r | |
103 | - }, | |
104 | - _ => { | |
105 | - let r = self.a; | |
106 | - self.a = Some(Coordinate( ax + self.sx | |
107 | - , ay + self.sy | |
108 | - , az + self.dz )); | |
109 | - self.err = self.err + self.dx + self.dy; | |
110 | - r | |
111 | - }, | |
112 | - } | |
113 | - } else { | |
114 | - self.a = None; | |
115 | - Some(self.b) | |
116 | - } | |
117 | - } | |
118 | - } | |
119 | - } | |
120 | -} | |
121 | - | |
122 | -impl<T> Coordinate<T> | |
123 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
124 | - + Debug + Clone + Copy + From<i32> { | |
125 | - fn iter(self, b :&Self, only_edges :bool) -> LineIterator<T> { | |
126 | - let Coordinate(ax, ay, az) = self; | |
127 | - let Coordinate(bx, by, bz) = *b; | |
128 | - | |
129 | - let dx = (bx - ax).abs(); | |
130 | - let dy = -(by - ay).abs(); | |
131 | - | |
132 | - LineIterator { a: Some(self) | |
133 | - , b: *b | |
134 | - , dx: dx | |
135 | - , dy: dy | |
136 | - , dz: (bz - az) / cmp::max(dx, -dy).into() | |
137 | - , sx: if ax < bx { 1 } else { -1 } | |
138 | - , sy: if ay < by { 1 } else { -1 } | |
139 | - , err: dx + dy | |
140 | - , only_edges: only_edges | |
141 | - } | |
142 | - } | |
143 | - | |
144 | - fn line_iter(self, b :&Self) -> LineIterator<T> { | |
145 | - self.iter(b, false) | |
146 | - } | |
147 | - | |
148 | - fn line(self, b :&Self) -> Vec<Self> { | |
149 | - self.line_iter(b).collect() | |
150 | - } | |
151 | - | |
152 | - fn edge_iter(self, b :&Self) -> LineIterator<T> { | |
153 | - self.iter(b, true) | |
154 | - } | |
155 | - | |
156 | - fn edge(self, b :&Self) -> Vec<Self> { | |
157 | - self.edge_iter(b).collect() | |
158 | - } | |
159 | - | |
160 | - fn face(edges :&[Self]) -> Vec<Self> { | |
161 | - edges.to_vec() | |
162 | - } | |
163 | -} | |
164 | - | |
165 | -impl<T> Display for Coordinate<T> { | |
166 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
167 | - write!(f, "<{},{}>", self.0, self.1) | |
168 | - } | |
169 | -} | |
170 | - | |
171 | -impl<T> Display for Coordinates<T> where T: Copy { | |
172 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
173 | - let Coordinates(is) = self; | |
174 | - | |
175 | - let c = match is[..] { | |
176 | - [] => String::from(""), | |
177 | - [a] => format!("{}", a), | |
178 | - _ => { | |
179 | - let mut a = format!("{}", is[0]); | |
180 | - for i in is[1..].iter() { | |
181 | - a = a + &format!(",{}", i); | |
182 | - } | |
183 | - a | |
184 | - } | |
185 | - }; | |
186 | - | |
187 | - write!(f, "Coordinates[{}]", c) | |
188 | - } | |
189 | -} | |
190 | - | |
191 | - | |
192 | -#[derive(Debug, Clone, Copy)] | |
193 | -pub struct Point<T>(pub Coordinate<T>); | |
194 | - | |
195 | -impl<T> Drawable<T> for Point<T> where T: Copy { | |
196 | - fn plot(&self) -> Coordinates<T> { | |
197 | - let Point(c) = *self; | |
198 | - Coordinates(vec!(c)) | |
199 | - } | |
200 | -} | |
201 | - | |
202 | -impl<T> Display for Point<T> { | |
203 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
204 | - let Point(p) = self; | |
205 | - write!(f, "Point[{}]", p) | |
206 | - } | |
207 | -} | |
208 | - | |
209 | -#[derive(Debug, Clone, Copy)] | |
210 | -pub struct Line<T>(pub Coordinate<T>, pub Coordinate<T>); | |
211 | - | |
212 | -impl<T> Drawable<T> for Line<T> | |
213 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
214 | - + Debug + Clone + Copy + From<i32> { | |
215 | - fn plot(&self) -> Coordinates<T> { | |
216 | - let Line(a, b) = *self; | |
217 | - Coordinates(a.line(&b)) | |
218 | - } | |
219 | -} | |
220 | - | |
221 | -impl<T> Display for Line<T> { | |
222 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
223 | - let Line(a, b) = self; | |
224 | - write!(f, "Line[{},{}]", a, b) | |
225 | - } | |
226 | -} | |
227 | - | |
228 | -#[derive(Debug, Clone)] | |
229 | -pub struct Polyline<T>(pub Coordinates<T>); | |
230 | - | |
231 | -impl<T> Drawable<T> for Polyline<T> | |
232 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
233 | - + Debug + Clone + Copy + From<i32> { | |
234 | - fn plot(&self) -> Coordinates<T> { | |
235 | - let Polyline(Coordinates(cs)) = self; | |
236 | - | |
237 | - match cs[..] { | |
238 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | |
239 | - [a] => Coordinates(vec!(a)), | |
240 | - [a, b] => Coordinates(a.line(&b)), | |
241 | - _ => { | |
242 | - let (a, b) = (cs[0], cs[1]); | |
243 | - let mut r = a.line(&b); | |
244 | - let mut i = b; | |
245 | - for j in cs[2..].iter() { | |
246 | - r.append(&mut i.line(j)[1..].to_vec()); | |
247 | - i = *j; | |
248 | - } | |
249 | - Coordinates(r) | |
250 | - }, | |
251 | - } | |
252 | - } | |
253 | -} | |
254 | - | |
255 | -impl<T> Display for Polyline<T> where T: Copy { | |
256 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
257 | - let Polyline(a) = self; | |
258 | - write!(f, "PLine[{}]", a) | |
259 | - } | |
260 | -} | |
261 | - | |
262 | -#[derive(Debug, Clone, Copy)] | |
263 | -enum Direction { Left, Right } | |
264 | - | |
265 | -#[derive(Debug, Clone)] | |
266 | -pub struct Polygon<T>(pub Coordinates<T>); | |
267 | - | |
268 | -#[derive(Debug, Clone)] | |
269 | -enum VertexIteratorMode { Vertex, Edge } | |
270 | -#[derive(Debug, Clone)] | |
271 | -pub struct VertexIterator<'a,T> where T: Debug { | |
272 | - p :&'a Polygon<T>, | |
273 | - top :usize, | |
274 | - current :Option<usize>, | |
275 | - edge :Option<LineIterator<T>>, | |
276 | - mode :VertexIteratorMode, | |
277 | - direction :Direction, | |
278 | -} | |
279 | - | |
280 | -impl<'a,T> VertexIterator<'a,T> | |
281 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
282 | - + Debug + Copy + From<i32> { | |
283 | - fn edge(p :&'a Polygon<T>, direction :Direction) -> Self { | |
284 | - let top = p.vert_min(direction); | |
285 | - let next = p.next_y(top, direction); | |
286 | - let edge = match next { | |
287 | - None => None, | |
288 | - Some(next) => Some(p.vertex(top).edge_iter(&p.vertex(next))), | |
289 | - }; | |
290 | - | |
291 | - VertexIterator { p: p | |
292 | - , top: top | |
293 | - , current: next | |
294 | - , edge: edge | |
295 | - , mode: VertexIteratorMode::Edge | |
296 | - , direction: direction } | |
297 | - } | |
298 | - | |
299 | - fn vertex(p :&'a Polygon<T>, direction :Direction) -> Self { | |
300 | - let top = p.vert_min(direction); | |
301 | - let next = p.next_y(top, direction); | |
302 | - | |
303 | - VertexIterator { p: p | |
304 | - , top: top | |
305 | - , current: next | |
306 | - , edge: None | |
307 | - , mode: VertexIteratorMode::Vertex | |
308 | - , direction: direction } | |
309 | - } | |
310 | - | |
311 | - // if this yields "None" we are finished. | |
312 | - fn next_edge(&mut self) -> Option<LineIterator<T>> { | |
313 | - let current = self.current?; | |
314 | - let next = self.p.next_y(current, self.direction)?; | |
315 | - let mut edge = self.p.vertex(current).edge_iter(&self.p.vertex(next)); | |
316 | - | |
317 | - match edge.next() { | |
318 | - // It should be impossible that a new edge iterator has no values | |
319 | - // at all… anyway, just in case I handle it here. | |
320 | - None => self.next_edge(), | |
321 | - Some(_) => { | |
322 | - self.current = Some(next); | |
323 | - self.edge = Some(edge); | |
324 | - self.edge | |
325 | - }, | |
326 | - } | |
327 | - } | |
328 | -} | |
329 | - | |
330 | -impl<'a,T> Iterator for VertexIterator<'a,T> | |
331 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
332 | - + Debug + Copy + From<i32> { | |
333 | - type Item = Coordinate<T>; | |
334 | - | |
335 | - fn next(&mut self) -> Option<Self::Item> { | |
336 | - match self.mode { | |
337 | - VertexIteratorMode::Edge => { | |
338 | - // if for whatever reason edge is "None" finish this iterator. | |
339 | - let next = self.edge.as_mut()?.next(); | |
340 | - | |
341 | - match next { | |
342 | - Some(_) => next, | |
343 | - None => { | |
344 | - self.next_edge()?; | |
345 | - self.next() | |
346 | - }, | |
347 | - } | |
348 | - }, | |
349 | - VertexIteratorMode::Vertex => { | |
350 | - let current = self.current?; | |
351 | - self.current = self.p.next_y(current, self.direction); | |
352 | - Some(self.p.vertex(current)) | |
353 | - }, | |
354 | - } | |
355 | - } | |
356 | -} | |
357 | - | |
358 | -impl<T> Polygon<T> | |
359 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
360 | - + Copy + Debug + From<i32> { | |
361 | - #[inline] | |
362 | - fn vertex(&self, v :usize) -> Coordinate<T> { | |
363 | - let Polygon(Coordinates(cs)) = self; | |
364 | - cs[v] | |
365 | - } | |
366 | - | |
367 | - fn vert_min<'a>(&'a self, d :Direction) -> usize { | |
368 | - let Polygon(Coordinates(cs)) = self; | |
369 | - | |
370 | - type ICoord<'a,T> = (usize, &'a Coordinate<T>); | |
371 | - | |
372 | - // TODO I guess the problem here is that it does not account for the | |
373 | - // same y vertex on the beggining and the end. So i guess correct | |
374 | - // would be finding the first one and then dependings on the | |
375 | - // given direction either search left or right for same y's. | |
376 | - let fold = |acc :Option<ICoord<'a,T>>, x :ICoord<'a,T>| | |
377 | - match acc { | |
378 | - None => Some(x), | |
379 | - Some(a) => { | |
380 | - let Coordinate(_, ay, _) = a.1; | |
381 | - let Coordinate(_, xy, _) = x.1; | |
382 | - if xy < ay {Some(x)} else {Some(a)} | |
383 | - }, | |
384 | - }; | |
385 | - | |
386 | - let mut min = cs.iter().enumerate().fold(None, fold).unwrap().0; | |
387 | - let mut next = self.step(min, d); | |
388 | - | |
389 | - while self.vertex(min).1 == self.vertex(next).1 { | |
390 | - min = next; | |
391 | - next = self.step(min, d); | |
392 | - } | |
393 | - | |
394 | - min | |
395 | - } | |
396 | - | |
397 | - fn left_edge(&self) -> VertexIterator<T> { | |
398 | - VertexIterator::edge(self, Direction::Left) | |
399 | - } | |
400 | - | |
401 | - fn right_edge(&self) -> VertexIterator<T> { | |
402 | - VertexIterator::edge(self, Direction::Right) | |
403 | - } | |
404 | - | |
405 | - fn left_vertices(&self) -> VertexIterator<T> { | |
406 | - VertexIterator::vertex(self, Direction::Left) | |
407 | - } | |
408 | - | |
409 | - fn right_vertices(&self) -> VertexIterator<T> { | |
410 | - VertexIterator::vertex(self, Direction::Right) | |
411 | - } | |
412 | - | |
413 | - fn left(&self, v :usize) -> usize { | |
414 | - let Polygon(Coordinates(cs)) = self; | |
415 | - | |
416 | - match v { | |
417 | - 0 => cs.len() - 1, | |
418 | - _ => v - 1, | |
419 | - } | |
420 | - } | |
421 | - | |
422 | - fn right(&self, v :usize) -> usize { | |
423 | - let Polygon(Coordinates(cs)) = self; | |
424 | - | |
425 | - (v + 1) % cs.len() | |
426 | - } | |
427 | - | |
428 | - fn step(&self, v :usize, d :Direction) -> usize { | |
429 | - match d { | |
430 | - Direction::Left => self.left(v), | |
431 | - Direction::Right => self.right(v), | |
432 | - } | |
433 | - } | |
434 | - | |
435 | - fn next_y(&self, c :usize, d :Direction) -> Option<usize> { | |
436 | - fn inner<T>( p :&Polygon<T> | |
437 | - , c :usize | |
438 | - , n :usize | |
439 | - , d :Direction) -> Option<usize> | |
440 | - where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
441 | - + Copy + Debug + From<i32> { | |
442 | - if c == n { | |
443 | - None | |
444 | - } else { | |
445 | - let Coordinate(_, cy, _) = p.vertex(c); | |
446 | - let Coordinate(_, ny, _) = p.vertex(n); | |
447 | - | |
448 | - if ny < cy { None } else { Some(n) } | |
449 | - } | |
450 | - } | |
451 | - | |
452 | - inner(self, c, self.step(c, d), d) | |
453 | - } | |
454 | - | |
455 | - pub fn debug(&self) { | |
456 | - let mut left = self.left_vertices(); | |
457 | - let mut right = self.right_vertices(); | |
458 | - | |
459 | - if left.find(|l| right.find(|r| l.0 == r.0).is_some()).is_some() { | |
460 | - let left :Vec<Coordinate<T>> = self.left_vertices().collect(); | |
461 | - let right :Vec<Coordinate<T>> = self.right_vertices().collect(); | |
462 | - | |
463 | - println!("==="); | |
464 | - println!("== poly : {:?}", self); | |
465 | - println!("== ltop : {:?}", self.vert_min(Direction::Left)); | |
466 | - println!("== rtop : {:?}", self.vert_min(Direction::Right)); | |
467 | - println!("== left : {:?}", left); | |
468 | - println!("== right : {:?}", right); | |
469 | - println!("==="); | |
470 | - } | |
471 | - } | |
472 | -} | |
473 | - | |
474 | -impl<T> Drawable<T> for Polygon<T> | |
475 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
476 | - + Debug + Clone + Copy + From<i32> { | |
477 | - fn plot(&self) -> Coordinates<T> { | |
478 | - let Polygon(Coordinates(cs)) = self; | |
479 | - | |
480 | - match cs[..] { | |
481 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | |
482 | - [a] => Coordinates(vec!(a)), | |
483 | - [a, b] => Coordinates(a.line(&b)), | |
484 | - _ => { | |
485 | - let (a, b) = (cs[0], cs[1]); | |
486 | - let mut r = a.line(&b); | |
487 | - let mut i = b; | |
488 | - for j in cs[2..].iter() { | |
489 | - r.append(&mut i.line(j)[1..].to_vec()); | |
490 | - i = *j; | |
491 | - } | |
492 | - let mut j = a.line(&i); | |
493 | - let l = j.len(); | |
494 | - if l > 1 { | |
495 | - r.append(&mut j[1..l-1].to_vec()); | |
496 | - } | |
497 | - Coordinates(r) | |
498 | - }, | |
499 | - } | |
500 | - } | |
501 | -} | |
502 | - | |
503 | -impl<T> Fillable<T> for Polygon<T> | |
504 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | |
505 | - + Debug + Clone + Copy + From<i32> { | |
506 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32) { | |
507 | - let scanlines = self.left_edge().zip(self.right_edge()); | |
508 | - | |
509 | - for l in scanlines.flat_map(|(l, r)| l.line_iter(&r)) { | |
510 | - canvas.set_pixel(l, color); | |
511 | - } | |
512 | - } | |
513 | -} | |
514 | - | |
515 | -impl<T> Display for Polygon<T> where T: Copy { | |
516 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | |
517 | - let Polygon(a) = self; | |
518 | - write!(f, "Poly[{}]", a) | |
519 | - } | |
520 | -} |
fractional/src/fractional.rs
deleted
100644 → 0
1 | -// | |
2 | -// Some code to support fractional numbers for full precision rational number | |
3 | -// calculations. (At least for the standard operations.) | |
4 | -// This also implements a sqrt on fractional numbers, which can not be precise | |
5 | -// because of the irrational nature of most sqare roots. | |
6 | -// Fractions can only represent rational numbers precise. | |
7 | -// | |
8 | -// Georg Hopp <georg@steffers.org> | |
9 | -// | |
10 | -// Copyright © 2019 Georg Hopp | |
11 | -// | |
12 | -// This program is free software: you can redistribute it and/or modify | |
13 | -// it under the terms of the GNU General Public License as published by | |
14 | -// the Free Software Foundation, either version 3 of the License, or | |
15 | -// (at your option) any later version. | |
16 | -// | |
17 | -// This program is distributed in the hope that it will be useful, | |
18 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | -// GNU General Public License for more details. | |
21 | -// | |
22 | -// You should have received a copy of the GNU General Public License | |
23 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | |
24 | -// | |
25 | -use std::cmp::Ordering; | |
26 | -use std::convert::{TryFrom, TryInto}; | |
27 | -use std::fmt::{Formatter, Display}; | |
28 | -use std::num::TryFromIntError; | |
29 | -use std::ops::{Add,Sub,Neg,Mul,Div}; | |
30 | - | |
31 | -use crate::continuous::Continuous; | |
32 | - | |
33 | -#[derive(Debug, Eq, Clone, Copy)] | |
34 | -pub struct Fractional (pub i64, pub i64); | |
35 | - | |
36 | -#[inline] | |
37 | -fn hcf(x :i64, y :i64) -> i64 { | |
38 | - match y { | |
39 | - 0 => x, | |
40 | - _ => hcf(y, x % y), | |
41 | - } | |
42 | -} | |
43 | - | |
44 | -pub fn from_vector(xs: &Vec<i64>) -> Vec<Fractional> { | |
45 | - xs.iter().map(|x| Fractional(*x, 1)).collect() | |
46 | -} | |
47 | - | |
48 | -impl Fractional { | |
49 | - #[inline] | |
50 | - pub fn gcd(self, other: Self) -> i64 { | |
51 | - let Fractional(_, d1) = self; | |
52 | - let Fractional(_, d2) = other; | |
53 | - (d1 * d2) / hcf(d1, d2) | |
54 | - } | |
55 | - | |
56 | - #[inline] | |
57 | - pub fn reduce(self) -> Self { | |
58 | - let Fractional(n, d) = self; | |
59 | - let (_n, _d) = if n > d { (n, d) } else { (d, n) }; | |
60 | - | |
61 | - // if the difference from _n % _d to _n is very big we are close to | |
62 | - // a whole number and can ignore the fractional part... this reduces | |
63 | - // the precision but ensures smaller numbers for numerator and | |
64 | - // denominator. | |
65 | - if _d > 1 && (_n % _d) * 10000000 < _n { | |
66 | - if n == _n { | |
67 | - Self(_n / _d, 1) | |
68 | - } else { | |
69 | - Self(1, _n / _d) | |
70 | - } | |
71 | - } else { | |
72 | - // Self(n / hcf(n, d), d / hcf(n, d)) | |
73 | - // The above reduces prcisely but results in very large numerator | |
74 | - // or denominator occasionally. The below is less precise but | |
75 | - // keeps the numbers small… the bad point is, that it is not very | |
76 | - // fast. | |
77 | - let cont = Continuous::from_prec(&self, Some(5)); | |
78 | - (&cont).into() | |
79 | - } | |
80 | - } | |
81 | - | |
82 | - pub fn noreduce_add(self, other: Self) -> Self { | |
83 | - let Fractional(n1, d1) = self; | |
84 | - let Fractional(n2, d2) = other; | |
85 | - let n = n1 * (self.gcd(other) / d1) + n2 * (self.gcd(other) / d2); | |
86 | - Self(n, self.gcd(other)) | |
87 | - } | |
88 | - | |
89 | - pub fn noreduce_sub(self, other: Self) -> Self { | |
90 | - self.noreduce_add(other.noreduce_neg()) | |
91 | - } | |
92 | - | |
93 | - pub fn noreduce_neg(self) -> Self { | |
94 | - let Fractional(n, d) = self; | |
95 | - Self(-n, d) | |
96 | - } | |
97 | -} | |
98 | - | |
99 | -impl From<i64> for Fractional { | |
100 | - fn from(x: i64) -> Self { | |
101 | - Self(x, 1) | |
102 | - } | |
103 | -} | |
104 | - | |
105 | -impl From<i32> for Fractional { | |
106 | - fn from(x: i32) -> Self { | |
107 | - Self(x as i64, 1) | |
108 | - } | |
109 | -} | |
110 | - | |
111 | -impl TryFrom<usize> for Fractional { | |
112 | - type Error = &'static str; | |
113 | - | |
114 | - fn try_from(x: usize) -> Result<Self, Self::Error> { | |
115 | - let v = i64::try_from(x); | |
116 | - match v { | |
117 | - Err(_) => Err("Conversion from usize to i32 failed"), | |
118 | - Ok(_v) => Ok(Self(_v, 1)), | |
119 | - } | |
120 | - } | |
121 | -} | |
122 | - | |
123 | -impl TryInto<f64> for Fractional { | |
124 | - type Error = TryFromIntError; | |
125 | - | |
126 | - fn try_into(self) -> Result<f64, Self::Error> { | |
127 | - let n :i32 = self.0.try_into()?; | |
128 | - let d :i32 = self.1.try_into()?; | |
129 | - Ok(f64::from(n) / f64::from(d)) | |
130 | - } | |
131 | -} | |
132 | - | |
133 | -impl TryInto<(i32, i32)> for Fractional { | |
134 | - type Error = TryFromIntError; | |
135 | - | |
136 | - fn try_into(self) -> Result<(i32, i32), Self::Error> { | |
137 | - let a :i32 = (self.0 / self.1).try_into()?; | |
138 | - let b :i32 = (self.0 % self.1).try_into()?; | |
139 | - Ok((a, b)) | |
140 | - } | |
141 | -} | |
142 | - | |
143 | -impl Display for Fractional { | |
144 | - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
145 | - write!(f, "({}/{})", self.0, self.1) | |
146 | - } | |
147 | -} | |
148 | - | |
149 | -impl PartialEq for Fractional { | |
150 | - fn eq(&self, other: &Self) -> bool { | |
151 | - let Fractional(n1, d1) = self; | |
152 | - let Fractional(n2, d2) = other; | |
153 | - n1 * (self.gcd(*other) / d1) == n2 * (self.gcd(*other) / d2) | |
154 | - } | |
155 | -} | |
156 | - | |
157 | -impl PartialOrd for Fractional { | |
158 | - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
159 | - Some(self.cmp(other)) | |
160 | - } | |
161 | -} | |
162 | - | |
163 | -impl Ord for Fractional { | |
164 | - fn cmp(&self, other: &Self) -> Ordering { | |
165 | - let Fractional(n1, d1) = self; | |
166 | - let Fractional(n2, d2) = other; | |
167 | - let x = n1 * (self.gcd(*other) / d1); | |
168 | - let y = n2 * (self.gcd(*other) / d2); | |
169 | - x.cmp(&y) | |
170 | - } | |
171 | -} | |
172 | - | |
173 | -impl Add for Fractional { | |
174 | - type Output = Self; | |
175 | - | |
176 | - fn add(self, other: Self) -> Self { | |
177 | - self.noreduce_add(other).reduce() | |
178 | - } | |
179 | -} | |
180 | - | |
181 | -impl Sub for Fractional { | |
182 | - type Output = Self; | |
183 | - | |
184 | - fn sub(self, other: Self) -> Self { | |
185 | - self + -other | |
186 | - } | |
187 | -} | |
188 | - | |
189 | -impl Neg for Fractional { | |
190 | - type Output = Self; | |
191 | - | |
192 | - fn neg(self) -> Self { | |
193 | - let Fractional(n, d) = self; | |
194 | - Self(-n, d) | |
195 | - } | |
196 | -} | |
197 | - | |
198 | -impl Mul for Fractional { | |
199 | - type Output = Self; | |
200 | - | |
201 | - fn mul(self, other :Self) -> Self { | |
202 | - let Fractional(n1, d1) = self; | |
203 | - let Fractional(n2, d2) = other; | |
204 | - Self(n1 * n2, d1 * d2).reduce() | |
205 | - } | |
206 | -} | |
207 | - | |
208 | -impl Div for Fractional { | |
209 | - type Output = Self; | |
210 | - | |
211 | - fn div(self, other: Self) -> Self { | |
212 | - let Fractional(n, d) = other; | |
213 | - self * Fractional(d, n) | |
214 | - } | |
215 | -} | |
216 | - | |
217 | - /* some stuff that could be tested... | |
218 | - let x = Fractional(1, 3); | |
219 | - let y = Fractional(1, 6); | |
220 | - | |
221 | - println!( | |
222 | - "Greatest common denominator of {} and {}: {}", x, y, x.gcd(y)); | |
223 | - println!("Numerator of {}: {}", x, x.numerator()); | |
224 | - println!("Denominator of {}: {}", x, x.denominator()); | |
225 | - assert_eq!(Fractional(1, 3), Fractional(2, 6)); | |
226 | - assert_eq!(Fractional(1, 3), Fractional(1, 3)); | |
227 | - assert_eq!(y < x, true); | |
228 | - assert_eq!(y > x, false); | |
229 | - assert_eq!(x == y, false); | |
230 | - assert_eq!(x == x, true); | |
231 | - assert_eq!(x + y, Fractional(1, 2)); | |
232 | - println!("{} + {} = {}", x, y, x + y); | |
233 | - assert_eq!(x - y, Fractional(1, 6)); | |
234 | - println!("{} - {} = {}", x, y, x - y); | |
235 | - assert_eq!(y - x, Fractional(-1, 6)); | |
236 | - println!("{} - {} = {}", y, x, y - x); | |
237 | - assert_eq!(-x, Fractional(-1, 3)); | |
238 | - println!("-{} = {}", x, -x); | |
239 | - assert_eq!(x * y, Fractional(1, 18)); | |
240 | - println!("{} * {} = {}", x, y, x * y); | |
241 | - assert_eq!(x / y, Fractional(2, 1)); | |
242 | - println!("{} / {} = {}", x, y, x / y); | |
243 | - assert_eq!(y / x, Fractional(1, 2)); | |
244 | - println!("{} / {} = {}", y, x, y / x); | |
245 | - | |
246 | - println!("Fractional from 3: {}", Fractional::from(3)); | |
247 | - let z :f64 = Fractional::into(x); | |
248 | - println!("Floating point of {}: {}", x, z); | |
249 | - let (d, r) = Fractional::into(x); | |
250 | - println!("(div, rest) of {}: ({}, {})", x, d, r); | |
251 | - */ | |
252 | - |
fractional/src/geometry.rs
deleted
100644 → 0
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 | -use std::convert::{From, Into}; | |
22 | -use std::ops::{Add,Sub,Neg,Mul,Div}; | |
23 | -use std::fmt::Debug; | |
24 | - | |
25 | -use crate::easel::{Canvas, Coordinate, Coordinates, Polygon}; | |
26 | -use crate::transform::{TMatrix, Transformable}; | |
27 | -use crate::trigonometry::Trig; | |
28 | -use crate::vector::Vector; | |
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 | -#[derive(Debug, PartialEq, Eq, Clone, Copy)] | |
38 | -pub struct Point<T>(pub Vector<T>, T) | |
39 | - where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig; | |
40 | - | |
41 | -impl<T> Point<T> | |
42 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
43 | - + Mul<Output = T> + Div<Output = T> | |
44 | - + PartialEq + Trig + Copy + From<i32> { | |
45 | - pub fn new(x :T, y :T, z :T) -> Self { | |
46 | - Self(Vector(x, y, z), 1.into()) | |
47 | - } | |
48 | -} | |
49 | - | |
50 | -impl<T> Add for Point<T> | |
51 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
52 | - + Mul<Output = T> + Div<Output = T> | |
53 | - + PartialEq + Trig + Copy { | |
54 | - type Output = Self; | |
55 | - | |
56 | - fn add(self, other :Self) -> Self { | |
57 | - let Point(v1, w1) = self; | |
58 | - let Point(v2, w2) = other; | |
59 | - Self(v1 + v2, w1 + w2) | |
60 | - } | |
61 | -} | |
62 | - | |
63 | -impl<T> Neg for Point<T> | |
64 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
65 | - + Mul<Output = T> + Div<Output = T> | |
66 | - + PartialEq + Trig + Copy { | |
67 | - type Output = Self; | |
68 | - | |
69 | - fn neg(self) -> Self { | |
70 | - let Point(v, w) = self; | |
71 | - Self(-v, -w) | |
72 | - } | |
73 | -} | |
74 | - | |
75 | -impl<T> Sub for Point<T> | |
76 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
77 | - + Mul<Output = T> + Div<Output = T> | |
78 | - + PartialEq + Trig + Copy { | |
79 | - type Output = Self; | |
80 | - | |
81 | - fn sub(self, other :Self) -> Self { | |
82 | - self + -other | |
83 | - } | |
84 | -} | |
85 | - | |
86 | -impl<T> Mul for Point<T> | |
87 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
88 | - + Mul<Output = T> + Div<Output = T> | |
89 | - + PartialEq + Trig + Copy + From<i32> { | |
90 | - type Output = Self; | |
91 | - | |
92 | - fn mul(self, other :Self) -> Self { | |
93 | - let a :Vector<T> = self.into(); | |
94 | - let b :Vector<T> = other.into(); | |
95 | - | |
96 | - Point(a * b, 1.into()) | |
97 | - } | |
98 | -} | |
99 | - | |
100 | -impl<T> From<Vector<T>> for Point<T> | |
101 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
102 | - + Mul<Output = T> + Div<Output = T> | |
103 | - + PartialEq + Trig + Copy + From<i32> { | |
104 | - fn from(v :Vector<T>) -> Self { | |
105 | - Point(v, 1.into()) | |
106 | - } | |
107 | -} | |
108 | - | |
109 | -impl<T> Into<Vector<T>> for Point<T> | |
110 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
111 | - + Mul<Output = T> + Div<Output = T> | |
112 | - + PartialEq + Trig + Copy + From<i32> { | |
113 | - fn into(self) -> Vector<T> { | |
114 | - let Point(v, w) = self; | |
115 | - | |
116 | - if w == 0.into() { | |
117 | - v | |
118 | - } else { | |
119 | - v.mul(&w.recip()) | |
120 | - } | |
121 | - } | |
122 | -} | |
123 | - | |
124 | -impl<T> Transformable<T> for Point<T> | |
125 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
126 | - + Mul<Output = T> + Div<Output = T> | |
127 | - + PartialEq + Debug + Trig + Copy + From<i32> { | |
128 | - fn transform(&self, m :&TMatrix<T>) -> Self { | |
129 | - let Point(v, w) = *self; | |
130 | - let (v, w) = m.apply(&v, w); | |
131 | - | |
132 | - if w == 0.into() { | |
133 | - v.into() | |
134 | - } else { | |
135 | - v.mul(&w.recip()).into() | |
136 | - } | |
137 | - } | |
138 | -} | |
139 | - | |
140 | -#[derive(Debug)] | |
141 | -pub struct Polyeder<T> | |
142 | -where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig { | |
143 | - points :Vec<Point<T>>, | |
144 | - faces :Vec<Face<T>>, | |
145 | -} | |
146 | - | |
147 | -pub trait Primitives<T> | |
148 | -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | |
149 | - fn transform(&self, m :&TMatrix<T>) -> Self; | |
150 | - fn project( &self | |
151 | - , camera :&Camera<T> | |
152 | - , light :&DirectLight<T> | |
153 | - , col :u32 ) -> Vec<(Polygon<T>, u32)>; | |
154 | -} | |
155 | - | |
156 | -pub struct Camera<T> | |
157 | -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | |
158 | - width :T, | |
159 | - height :T, | |
160 | - distance :T, | |
161 | - project :TMatrix<T>, | |
162 | -} | |
163 | - | |
164 | -pub struct DirectLight<T> | |
165 | -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | |
166 | - direction: Vector<T>, | |
167 | -} | |
168 | - | |
169 | -impl<T> Camera<T> | |
170 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
171 | - + Mul<Output = T> + Div<Output = T> | |
172 | - + PartialEq + Debug + Copy + Trig + From<i32> { | |
173 | - // This code assumes that the size of the viewport is always | |
174 | - // equal to the size of the physical screen… e.g. window/canvas thus some | |
175 | - // effects can't be done. See book for examples with different viewport | |
176 | - // and screen sizes. | |
177 | - pub fn new(c :&dyn Canvas<T>, angle :i32) -> Self { | |
178 | - let width :T = (c.width() as i32).into(); | |
179 | - let height :T = (c.height() as i32).into(); | |
180 | - let d :T = 1.into(); | |
181 | - let fov = T::cot(angle) * width; | |
182 | - let wh = width / 2.into(); | |
183 | - let hh = height / 2.into(); | |
184 | - | |
185 | - Camera { width: width | |
186 | - , height: height | |
187 | - , distance: d | |
188 | - , project: TMatrix::new( | |
189 | - ( fov, 0.into(), wh, 0.into()) | |
190 | - , (0.into(), fov, hh, 0.into()) | |
191 | - , (0.into(), 0.into(), d, 1.into()) | |
192 | - , (0.into(), 0.into(), 1.into(), 0.into()) ) } | |
193 | - } | |
194 | - | |
195 | - pub fn get_distance(&self) -> T { | |
196 | - self.distance | |
197 | - } | |
198 | - | |
199 | - pub fn get_projection(&self) -> TMatrix<T> { | |
200 | - self.project | |
201 | - } | |
202 | - | |
203 | - pub fn project(&self, p :Point<T>) -> Point<T> { | |
204 | - p.transform(&self.project) | |
205 | - } | |
206 | -} | |
207 | - | |
208 | -impl<T> DirectLight<T> | |
209 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
210 | - + Mul<Output = T> + Div<Output = T> | |
211 | - + Debug + Copy + Trig + From<i32> { | |
212 | - pub fn new(v :Vector<T>) -> Self { | |
213 | - DirectLight{ direction: v } | |
214 | - } | |
215 | - | |
216 | - pub fn dir(&self) -> Vector<T> { | |
217 | - self.direction | |
218 | - } | |
219 | -} | |
220 | - | |
221 | -impl<T> Face<T> | |
222 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | |
223 | - + Mul<Output = T> + Div<Output = T> | |
224 | - + PartialEq + Debug + Copy + Trig + From<i32> { | |
225 | - fn new(corners :Vec<usize>, ps :&[Point<T>]) -> Self { | |
226 | - let mut f = Face{ corners: corners, normal: None }; | |
227 | - f.update_normal(ps); | |
228 | - f | |
229 | - } | |
230 | - | |
231 | - fn update_normal(&mut self, ps :&[Point<T>]) { | |
232 | - let edge10 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[0]]).into(); | |