Commit a0c638c5575fd5b43dde1ecfc4b1e7b7ec1c990e
1 parent
c7543ff2
Start implement 3d from fractional as html5 canvas
Showing
9 changed files
with
1515 additions
and
2 deletions
@@ -12,6 +12,7 @@ default = ["console_error_panic_hook"] | @@ -12,6 +12,7 @@ default = ["console_error_panic_hook"] | ||
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | wasm-bindgen = "0.2" | 14 | wasm-bindgen = "0.2" |
15 | +lazy_static = "1.4.0" | ||
15 | 16 | ||
16 | # The `console_error_panic_hook` crate provides better debugging of panics by | 17 | # The `console_error_panic_hook` crate provides better debugging of panics by |
17 | # logging them with `console.error`. This is great for development, but requires | 18 | # logging them with `console.error`. This is great for development, but requires |
tutorial/wasm-game-of-life/src/easel.rs
0 → 100644
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 | +} |
tutorial/wasm-game-of-life/src/geometry.rs
0 → 100644
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 | +#[derive(Debug, Clone, Copy)] | ||
157 | +pub struct Camera<T> | ||
158 | +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | ||
159 | + width :T, | ||
160 | + height :T, | ||
161 | + distance :T, | ||
162 | + project :TMatrix<T>, | ||
163 | +} | ||
164 | + | ||
165 | +pub struct DirectLight<T> | ||
166 | +where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | ||
167 | + direction: Vector<T>, | ||
168 | +} | ||
169 | + | ||
170 | +impl<T> Camera<T> | ||
171 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
172 | + + Mul<Output = T> + Div<Output = T> | ||
173 | + + PartialEq + Debug + Copy + Trig + From<i32> { | ||
174 | + // This code assumes that the size of the viewport is always | ||
175 | + // equal to the size of the physical screen… e.g. window/canvas thus some | ||
176 | + // effects can't be done. See book for examples with different viewport | ||
177 | + // and screen sizes. | ||
178 | + pub fn new(c :&dyn Canvas<T>, angle :i32) -> Self { | ||
179 | + let width :T = (c.width() as i32).into(); | ||
180 | + let height :T = (c.height() as i32).into(); | ||
181 | + let d :T = 1.into(); | ||
182 | + let fov = T::cot(angle) * width; | ||
183 | + let wh = width / 2.into(); | ||
184 | + let hh = height / 2.into(); | ||
185 | + | ||
186 | + Camera { width: width | ||
187 | + , height: height | ||
188 | + , distance: d | ||
189 | + , project: TMatrix::new( | ||
190 | + ( fov, 0.into(), wh, 0.into()) | ||
191 | + , (0.into(), fov, hh, 0.into()) | ||
192 | + , (0.into(), 0.into(), d, 1.into()) | ||
193 | + , (0.into(), 0.into(), 1.into(), 0.into()) ) } | ||
194 | + } | ||
195 | + | ||
196 | + pub fn get_distance(&self) -> T { | ||
197 | + self.distance | ||
198 | + } | ||
199 | + | ||
200 | + pub fn get_projection(&self) -> TMatrix<T> { | ||
201 | + self.project | ||
202 | + } | ||
203 | + | ||
204 | + pub fn project(&self, p :Point<T>) -> Point<T> { | ||
205 | + p.transform(&self.project) | ||
206 | + } | ||
207 | +} | ||
208 | + | ||
209 | +impl<T> DirectLight<T> | ||
210 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
211 | + + Mul<Output = T> + Div<Output = T> | ||
212 | + + Debug + Copy + Trig + From<i32> { | ||
213 | + pub fn new(v :Vector<T>) -> Self { | ||
214 | + DirectLight{ direction: v } | ||
215 | + } | ||
216 | + | ||
217 | + pub fn dir(&self) -> Vector<T> { | ||
218 | + self.direction | ||
219 | + } | ||
220 | +} | ||
221 | + | ||
222 | +impl<T> Face<T> | ||
223 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
224 | + + Mul<Output = T> + Div<Output = T> | ||
225 | + + PartialEq + Debug + Copy + Trig + From<i32> { | ||
226 | + fn new(corners :Vec<usize>, ps :&[Point<T>]) -> Self { | ||
227 | + let mut f = Face{ corners: corners, normal: None }; | ||
228 | + f.update_normal(ps); | ||
229 | + f | ||
230 | + } | ||
231 | + | ||
232 | + fn update_normal(&mut self, ps :&[Point<T>]) { | ||
233 | + let edge10 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[0]]).into(); | ||
234 | + let edge12 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[2]]).into(); | ||
235 | + self.normal = Some(edge10 * edge12); | ||
236 | + } | ||
237 | +} | ||
238 | + | ||
239 | +impl<T> Polyeder<T> | ||
240 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
241 | + + Mul<Output = T> + Div<Output = T> | ||
242 | + + PartialEq + Debug + Copy + Trig + From<i32> { | ||
243 | + fn update_normals(&mut self) { | ||
244 | + for f in self.faces.iter_mut() { | ||
245 | + f.update_normal(&self.points); | ||
246 | + } | ||
247 | + } | ||
248 | + | ||
249 | + // construct via cube, see polyhedra.pdf | ||
250 | + pub fn tetrahedron(a :T) -> Polyeder<T> { | ||
251 | + let f2 :T = 2.into(); | ||
252 | + let ch = a / (f2 * T::sqrt(f2).unwrap()); | ||
253 | + | ||
254 | + let ps = vec!( Point::new(-ch, -ch, ch) // A | ||
255 | + , Point::new(-ch, ch, -ch) // C | ||
256 | + , Point::new( ch, -ch, -ch) // E | ||
257 | + , Point::new( ch, ch, ch) ); // G | ||
258 | + | ||
259 | + // bottom: 1, 2, 3 | ||
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 | + //let fs = vec!( Face::new(vec!(0, 1, 2), &ps) // bottom | ||
265 | + // , Face::new(vec!(0, 2, 3), &ps) | ||
266 | + // , Face::new(vec!(3, 1, 0), &ps) | ||
267 | + // , Face::new(vec!(3, 2, 1), &ps) ); | ||
268 | + | ||
269 | + Polyeder{ points: ps, faces: fs } | ||
270 | + } | ||
271 | + | ||
272 | + pub fn triangle(a :T) -> Polyeder<T> { | ||
273 | + let f0 :T = 0.into(); | ||
274 | + let f3 :T = 3.into(); | ||
275 | + let f6 :T = 6.into(); | ||
276 | + let zi :T = T::sqrt(f3).unwrap() / f6 * a; | ||
277 | + let zc :T = T::sqrt(f3).unwrap() / f3 * a; | ||
278 | + let ah :T = a / 2.into(); | ||
279 | + | ||
280 | + let ps = vec!( Point::new(-ah, f0, -zi) | ||
281 | + , Point::new( f0, f0, zc) | ||
282 | + , Point::new( ah, f0, -zi) ); | ||
283 | + | ||
284 | + let fs = vec!(Face::new(vec!(0, 1, 2), &ps)); | ||
285 | + | ||
286 | + Polyeder{ points: ps, faces: fs } | ||
287 | + } | ||
288 | + | ||
289 | + pub fn cube(a :T) -> Polyeder<T> { | ||
290 | + let ah :T = a / From::<i32>::from(2); | ||
291 | + | ||
292 | + let ps = vec!( Point::new(-ah, ah, -ah) // 0 => front 1 | ||
293 | + , Point::new(-ah, -ah, -ah) // 1 => front 2 | ||
294 | + , Point::new( ah, -ah, -ah) // 2 => front 3 | ||
295 | + , Point::new( ah, ah, -ah) // 3 => front 4 | ||
296 | + , Point::new(-ah, ah, ah) // 4 => back 1 | ||
297 | + , Point::new(-ah, -ah, ah) // 5 => back 2 | ||
298 | + , Point::new( ah, -ah, ah) // 6 => back 3 | ||
299 | + , Point::new( ah, ah, ah) ); // 7 => back 4 | ||
300 | + | ||
301 | + let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front | ||
302 | + , Face::new(vec!(7, 6, 5, 4), &ps) // back | ||
303 | + , Face::new(vec!(1, 5, 6, 2), &ps) // top | ||
304 | + , Face::new(vec!(0, 3, 7, 4), &ps) // bottom | ||
305 | + , Face::new(vec!(0, 4, 5, 1), &ps) // left | ||
306 | + , Face::new(vec!(2, 6, 7, 3), &ps) ); // right | ||
307 | + | ||
308 | + Polyeder{ points: ps, faces: fs } | ||
309 | + } | ||
310 | +} | ||
311 | + | ||
312 | +impl<T> Primitives<T> for Polyeder<T> | ||
313 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
314 | + + Mul<Output = T> + Div<Output = T> | ||
315 | + + Debug + Copy + Trig + From<i32> + PartialOrd { | ||
316 | + // TODO Maybe this should also be an instance of Transformable… | ||
317 | + fn transform(&self, m :&TMatrix<T>) -> Self { | ||
318 | + let Polyeder{ points: ps, faces: fs } = self; | ||
319 | + | ||
320 | + let mut p = Polyeder{ | ||
321 | + points: ps.iter().map(|p| p.transform(m)).collect() | ||
322 | + , faces: fs.to_vec() | ||
323 | + }; | ||
324 | + | ||
325 | + // TODO alternatively we could rotate the normals too, but this cannot | ||
326 | + // done with the original matrix… the question is, what is faster. | ||
327 | + p.update_normals(); | ||
328 | + p | ||
329 | + } | ||
330 | + | ||
331 | + fn project( &self | ||
332 | + , camera :&Camera<T> | ||
333 | + , light :&DirectLight<T> | ||
334 | + , color :u32 ) -> Vec<(Polygon<T>, u32)> { | ||
335 | + // Helper to create a Polygon from Coordinates… | ||
336 | + // TODO probably there needs to be a Polygon constructor for this. | ||
337 | + fn polygon<I, T>(c :I) -> Polygon<T> | ||
338 | + where I: Iterator<Item = Coordinate<T>> { | ||
339 | + Polygon(Coordinates(c.collect())) | ||
340 | + } | ||
341 | + | ||
342 | + // this one does the projection... as the projection was the last | ||
343 | + // matrix we do not need to do it here. | ||
344 | + let to_coord = |p :&usize| { | ||
345 | + let Point(v, _) = camera.project(self.points[*p]); | ||
346 | + Coordinate(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into()) | ||
347 | + }; | ||
348 | + let to_poly = |f :&Face<T>| { | ||
349 | + let pg = polygon(f.corners.iter().map(to_coord)); | ||
350 | + let mut r :T = (((color >> 16) & 0xFF) as i32).into(); | ||
351 | + let mut g :T = (((color >> 8) & 0xFF) as i32).into(); | ||
352 | + let mut b :T = (((color ) & 0xFF) as i32).into(); | ||
353 | + let lf :T = match f.normal { | ||
354 | + None => 1.into(), | ||
355 | + Some(n) => n.dot(light.dir()) | ||
356 | + / (n.mag() * light.dir().mag()), | ||
357 | + }; | ||
358 | + | ||
359 | + // this "if" represents a first simple backface culling | ||
360 | + // approach. We only return face that face towards us. | ||
361 | + if lf < 0.into() { | ||
362 | + r = r * -lf; | ||
363 | + g = g * -lf; | ||
364 | + b = b * -lf; | ||
365 | + | ||
366 | + let c :u32 = (r.round() as u32) << 16 | ||
367 | + | (g.round() as u32) << 8 | ||
368 | + | (b.round() as u32); | ||
369 | + | ||
370 | + Some((pg, c)) | ||
371 | + } else { | ||
372 | + None | ||
373 | + }}; | ||
374 | + | ||
375 | + self.faces.iter().filter_map(to_poly).collect() | ||
376 | + } | ||
377 | +} |
1 | +extern crate lazy_static; | ||
2 | + | ||
3 | +pub type Error = &'static str; | ||
4 | + | ||
5 | +pub mod easel; | ||
6 | +pub mod transform; | ||
7 | +pub mod trigonometry; | ||
8 | +pub mod vector; | ||
9 | +pub mod geometry; | ||
10 | + | ||
1 | mod utils; | 11 | mod utils; |
2 | 12 | ||
13 | +use vector::Vector; | ||
14 | +use easel::{Canvas, Coordinate, Drawable, Fillable}; | ||
15 | +use geometry::{Camera, DirectLight, Polyeder, Primitives}; | ||
16 | +use transform::{TMatrix}; | ||
17 | + | ||
3 | use std::fmt::{Display, Formatter, Result}; | 18 | use std::fmt::{Display, Formatter, Result}; |
19 | +use std::ptr; | ||
20 | +use std::sync::mpsc; | ||
21 | +use std::time::Instant; | ||
4 | use wasm_bindgen::prelude::*; | 22 | use wasm_bindgen::prelude::*; |
5 | 23 | ||
6 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global | 24 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global |
@@ -10,6 +28,117 @@ use wasm_bindgen::prelude::*; | @@ -10,6 +28,117 @@ use wasm_bindgen::prelude::*; | ||
10 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; | 28 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
11 | 29 | ||
12 | #[wasm_bindgen] | 30 | #[wasm_bindgen] |
31 | +#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
32 | +pub struct Color(u8, u8, u8, u8); | ||
33 | + | ||
34 | +#[wasm_bindgen] | ||
35 | +pub struct View3d { width :u16 | ||
36 | + , height :u16 | ||
37 | + , size :usize | ||
38 | + , start :Instant | ||
39 | + , tetrahedron :Polyeder<f64> | ||
40 | + , cube :Polyeder<f64> | ||
41 | + , camera :Option<Camera<f64>> | ||
42 | + , light :DirectLight<f64> | ||
43 | + , zbuf :Vec<f64> | ||
44 | + , image :Vec<Color> | ||
45 | +} | ||
46 | + | ||
47 | +#[wasm_bindgen] | ||
48 | +impl View3d { | ||
49 | + pub fn new(width :u16, height :u16) -> Self { | ||
50 | + let size = width as usize * height as usize; | ||
51 | + let light_vector = Vector(0.0, 0.0, 1.0); | ||
52 | + | ||
53 | + let mut view3d = Self { width: width | ||
54 | + , height: height | ||
55 | + , size: size | ||
56 | + , start: Instant::now() | ||
57 | + , tetrahedron: Polyeder::tetrahedron(100.0) | ||
58 | + , cube: Polyeder::cube(56.25) | ||
59 | + , camera: None | ||
60 | + , light: DirectLight::new(light_vector) | ||
61 | + , zbuf: vec!(0.0; size) | ||
62 | + , image: vec!(Color(0, 0, 0, 0); size) }; | ||
63 | + | ||
64 | + view3d.camera = Some(Camera::<f64>::new(&view3d, 45)); | ||
65 | + view3d | ||
66 | + } | ||
67 | + | ||
68 | + pub fn update(mut self) { | ||
69 | + let deg = ((self.start.elapsed() / 25).as_millis() % 360) as i32; | ||
70 | + | ||
71 | + let t = TMatrix::translate(Vector(0.0, 0.0, 150.0)); | ||
72 | + let rz = TMatrix::rotate_z(deg); | ||
73 | + let rx = TMatrix::rotate_x(-deg*2); | ||
74 | + let ry = TMatrix::rotate_y(-deg*2); | ||
75 | + | ||
76 | + let rot1 = TMatrix::combine(vec!(rz, rx, t)); | ||
77 | + let rot2 = TMatrix::combine(vec!(rz, ry, t)); | ||
78 | + | ||
79 | + let objects = vec!( (self.tetrahedron.transform(&rot1), 0xFFFF00) | ||
80 | + , ( self.cube.transform(&rot2), 0x0000FF) ); | ||
81 | + | ||
82 | + self.clear(); | ||
83 | + | ||
84 | + match self.camera { | ||
85 | + None => {}, | ||
86 | + Some(camera) => { | ||
87 | + for (o, color) in objects { | ||
88 | + for (pg, c) in o.project(&camera, &self.light, color) { | ||
89 | + (&pg).fill(&mut self, c); | ||
90 | + } | ||
91 | + } | ||
92 | + }, | ||
93 | + } | ||
94 | + } | ||
95 | + | ||
96 | + pub fn image(&self) -> *const Color { | ||
97 | + self.image.as_ptr() | ||
98 | + } | ||
99 | +} | ||
100 | + | ||
101 | +impl Canvas<f64> for View3d { | ||
102 | + fn width(&self) -> u16 { | ||
103 | + self.width | ||
104 | + } | ||
105 | + | ||
106 | + fn height(&self) -> u16 { | ||
107 | + self.height | ||
108 | + } | ||
109 | + | ||
110 | + fn clear(&mut self) { | ||
111 | + self.zbuf = vec!(0.0; self.size); | ||
112 | + unsafe { | ||
113 | + let ptr = self.image.as_mut_ptr(); | ||
114 | + ptr::write_bytes(ptr, 0, self.size); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + fn set_pixel(&mut self, c :Coordinate<f64>, color :u32) { | ||
119 | + let Coordinate(x, y, zr) = c; | ||
120 | + let idx :usize = (y * (self.width as i32) + x) as usize; | ||
121 | + | ||
122 | + let r = ((color >> 16) & 0xFF) as u8; | ||
123 | + let g = ((color >> 8) & 0xFF) as u8; | ||
124 | + let b = ( color & 0xFF) as u8; | ||
125 | + | ||
126 | + if self.zbuf[idx] < zr { | ||
127 | + self.zbuf[idx] = zr; | ||
128 | + self.image[idx] = Color(r, g, b, 0xFF); | ||
129 | + } | ||
130 | + } | ||
131 | + | ||
132 | + // Empty implementations for now… mostly not needed because it is | ||
133 | + // done from JavaScript… | ||
134 | + fn init_events(&self) {} | ||
135 | + fn start_events(&self, _ :mpsc::Sender<i32>) {} | ||
136 | + fn draw( &mut self, _ :&dyn Drawable<f64>, _ :Coordinate<f64>, _ :u32 ) {} | ||
137 | + fn put_text(&self, _ :Coordinate<f64>, _ :&str) {} | ||
138 | + fn show(&self) {} | ||
139 | +} | ||
140 | + | ||
141 | +#[wasm_bindgen] | ||
13 | #[repr(u8)] | 142 | #[repr(u8)] |
14 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 143 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
15 | pub enum Cell { | 144 | pub enum Cell { |
tutorial/wasm-game-of-life/src/transform.rs
0 → 100644
1 | +// | ||
2 | +// Transformation of vectors in a given coordinate system... | ||
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::ops::{Add, Sub, Neg, Mul, Div}; | ||
22 | +use std::fmt::Debug; | ||
23 | + | ||
24 | +use crate::Vector; | ||
25 | +use crate::trigonometry::Trig; | ||
26 | + | ||
27 | +#[derive(Debug, Clone, Copy)] | ||
28 | +pub struct TMatrix<T>( (T, T, T, T) | ||
29 | + , (T, T, T, T) | ||
30 | + , (T, T, T, T) | ||
31 | + , (T, T, T, T) ) | ||
32 | + where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From<i32> + Copy; | ||
33 | + | ||
34 | +pub trait Transformable<T> | ||
35 | +where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From<i32> + Copy { | ||
36 | + fn transform(&self, m :&TMatrix<T>) -> Self; | ||
37 | +} | ||
38 | + | ||
39 | +impl<T> TMatrix<T> | ||
40 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
41 | + + Mul<Output = T> + Div<Output = T> | ||
42 | + + Debug + Trig + From<i32> + Copy { | ||
43 | + pub fn new( r1 :(T, T, T, T) | ||
44 | + , r2 :(T, T, T, T) | ||
45 | + , r3 :(T, T, T, T) | ||
46 | + , r4 :(T, T, T, T) ) -> Self { | ||
47 | + TMatrix(r1, r2, r3, r4) | ||
48 | + } | ||
49 | + | ||
50 | + pub fn unit() -> Self { | ||
51 | + Self::new( (1.into(), 0.into(), 0.into(), 0.into()) | ||
52 | + , (0.into(), 1.into(), 0.into(), 0.into()) | ||
53 | + , (0.into(), 0.into(), 1.into(), 0.into()) | ||
54 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
55 | + } | ||
56 | + | ||
57 | + pub fn translate(v :Vector<T>) -> Self { | ||
58 | + let Vector(x, y, z) = v; | ||
59 | + | ||
60 | + Self::new( (1.into(), 0.into(), 0.into(), x) | ||
61 | + , (0.into(), 1.into(), 0.into(), y) | ||
62 | + , (0.into(), 0.into(), 1.into(), z) | ||
63 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
64 | + } | ||
65 | + | ||
66 | + pub fn rotate_x(a :i32) -> Self { | ||
67 | + let sin :T = Trig::sin(a); | ||
68 | + let cos :T = Trig::cos(a); | ||
69 | + | ||
70 | + Self::new( (1.into(), 0.into(), 0.into(), 0.into()) | ||
71 | + , (0.into(), cos , -sin , 0.into()) | ||
72 | + , (0.into(), sin , cos , 0.into()) | ||
73 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
74 | + } | ||
75 | + | ||
76 | + pub fn rotate_y(a :i32) -> Self { | ||
77 | + let sin :T = Trig::sin(a); | ||
78 | + let cos :T = Trig::cos(a); | ||
79 | + | ||
80 | + Self::new( (cos , 0.into(), sin , 0.into()) | ||
81 | + , (0.into(), 1.into(), 0.into(), 0.into()) | ||
82 | + , (-sin , 0.into(), cos , 0.into()) | ||
83 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
84 | + } | ||
85 | + | ||
86 | + pub fn rotate_z(a :i32) -> Self { | ||
87 | + let sin :T = Trig::sin(a); | ||
88 | + let cos :T = Trig::cos(a); | ||
89 | + | ||
90 | + Self::new( (cos , -sin , 0.into(), 0.into()) | ||
91 | + , (sin , cos , 0.into(), 0.into()) | ||
92 | + , (0.into(), 0.into(), 1.into(), 0.into()) | ||
93 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
94 | + } | ||
95 | + | ||
96 | + pub fn rotate_v(v :&Vector<T>, a :i32) -> Self { | ||
97 | + let Vector(x, y, z) = *v; | ||
98 | + | ||
99 | + let sin :T = Trig::sin(a); | ||
100 | + let cos :T = Trig::cos(a); | ||
101 | + | ||
102 | + let zero :T = 0.into(); | ||
103 | + let one :T = 1.into(); | ||
104 | + | ||
105 | + Self::new( ( (one - cos) * x * x + cos | ||
106 | + , (one - cos) * x * y - sin * z | ||
107 | + , (one - cos) * x * z + sin * y | ||
108 | + , zero ) | ||
109 | + , ( (one - cos) * x * y + sin * z | ||
110 | + , (one - cos) * y * y + cos | ||
111 | + , (one - cos) * y * z - sin * x | ||
112 | + , zero ) | ||
113 | + , ( (one - cos) * x * z - sin * y | ||
114 | + , (one - cos) * y * z + sin * x | ||
115 | + , (one - cos) * z * z + cos | ||
116 | + , zero ) | ||
117 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
118 | + } | ||
119 | + | ||
120 | + pub fn scale(v :Vector<T>) -> Self { | ||
121 | + let Vector(x, y, z) = v; | ||
122 | + | ||
123 | + Self::new( ( x, 0.into(), 0.into(), 0.into()) | ||
124 | + , (0.into(), y, 0.into(), 0.into()) | ||
125 | + , (0.into(), 0.into(), z, 0.into()) | ||
126 | + , (0.into(), 0.into(), 0.into(), 1.into()) ) | ||
127 | + } | ||
128 | + | ||
129 | + pub fn combine<I>(mi :I) -> TMatrix<T> | ||
130 | + where I: IntoIterator<Item = TMatrix<T>> { | ||
131 | + | ||
132 | + mi.into_iter().fold(Self::unit(), |acc, x| x * acc) | ||
133 | + } | ||
134 | + | ||
135 | + pub fn apply(&self, v :&Vector<T>, w :T) -> (Vector<T>, T) { | ||
136 | + let TMatrix( (a11, a12, a13, a14) | ||
137 | + , (a21, a22, a23, a24) | ||
138 | + , (a31, a32, a33, a34) | ||
139 | + , (a41, a42, a43, a44) ) = *self; | ||
140 | + let Vector(x, y, z) = *v; | ||
141 | + | ||
142 | + let v = Vector( a11 * x + a12 * y + a13 * z + a14 * w | ||
143 | + , a21 * x + a22 * y + a23 * z + a24 * w | ||
144 | + , a31 * x + a32 * y + a33 * z + a34 * w ); | ||
145 | + let w = a41 * x + a42 * y + a43 * z + a44 * w; | ||
146 | + | ||
147 | + //v.mul(&w.recip()) | ||
148 | + (v, w) | ||
149 | + } | ||
150 | +} | ||
151 | + | ||
152 | +impl<T> Mul for TMatrix<T> | ||
153 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
154 | + + Mul<Output = T> + Div<Output = T> | ||
155 | + + Debug + Trig + From<i32> + Copy { | ||
156 | + type Output = Self; | ||
157 | + | ||
158 | + // ATTENTION: This is not commutative, nor assoziative. | ||
159 | + fn mul(self, other :Self) -> Self { | ||
160 | + let TMatrix( (a11, a12, a13, a14) | ||
161 | + , (a21, a22, a23, a24) | ||
162 | + , (a31, a32, a33, a34) | ||
163 | + , (a41, a42, a43, a44) ) = self; | ||
164 | + let TMatrix( (b11, b12, b13, b14) | ||
165 | + , (b21, b22, b23, b24) | ||
166 | + , (b31, b32, b33, b34) | ||
167 | + , (b41, b42, b43, b44) ) = other; | ||
168 | + | ||
169 | + TMatrix( ( a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41 | ||
170 | + , a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42 | ||
171 | + , a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43 | ||
172 | + , a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44 ) | ||
173 | + , ( a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41 | ||
174 | + , a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42 | ||
175 | + , a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43 | ||
176 | + , a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44 ) | ||
177 | + , ( a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41 | ||
178 | + , a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42 | ||
179 | + , a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43 | ||
180 | + , a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44 ) | ||
181 | + , ( a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41 | ||
182 | + , a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42 | ||
183 | + , a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43 | ||
184 | + , a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44 ) ) | ||
185 | + } | ||
186 | +} |
1 | +// | ||
2 | +// Some trigonometic functions with Fractions results. | ||
3 | +// Currently only sin, cos and tan are implemented. | ||
4 | +// As I was unable to find a really good integral approximation for them I | ||
5 | +// implement them as a table which is predefined using the floating point | ||
6 | +// function f64::sin and then transformed into a fraction of a given | ||
7 | +// PRECISION. | ||
8 | +// These approximations are quite good and for a few edge cases | ||
9 | +// even better than the floating point implementations. | ||
10 | +// | ||
11 | +// Georg Hopp <georg@steffers.org> | ||
12 | +// | ||
13 | +// Copyright © 2019 Georg Hopp | ||
14 | +// | ||
15 | +// This program is free software: you can redistribute it and/or modify | ||
16 | +// it under the terms of the GNU General Public License as published by | ||
17 | +// the Free Software Foundation, either version 3 of the License, or | ||
18 | +// (at your option) any later version. | ||
19 | +// | ||
20 | +// This program is distributed in the hope that it will be useful, | ||
21 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | +// GNU General Public License for more details. | ||
24 | +// | ||
25 | +// You should have received a copy of the GNU General Public License | ||
26 | +// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
27 | +// | ||
28 | +use std::ops::Div; | ||
29 | +use std::ops::Neg; | ||
30 | +use std::marker::Sized; | ||
31 | +use crate::Error; | ||
32 | + | ||
33 | +pub trait Trig { | ||
34 | + fn pi() -> Self; | ||
35 | + fn recip(self) -> Self; | ||
36 | + fn round(&self) -> i32; | ||
37 | + fn sqrt(self) -> Result<Self, Error> where Self: Sized; | ||
38 | + fn sintab() -> Vec<Self> where Self: Sized; | ||
39 | + fn tantab() -> Vec<Self> where Self: Sized; | ||
40 | + | ||
41 | + fn sin(d :i32) -> Self | ||
42 | + where Self: Sized + Neg<Output = Self> + Copy { | ||
43 | + match d { | ||
44 | + 0 ..=90 => Self::sintab()[d as usize], | ||
45 | + 91 ..=180 => Self::sintab()[180 - d as usize], | ||
46 | + 181..=270 => -Self::sintab()[d as usize - 180], | ||
47 | + 271..=359 => -Self::sintab()[360 - d as usize], | ||
48 | + _ => { | ||
49 | + Self::sin(if d < 0 { d % 360 + 360 } else { d % 360 }) | ||
50 | + }, | ||
51 | + } | ||
52 | + } | ||
53 | + | ||
54 | + fn cos(d :i32) -> Self | ||
55 | + where Self: Sized + Neg<Output = Self> + Copy { | ||
56 | + match d { | ||
57 | + 0 ..=90 => Self::sintab()[90 - d as usize], | ||
58 | + 91 ..=180 => -Self::sintab()[90 - (180 - d as usize)], | ||
59 | + 181..=270 => -Self::sintab()[90 - (d as usize - 180)], | ||
60 | + 271..=359 => Self::sintab()[90 - (360 - d as usize)], | ||
61 | + _ => { | ||
62 | + Self::cos(if d < 0 { d % 360 + 360 } else { d % 360 }) | ||
63 | + }, | ||
64 | + } | ||
65 | + } | ||
66 | + | ||
67 | + fn tan(d :i32) -> Self where Self: Sized + Copy { | ||
68 | + match d { | ||
69 | + 0 ..=179 => Self::tantab()[d as usize], | ||
70 | + 180..=359 => Self::tantab()[d as usize - 180], | ||
71 | + _ => { | ||
72 | + Self::tan(if d < 0 { d % 360 + 360 } else { d % 360 }) | ||
73 | + }, | ||
74 | + } | ||
75 | + } | ||
76 | + | ||
77 | + fn cot(d :i32) -> Self | ||
78 | + where Self: Sized + Copy + From<i32> + Div<Output = Self> { | ||
79 | + Into::<Self>::into(1) / Self::tan(d) | ||
80 | + } | ||
81 | +} | ||
82 | + | ||
83 | +impl Trig for f64 { | ||
84 | + fn pi() -> Self { | ||
85 | + std::f64::consts::PI | ||
86 | + } | ||
87 | + | ||
88 | + fn recip(self) -> Self { | ||
89 | + self.recip() | ||
90 | + } | ||
91 | + | ||
92 | + fn round(&self) -> i32 { | ||
93 | + f64::round(*self) as i32 | ||
94 | + } | ||
95 | + | ||
96 | + fn sqrt(self) -> Result<Self, Error> { | ||
97 | + let x = self.sqrt(); | ||
98 | + match x.is_nan() { | ||
99 | + true => Err("sqrt on negative undefined"), | ||
100 | + false => Ok(x), | ||
101 | + } | ||
102 | + } | ||
103 | + | ||
104 | + fn sintab() -> Vec<Self> { | ||
105 | + lazy_static::lazy_static! { | ||
106 | + static ref SINTAB :Vec<f64> = | ||
107 | + (0..=90).map(|x| _sin(x)).collect(); | ||
108 | + } | ||
109 | + | ||
110 | + // f64 sin. (From 0° to 90°) | ||
111 | + fn _sin(d: u32) -> f64 { | ||
112 | + match d { | ||
113 | + 0 => 0.0, | ||
114 | + 90 => 1.0, | ||
115 | + _ => (d as f64).to_radians().sin(), | ||
116 | + } | ||
117 | + } | ||
118 | + | ||
119 | + SINTAB.to_vec() | ||
120 | + } | ||
121 | + | ||
122 | + fn tantab() -> Vec<Self> { | ||
123 | + // This table exists only because the sin(α) / cos(α) method | ||
124 | + // yields very large unreducable denominators in a lot of cases. | ||
125 | + lazy_static::lazy_static! { | ||
126 | + static ref TANTAB :Vec<f64> = | ||
127 | + (0..180).map(|x| _tan(x)).collect(); | ||
128 | + } | ||
129 | + | ||
130 | + // fractional tan from f64 tan. (From 0° to 179°) | ||
131 | + fn _tan(d: u32) -> f64 { | ||
132 | + match d { | ||
133 | + 0 => 0.0, | ||
134 | + 45 => 1.0, | ||
135 | + 90 => std::f64::INFINITY, | ||
136 | + 135 => -1.0, | ||
137 | + _ => (d as f64).to_radians().tan(), | ||
138 | + } | ||
139 | + } | ||
140 | + | ||
141 | + TANTAB.to_vec() | ||
142 | + } | ||
143 | +} |
tutorial/wasm-game-of-life/src/vector.rs
0 → 100644
1 | +// | ||
2 | +// Stuff for manipulating 3 dimensional vectors. | ||
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::fmt::{Debug, Display, Formatter, Result}; | ||
22 | +use std::ops::{Add, Sub, Neg, Mul, Div}; | ||
23 | + | ||
24 | +use crate::trigonometry::Trig; | ||
25 | +use crate::transform::{TMatrix, Transformable}; | ||
26 | + | ||
27 | +#[derive(Debug, Eq, Clone, Copy)] | ||
28 | +pub struct Vector<T>(pub T, pub T, pub T) | ||
29 | + where T: Add + Sub + Neg + Mul + Div + Trig + Copy; | ||
30 | + | ||
31 | +impl<T> Vector<T> | ||
32 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
33 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
34 | + pub fn x(self) -> T { self.0 } | ||
35 | + pub fn y(self) -> T { self.1 } | ||
36 | + pub fn z(self) -> T { self.2 } | ||
37 | + | ||
38 | + pub fn mag(self) -> T { | ||
39 | + let Vector(x, y, z) = self; | ||
40 | + (x * x + y * y + z * z).sqrt().unwrap() | ||
41 | + } | ||
42 | + | ||
43 | + pub fn mul(self, s :&T) -> Self { | ||
44 | + let Vector(x, y, z) = self; | ||
45 | + Vector(x * *s, y * *s, z * *s) | ||
46 | + } | ||
47 | + | ||
48 | + pub fn dot(self, other :Self) -> T { | ||
49 | + let Vector(x1, y1, z1) = self; | ||
50 | + let Vector(x2, y2, z2) = other; | ||
51 | + | ||
52 | + x1 * x2 + y1 * y2 + z1 * z2 | ||
53 | + } | ||
54 | + | ||
55 | + pub fn norm(self) -> Self { | ||
56 | + // TODO This can result in 0 or inf Vectors… | ||
57 | + // Maybe we need to handle zero and inf magnitude here… | ||
58 | + self.mul(&self.mag().recip()) | ||
59 | + } | ||
60 | + | ||
61 | + pub fn distance(self, other :Self) -> T { | ||
62 | + (self - other).mag() | ||
63 | + } | ||
64 | +} | ||
65 | + | ||
66 | +impl<T> Display for Vector<T> | ||
67 | +where T: Add + Sub + Neg + Mul + Div + Trig + Display + Copy { | ||
68 | + fn fmt(&self, f :&mut Formatter<'_>) -> Result { | ||
69 | + let Vector(x, y, z) = self; | ||
70 | + write!(f, "({}, {}, {})", x, y, z) | ||
71 | + } | ||
72 | +} | ||
73 | + | ||
74 | +impl<T> PartialEq for Vector<T> | ||
75 | +where T: Add + Sub + Neg + Mul + Div + Trig + PartialEq + Copy { | ||
76 | + fn eq(&self, other :&Self) -> bool { | ||
77 | + let Vector(x1, y1, z1) = self; | ||
78 | + let Vector(x2, y2, z2) = other; | ||
79 | + x1 == x2 && y1 == y2 && z1 == z2 | ||
80 | + } | ||
81 | +} | ||
82 | + | ||
83 | +impl<T> Add for Vector<T> | ||
84 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
85 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
86 | + type Output = Self; | ||
87 | + | ||
88 | + fn add(self, other :Self) -> Self { | ||
89 | + let Vector(x1, y1, z1) = self; | ||
90 | + let Vector(x2, y2, z2) = other; | ||
91 | + Vector(x1 + x2, y1 + y2, z1 + z2) | ||
92 | + } | ||
93 | +} | ||
94 | + | ||
95 | +impl<T> Sub for Vector<T> | ||
96 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
97 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
98 | + type Output = Self; | ||
99 | + | ||
100 | + fn sub(self, other :Self) -> Self { | ||
101 | + self + -other | ||
102 | + } | ||
103 | +} | ||
104 | + | ||
105 | +impl<T> Neg for Vector<T> | ||
106 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
107 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
108 | + type Output = Self; | ||
109 | + | ||
110 | + fn neg(self) -> Self { | ||
111 | + let Vector(x, y, z) = self; | ||
112 | + Self(-x, -y, -z) | ||
113 | + } | ||
114 | +} | ||
115 | + | ||
116 | +impl<T> Mul for Vector<T> | ||
117 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
118 | + + Mul<Output = T> + Div<Output = T> + Trig + Copy { | ||
119 | + type Output = Self; | ||
120 | + | ||
121 | + fn mul(self, other :Self) -> Self { | ||
122 | + let Vector(ax, ay, az) = self; | ||
123 | + let Vector(bx, by, bz) = other; | ||
124 | + | ||
125 | + Vector( ay * bz - az * by | ||
126 | + , az * bx - ax * bz | ||
127 | + , ax * by - ay * bx ) | ||
128 | + } | ||
129 | +} | ||
130 | + | ||
131 | +impl<T> Transformable<T> for Vector<T> | ||
132 | +where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
133 | + + Mul<Output = T> + Div<Output = T> | ||
134 | + + Trig + Copy + Debug + From<i32> { | ||
135 | + fn transform(&self, m :&TMatrix<T>) -> Self { | ||
136 | + let (v, _) = m.apply(self, 0.into()); | ||
137 | + v | ||
138 | + } | ||
139 | +} |
@@ -18,6 +18,7 @@ | @@ -18,6 +18,7 @@ | ||
18 | </style> | 18 | </style> |
19 | </head> | 19 | </head> |
20 | <body> | 20 | <body> |
21 | + <canvas id="view3d"></canvas> | ||
21 | <canvas id="game-of-life-canvas"></canvas> | 22 | <canvas id="game-of-life-canvas"></canvas> |
22 | <script src="./bootstrap.js"></script> | 23 | <script src="./bootstrap.js"></script> |
23 | </body> | 24 | </body> |
1 | -import { Universe, Cell } from "wasm-game-of-life"; | 1 | +import { Universe, Cell, View3d, Color } from "wasm-game-of-life"; |
2 | import { memory } from "wasm-game-of-life/wasm_game_of_life_bg"; | 2 | import { memory } from "wasm-game-of-life/wasm_game_of_life_bg"; |
3 | 3 | ||
4 | +// 3D canvas stuff | ||
5 | +const view3d = View3d.new(151, 151); | ||
6 | + | ||
7 | +const view3d_canvas = document.getElementById("view3d"); | ||
8 | +view3d_canvas.width = view3d.width(); | ||
9 | +view3d_canvas.width = view3d.height(); | ||
10 | + | ||
11 | +const view3d_ctx = view3d_canvas.getContext('2d'); | ||
12 | + | ||
13 | +const view3d_renderLoop = () => { | ||
14 | + view3d.update(); | ||
15 | + requestAnimationFrame(view3d_renderLoop); | ||
16 | +} | ||
17 | + | ||
18 | +// game of life stuff | ||
4 | const CELL_SIZE = 5; // px | 19 | const CELL_SIZE = 5; // px |
5 | const GRID_COLOR = "#CCCCCC"; | 20 | const GRID_COLOR = "#CCCCCC"; |
6 | const DEAD_COLOR = "#FFFFFF"; | 21 | const DEAD_COLOR = "#FFFFFF"; |
7 | const ALIVE_COLOR = "#000000"; | 22 | const ALIVE_COLOR = "#000000"; |
8 | 23 | ||
9 | const universe = Universe.new(); | 24 | const universe = Universe.new(); |
10 | -const width = universe.height(); | 25 | +const width = universe.width(); |
11 | const height = universe.height(); | 26 | const height = universe.height(); |
12 | 27 | ||
13 | const canvas = document.getElementById("game-of-life-canvas"); | 28 | const canvas = document.getElementById("game-of-life-canvas"); |
@@ -74,6 +89,8 @@ const drawCells = () => { | @@ -74,6 +89,8 @@ const drawCells = () => { | ||
74 | ctx.stroke(); | 89 | ctx.stroke(); |
75 | }; | 90 | }; |
76 | 91 | ||
92 | +// start everything ... | ||
77 | drawGrid(); | 93 | drawGrid(); |
78 | drawCells(); | 94 | drawCells(); |
79 | requestAnimationFrame(renderLoop); | 95 | requestAnimationFrame(renderLoop); |
96 | +requestAnimationFrame(view3d_renderLoop); |
Please
register
or
login
to post a comment