Commit a0c638c5575fd5b43dde1ecfc4b1e7b7ec1c990e

Authored by Georg Hopp
1 parent c7543ff2

Start implement 3d from fractional as html5 canvas

... ... @@ -12,6 +12,7 @@ default = ["console_error_panic_hook"]
12 12
13 13 [dependencies]
14 14 wasm-bindgen = "0.2"
  15 +lazy_static = "1.4.0"
15 16
16 17 # The `console_error_panic_hook` crate provides better debugging of panics by
17 18 # logging them with `console.error`. This is great for development, but requires
... ...
  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 +}
... ...
  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 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 18 use std::fmt::{Display, Formatter, Result};
  19 +use std::ptr;
  20 +use std::sync::mpsc;
  21 +use std::time::Instant;
4 22 use wasm_bindgen::prelude::*;
5 23
6 24 // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
... ... @@ -10,6 +28,117 @@ use wasm_bindgen::prelude::*;
10 28 static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
11 29
12 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 142 #[repr(u8)]
14 143 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
15 144 pub enum Cell {
... ...
  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 +}
... ...
  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 18 </style>
19 19 </head>
20 20 <body>
  21 + <canvas id="view3d"></canvas>
21 22 <canvas id="game-of-life-canvas"></canvas>
22 23 <script src="./bootstrap.js"></script>
23 24 </body>
... ...
1   -import { Universe, Cell } from "wasm-game-of-life";
  1 +import { Universe, Cell, View3d, Color } from "wasm-game-of-life";
2 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 19 const CELL_SIZE = 5; // px
5 20 const GRID_COLOR = "#CCCCCC";
6 21 const DEAD_COLOR = "#FFFFFF";
7 22 const ALIVE_COLOR = "#000000";
8 23
9 24 const universe = Universe.new();
10   -const width = universe.height();
  25 +const width = universe.width();
11 26 const height = universe.height();
12 27
13 28 const canvas = document.getElementById("game-of-life-canvas");
... ... @@ -74,6 +89,8 @@ const drawCells = () => {
74 89 ctx.stroke();
75 90 };
76 91
  92 +// start everything ...
77 93 drawGrid();
78 94 drawCells();
79 95 requestAnimationFrame(renderLoop);
  96 +requestAnimationFrame(view3d_renderLoop);
... ...
Please register or login to post a comment