From 8f79b4d2a8c62271b06fd9f959d01d4d13ff6c9e Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Sun, 12 Jan 2020 19:26:00 +0100 Subject: [PATCH] use easel3d crate now --- Cargo.toml | 20 +- README.md | 121 ++------- src/easel.rs | 520 ------------------------------------ src/easel3d_wasm.rs | 68 +++++ src/geometry.rs | 377 -------------------------- src/lib.rs | 245 +++-------------- src/transform.rs | 186 ------------- src/trigonometry.rs | 143 ---------- src/utils.rs | 10 - src/vector.rs | 139 ---------- www/.bin/create-wasm-app.js | 24 -- www/README.md | 67 ----- www/index.html | 2 +- www/index.js | 7 +- www/package-lock.json | 14 +- www/package.json | 22 +- 16 files changed, 144 insertions(+), 1821 deletions(-) delete mode 100644 src/easel.rs create mode 100644 src/easel3d_wasm.rs delete mode 100644 src/geometry.rs delete mode 100644 src/transform.rs delete mode 100644 src/trigonometry.rs delete mode 100644 src/utils.rs delete mode 100644 src/vector.rs delete mode 100755 www/.bin/create-wasm-app.js delete mode 100644 www/README.md diff --git a/Cargo.toml b/Cargo.toml index dcf18a8..6b1df11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasm-game-of-life" +name = "easel3d-wasm" version = "0.1.0" authors = ["hopp@silpion.de"] edition = "2018" @@ -7,29 +7,13 @@ edition = "2018" [lib] crate-type = ["cdylib", "rlib"] -[features] -default = ["console_error_panic_hook"] - [dependencies] wasm-bindgen = "0.2" -lazy_static = "1.4.0" - -# The `console_error_panic_hook` crate provides better debugging of panics by -# logging them with `console.error`. This is great for development, but requires -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for -# code size when deploying. -console_error_panic_hook = { version = "0.1.1", optional = true } - -# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size -# compared to the default allocator's ~10K. It is slower than the default -# allocator, however. -# -# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +easel3d = { path = "../easel3d" } wee_alloc = { version = "0.4.2", optional = true } [dev-dependencies] wasm-bindgen-test = "0.2" [profile.release] -# Tell `rustc` to optimize for small code size. opt-level = "s" diff --git a/README.md b/README.md index 2dab778..469f52c 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,45 @@ -
+# Easel3D-WASM -

wasm-pack-template

- - A template for kick starting a Rust and WebAssembly project using wasm-pack. - -

- Build Status -

- -

- Tutorial - | - Chat -

- - Built with 🦀🕸 by The Rust and WebAssembly Working Group -
- -## About - -[**📚 Read this template tutorial! 📚**][template-docs] - -This template is designed for compiling Rust libraries into WebAssembly and -publishing the resulting package to NPM. - -Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other -templates and usages of `wasm-pack`. - -[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html -[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html - -## 🚴 Usage - -### 🐑 Use `cargo generate` to Clone this Template - -[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) - -``` -cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project -cd my-project -``` - -### 🛠️ Build with `wasm-pack build` - -``` -wasm-pack build -``` - -### 🔬 Test in Headless Browsers with `wasm-pack test` - -``` -wasm-pack test --headless --firefox -``` - -### 🎁 Publish to NPM with `wasm-pack publish` - -``` -wasm-pack publish -``` - -## 🔋 Batteries Included - -* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating - between WebAssembly and JavaScript. -* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) - for logging panic messages to the developer console. -* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized - for small code size. -# Rust playground - -Things I have recently done while learning the Rust programming language. +WebAssembly demo application using Easel3D... ## Synopsis -Change in one of the toplevel subdirectories and try `cargo build` or -`cargo run`. Maybe not everything is working oob. Feel free to fix whatever -you want. +Checkout this repository as well as the _easel3d_ repository to the same +destination directory. -## Description +In the root of this repository call `wasm-pack build`. +Then change to www and call `npm install`. -Various small examples I have tried while learning rust. The biggest and -currently most active project is **fractional** which started as an -implamentation of a rational number data type and then switched to a 3D -math playground visualizing using **XCB** (in future it might also use -a **HTML5 Canvas** for drawing as WebAssembly application. -Using fractions with 3D math has several drawbacks: +To test the stuff call `npm run start` from www directory and in your browser +open http:://localhost:8080/. -1. A huge part of 3D math is non rational, like sin, cos, tan and sqrt. -2. The numerator and denominator tend to become very huge while nearing to non - rational numbers and reduction is difficult and time consuming. -3. Because of 2 it is way slower than the floating point calculation (at least - with a decent coprocessor). +## Description -Anyway, implementing the vector math stuff for both fractions and floating -point was a nice playground for generics and traits. In future I might add -another data type which implements the math as done by David Braben for the -elite computer game. +A demo application using easel3d to draw in an HTML5 canvas element in a web +page. ## Requirements -### Always - - A recent version of the Rust programming language as well as tooling. Currently I use Rust 1.39.0. +- wasm-pack to build to wasm target +- npm for Javascript code. +- A browser capable of executing WebAssembly. -### For fractional +## Dependencies -- A running X Server with **XCB** and **X11-SHM** extentions +### Rust crates. -## Dependencies +- easel3d (from parent directory) +- wasm-bindgen =0.2 +- wee-alloc =0.4.2 (optional) + +Along with the dependencies of the external crates. `wasm-pack build` should +take care of having them available. + +### Javascript -... +Various things npm installs. ## Contributing diff --git a/src/easel.rs b/src/easel.rs deleted file mode 100644 index c9a3f7e..0000000 --- a/src/easel.rs +++ /dev/null @@ -1,520 +0,0 @@ -// -// This is an abstraction over a drawing environment. -// Future note: z-Buffer is described here: -// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::cmp; -use std::fmt::{Formatter, Debug, Display, Result}; -use std::ops::{Add, Sub, Div}; -use std::sync::mpsc; - -pub trait Easel { - //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; -} - -pub trait Canvas { - fn init_events(&self); - fn start_events(&self, tx :mpsc::Sender); - - fn width(&self) -> u16; - fn height(&self) -> u16; - - fn clear(&mut self); - fn draw(&mut self, c :&dyn Drawable, ofs :Coordinate, color :u32); - fn put_text(&self, ofs :Coordinate, s :&str); - fn set_pixel(&mut self, c :Coordinate, color :u32); - fn show(&self); -} - -pub trait Drawable { - fn plot(&self) -> Coordinates; -} - -pub trait Fillable -where T: Add + Sub + Div - + Debug + Copy + From { - fn fill(&self, canvas :&mut dyn Canvas, color :u32); -} - -#[derive(Debug, Clone, Copy)] -pub struct Coordinate(pub i32, pub i32, pub T); - -#[derive(Debug, Clone)] -pub struct Coordinates(pub Vec>); - -#[derive(Debug, Clone, Copy)] -pub struct LineIterator where T: Debug { - a :Option> - , b :Coordinate - , dx :i32 - , dy :i32 - , dz :T - , sx :i32 - , sy :i32 - , err :i32 - , only_edges :bool -} - -impl Iterator for LineIterator -where T: Add + Debug + Copy + From { - type Item = Coordinate; - - fn next(&mut self) -> Option { - match self.a { - None => None, - Some(a) => { - let Coordinate(ax, ay, az) = a; - let Coordinate(bx, by, _) = self.b; - - if ax != bx || ay != by { - match (2 * self.err >= self.dy, 2 * self.err <= self.dx ) { - (true, false) => { - let r = self.a; - self.a = Some(Coordinate( ax + self.sx - , ay - , az + self.dz )); - self.err = self.err + self.dy; - if self.only_edges { self.next() } else { r } - }, - (false, true) => { - let r = self.a; - self.a = Some(Coordinate( ax - , ay + self.sy - , az + self.dz )); - self.err = self.err + self.dx; - r - }, - _ => { - let r = self.a; - self.a = Some(Coordinate( ax + self.sx - , ay + self.sy - , az + self.dz )); - self.err = self.err + self.dx + self.dy; - r - }, - } - } else { - self.a = None; - Some(self.b) - } - } - } - } -} - -impl Coordinate -where T: Add + Sub + Div - + Debug + Clone + Copy + From { - fn iter(self, b :&Self, only_edges :bool) -> LineIterator { - let Coordinate(ax, ay, az) = self; - let Coordinate(bx, by, bz) = *b; - - let dx = (bx - ax).abs(); - let dy = -(by - ay).abs(); - - LineIterator { a: Some(self) - , b: *b - , dx: dx - , dy: dy - , dz: (bz - az) / cmp::max(dx, -dy).into() - , sx: if ax < bx { 1 } else { -1 } - , sy: if ay < by { 1 } else { -1 } - , err: dx + dy - , only_edges: only_edges - } - } - - fn line_iter(self, b :&Self) -> LineIterator { - self.iter(b, false) - } - - fn line(self, b :&Self) -> Vec { - self.line_iter(b).collect() - } - - fn edge_iter(self, b :&Self) -> LineIterator { - self.iter(b, true) - } - - fn edge(self, b :&Self) -> Vec { - self.edge_iter(b).collect() - } - - fn face(edges :&[Self]) -> Vec { - edges.to_vec() - } -} - -impl Display for Coordinate { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "<{},{}>", self.0, self.1) - } -} - -impl Display for Coordinates where T: Copy { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let Coordinates(is) = self; - - let c = match is[..] { - [] => String::from(""), - [a] => format!("{}", a), - _ => { - let mut a = format!("{}", is[0]); - for i in is[1..].iter() { - a = a + &format!(",{}", i); - } - a - } - }; - - write!(f, "Coordinates[{}]", c) - } -} - - -#[derive(Debug, Clone, Copy)] -pub struct Point(pub Coordinate); - -impl Drawable for Point where T: Copy { - fn plot(&self) -> Coordinates { - let Point(c) = *self; - Coordinates(vec!(c)) - } -} - -impl Display for Point { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let Point(p) = self; - write!(f, "Point[{}]", p) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Line(pub Coordinate, pub Coordinate); - -impl Drawable for Line -where T: Add + Sub + Div - + Debug + Clone + Copy + From { - fn plot(&self) -> Coordinates { - let Line(a, b) = *self; - Coordinates(a.line(&b)) - } -} - -impl Display for Line { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let Line(a, b) = self; - write!(f, "Line[{},{}]", a, b) - } -} - -#[derive(Debug, Clone)] -pub struct Polyline(pub Coordinates); - -impl Drawable for Polyline -where T: Add + Sub + Div - + Debug + Clone + Copy + From { - fn plot(&self) -> Coordinates { - let Polyline(Coordinates(cs)) = self; - - match cs[..] { - [] => Coordinates(Vec::>::new()), - [a] => Coordinates(vec!(a)), - [a, b] => Coordinates(a.line(&b)), - _ => { - let (a, b) = (cs[0], cs[1]); - let mut r = a.line(&b); - let mut i = b; - for j in cs[2..].iter() { - r.append(&mut i.line(j)[1..].to_vec()); - i = *j; - } - Coordinates(r) - }, - } - } -} - -impl Display for Polyline where T: Copy { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let Polyline(a) = self; - write!(f, "PLine[{}]", a) - } -} - -#[derive(Debug, Clone, Copy)] -enum Direction { Left, Right } - -#[derive(Debug, Clone)] -pub struct Polygon(pub Coordinates); - -#[derive(Debug, Clone)] -enum VertexIteratorMode { Vertex, Edge } -#[derive(Debug, Clone)] -pub struct VertexIterator<'a,T> where T: Debug { - p :&'a Polygon, - top :usize, - current :Option, - edge :Option>, - mode :VertexIteratorMode, - direction :Direction, -} - -impl<'a,T> VertexIterator<'a,T> -where T: Add + Sub + Div - + Debug + Copy + From { - fn edge(p :&'a Polygon, direction :Direction) -> Self { - let top = p.vert_min(direction); - let next = p.next_y(top, direction); - let edge = match next { - None => None, - Some(next) => Some(p.vertex(top).edge_iter(&p.vertex(next))), - }; - - VertexIterator { p: p - , top: top - , current: next - , edge: edge - , mode: VertexIteratorMode::Edge - , direction: direction } - } - - fn vertex(p :&'a Polygon, direction :Direction) -> Self { - let top = p.vert_min(direction); - let next = p.next_y(top, direction); - - VertexIterator { p: p - , top: top - , current: next - , edge: None - , mode: VertexIteratorMode::Vertex - , direction: direction } - } - - // if this yields "None" we are finished. - fn next_edge(&mut self) -> Option> { - let current = self.current?; - let next = self.p.next_y(current, self.direction)?; - let mut edge = self.p.vertex(current).edge_iter(&self.p.vertex(next)); - - match edge.next() { - // It should be impossible that a new edge iterator has no values - // at all… anyway, just in case I handle it here. - None => self.next_edge(), - Some(_) => { - self.current = Some(next); - self.edge = Some(edge); - self.edge - }, - } - } -} - -impl<'a,T> Iterator for VertexIterator<'a,T> -where T: Add + Sub + Div - + Debug + Copy + From { - type Item = Coordinate; - - fn next(&mut self) -> Option { - match self.mode { - VertexIteratorMode::Edge => { - // if for whatever reason edge is "None" finish this iterator. - let next = self.edge.as_mut()?.next(); - - match next { - Some(_) => next, - None => { - self.next_edge()?; - self.next() - }, - } - }, - VertexIteratorMode::Vertex => { - let current = self.current?; - self.current = self.p.next_y(current, self.direction); - Some(self.p.vertex(current)) - }, - } - } -} - -impl Polygon -where T: Add + Sub + Div - + Copy + Debug + From { - #[inline] - fn vertex(&self, v :usize) -> Coordinate { - let Polygon(Coordinates(cs)) = self; - cs[v] - } - - fn vert_min<'a>(&'a self, d :Direction) -> usize { - let Polygon(Coordinates(cs)) = self; - - type ICoord<'a,T> = (usize, &'a Coordinate); - - // TODO I guess the problem here is that it does not account for the - // same y vertex on the beggining and the end. So i guess correct - // would be finding the first one and then dependings on the - // given direction either search left or right for same y's. - let fold = |acc :Option>, x :ICoord<'a,T>| - match acc { - None => Some(x), - Some(a) => { - let Coordinate(_, ay, _) = a.1; - let Coordinate(_, xy, _) = x.1; - if xy < ay {Some(x)} else {Some(a)} - }, - }; - - let mut min = cs.iter().enumerate().fold(None, fold).unwrap().0; - let mut next = self.step(min, d); - - while self.vertex(min).1 == self.vertex(next).1 { - min = next; - next = self.step(min, d); - } - - min - } - - fn left_edge(&self) -> VertexIterator { - VertexIterator::edge(self, Direction::Left) - } - - fn right_edge(&self) -> VertexIterator { - VertexIterator::edge(self, Direction::Right) - } - - fn left_vertices(&self) -> VertexIterator { - VertexIterator::vertex(self, Direction::Left) - } - - fn right_vertices(&self) -> VertexIterator { - VertexIterator::vertex(self, Direction::Right) - } - - fn left(&self, v :usize) -> usize { - let Polygon(Coordinates(cs)) = self; - - match v { - 0 => cs.len() - 1, - _ => v - 1, - } - } - - fn right(&self, v :usize) -> usize { - let Polygon(Coordinates(cs)) = self; - - (v + 1) % cs.len() - } - - fn step(&self, v :usize, d :Direction) -> usize { - match d { - Direction::Left => self.left(v), - Direction::Right => self.right(v), - } - } - - fn next_y(&self, c :usize, d :Direction) -> Option { - fn inner( p :&Polygon - , c :usize - , n :usize - , d :Direction) -> Option - where T: Add + Sub + Div - + Copy + Debug + From { - if c == n { - None - } else { - let Coordinate(_, cy, _) = p.vertex(c); - let Coordinate(_, ny, _) = p.vertex(n); - - if ny < cy { None } else { Some(n) } - } - } - - inner(self, c, self.step(c, d), d) - } - - pub fn debug(&self) { - let mut left = self.left_vertices(); - let mut right = self.right_vertices(); - - if left.find(|l| right.find(|r| l.0 == r.0).is_some()).is_some() { - let left :Vec> = self.left_vertices().collect(); - let right :Vec> = self.right_vertices().collect(); - - println!("==="); - println!("== poly : {:?}", self); - println!("== ltop : {:?}", self.vert_min(Direction::Left)); - println!("== rtop : {:?}", self.vert_min(Direction::Right)); - println!("== left : {:?}", left); - println!("== right : {:?}", right); - println!("==="); - } - } -} - -impl Drawable for Polygon -where T: Add + Sub + Div - + Debug + Clone + Copy + From { - fn plot(&self) -> Coordinates { - let Polygon(Coordinates(cs)) = self; - - match cs[..] { - [] => Coordinates(Vec::>::new()), - [a] => Coordinates(vec!(a)), - [a, b] => Coordinates(a.line(&b)), - _ => { - let (a, b) = (cs[0], cs[1]); - let mut r = a.line(&b); - let mut i = b; - for j in cs[2..].iter() { - r.append(&mut i.line(j)[1..].to_vec()); - i = *j; - } - let mut j = a.line(&i); - let l = j.len(); - if l > 1 { - r.append(&mut j[1..l-1].to_vec()); - } - Coordinates(r) - }, - } - } -} - -impl Fillable for Polygon -where T: Add + Sub + Div - + Debug + Clone + Copy + From { - fn fill(&self, canvas :&mut dyn Canvas, color :u32) { - let scanlines = self.left_edge().zip(self.right_edge()); - - for l in scanlines.flat_map(|(l, r)| l.line_iter(&r)) { - canvas.set_pixel(l, color); - } - } -} - -impl Display for Polygon where T: Copy { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let Polygon(a) = self; - write!(f, "Poly[{}]", a) - } -} diff --git a/src/easel3d_wasm.rs b/src/easel3d_wasm.rs new file mode 100644 index 0000000..3946c4d --- /dev/null +++ b/src/easel3d_wasm.rs @@ -0,0 +1,68 @@ +use std::sync::mpsc; + +use easel3d::easel::canvas::{Canvas, Vertex}; +use easel3d::easel::drawable::Drawable; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Color(u8, u8, u8, u8); + +pub struct WasmCanvas { width :u16 + , height :u16 + , size :usize + , zbuf :Vec + , image :Vec } + +impl WasmCanvas { + pub fn new(width :u16, height :u16) -> Self { + let size = width as usize * height as usize; + + Self { width: width + , height: height + , size: size + , zbuf: vec!(0.0; size) + , image: vec!(Color(0, 0, 0, 0xFF); size) } + } + + pub fn image(&self) -> *const Color { + self.image.as_ptr() + } +} + +impl Canvas for WasmCanvas { + #[inline] + fn width(&self) -> u16 { + self.width + } + + #[inline] + fn height(&self) -> u16 { + self.height + } + + fn clear(&mut self) { + self.zbuf = vec!(0.0; self.size); + self.image = vec!(Color(0, 0, 0, 0xFF); self.size); + } + + fn set_pixel(&mut self, c :Vertex, color :u32) { + let (x, y, zr) = c.as_tuple(); + let idx :usize = (y * (self.width as i32) + x) as usize; + + let r = ((color >> 16) & 0xFF) as u8; + let g = ((color >> 8) & 0xFF) as u8; + let b = ( color & 0xFF) as u8; + + if self.zbuf[idx] < zr { + self.zbuf[idx] = zr; + self.image[idx] = Color(r, g, b, 0xFF); + } + } + + // Empty implementations for now… mostly not needed because it is + // done from JavaScript… + fn init_events(&self) {} + fn start_events(&self, _ :mpsc::Sender) {} + fn draw(&mut self, _ :&dyn Drawable, _ :u32) {} + fn put_text(&self, _ :Vertex, _ :&str) {} + fn show(&self) {} +} diff --git a/src/geometry.rs b/src/geometry.rs deleted file mode 100644 index 0320378..0000000 --- a/src/geometry.rs +++ /dev/null @@ -1,377 +0,0 @@ -// -// Basic geometric things... -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::convert::{From, Into}; -use std::ops::{Add,Sub,Neg,Mul,Div}; -use std::fmt::Debug; - -use crate::easel::{Canvas, Coordinate, Coordinates, Polygon}; -use crate::transform::{TMatrix, Transformable}; -use crate::trigonometry::Trig; -use crate::vector::Vector; - -#[derive(Debug, Clone)] -pub struct Face -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { - corners :Vec, - normal :Option>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct Point(pub Vector, T) - where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig; - -impl Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy + From { - pub fn new(x :T, y :T, z :T) -> Self { - Self(Vector(x, y, z), 1.into()) - } -} - -impl Add for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy { - type Output = Self; - - fn add(self, other :Self) -> Self { - let Point(v1, w1) = self; - let Point(v2, w2) = other; - Self(v1 + v2, w1 + w2) - } -} - -impl Neg for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy { - type Output = Self; - - fn neg(self) -> Self { - let Point(v, w) = self; - Self(-v, -w) - } -} - -impl Sub for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy { - type Output = Self; - - fn sub(self, other :Self) -> Self { - self + -other - } -} - -impl Mul for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy + From { - type Output = Self; - - fn mul(self, other :Self) -> Self { - let a :Vector = self.into(); - let b :Vector = other.into(); - - Point(a * b, 1.into()) - } -} - -impl From> for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy + From { - fn from(v :Vector) -> Self { - Point(v, 1.into()) - } -} - -impl Into> for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Trig + Copy + From { - fn into(self) -> Vector { - let Point(v, w) = self; - - if w == 0.into() { - v - } else { - v.mul(&w.recip()) - } - } -} - -impl Transformable for Point -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Debug + Trig + Copy + From { - fn transform(&self, m :&TMatrix) -> Self { - let Point(v, w) = *self; - let (v, w) = m.apply(&v, w); - - if w == 0.into() { - v.into() - } else { - v.mul(&w.recip()).into() - } - } -} - -#[derive(Debug)] -pub struct Polyeder -where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig { - points :Vec>, - faces :Vec>, -} - -pub trait Primitives -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - fn transform(&self, m :&TMatrix) -> Self; - fn project( &self - , camera :&Camera - , light :&DirectLight - , col :u32 ) -> Vec<(Polygon, u32)>; -} - -#[derive(Debug, Clone, Copy)] -pub struct Camera -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - width :T, - height :T, - distance :T, - project :TMatrix, -} - -pub struct DirectLight -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From { - direction: Vector, -} - -impl Camera -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Debug + Copy + Trig + From { - // This code assumes that the size of the viewport is always - // equal to the size of the physical screen… e.g. window/canvas thus some - // effects can't be done. See book for examples with different viewport - // and screen sizes. - pub fn new(c :&dyn Canvas, angle :i32) -> Self { - let width :T = (c.width() as i32).into(); - let height :T = (c.height() as i32).into(); - let d :T = 1.into(); - let fov = T::cot(angle) * width; - let wh = width / 2.into(); - let hh = height / 2.into(); - - Camera { width: width - , height: height - , distance: d - , project: TMatrix::new( - ( fov, 0.into(), wh, 0.into()) - , (0.into(), fov, hh, 0.into()) - , (0.into(), 0.into(), d, 1.into()) - , (0.into(), 0.into(), 1.into(), 0.into()) ) } - } - - pub fn get_distance(&self) -> T { - self.distance - } - - pub fn get_projection(&self) -> TMatrix { - self.project - } - - pub fn project(&self, p :Point) -> Point { - p.transform(&self.project) - } -} - -impl DirectLight -where T: Add + Sub + Neg - + Mul + Div - + Debug + Copy + Trig + From { - pub fn new(v :Vector) -> Self { - DirectLight{ direction: v } - } - - pub fn dir(&self) -> Vector { - self.direction - } -} - -impl Face -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Debug + Copy + Trig + From { - fn new(corners :Vec, ps :&[Point]) -> Self { - let mut f = Face{ corners: corners, normal: None }; - f.update_normal(ps); - f - } - - fn update_normal(&mut self, ps :&[Point]) { - let edge10 :Vector = (ps[self.corners[1]] - ps[self.corners[0]]).into(); - let edge12 :Vector = (ps[self.corners[1]] - ps[self.corners[2]]).into(); - self.normal = Some(edge10 * edge12); - } -} - -impl Polyeder -where T: Add + Sub + Neg - + Mul + Div - + PartialEq + Debug + Copy + Trig + From { - fn update_normals(&mut self) { - for f in self.faces.iter_mut() { - f.update_normal(&self.points); - } - } - - // construct via cube, see polyhedra.pdf - pub fn tetrahedron(a :T) -> Polyeder { - let f2 :T = 2.into(); - let ch = a / (f2 * T::sqrt(f2).unwrap()); - - let ps = vec!( Point::new(-ch, -ch, ch) // A - , Point::new(-ch, ch, -ch) // C - , Point::new( ch, -ch, -ch) // E - , Point::new( ch, ch, ch) ); // G - - // bottom: 1, 2, 3 - let fs = vec!( Face::new(vec!(2, 1, 0), &ps) // bottom - , Face::new(vec!(3, 2, 0), &ps) - , Face::new(vec!(0, 1, 3), &ps) - , Face::new(vec!(1, 2, 3), &ps) ); - //let fs = vec!( Face::new(vec!(0, 1, 2), &ps) // bottom - // , Face::new(vec!(0, 2, 3), &ps) - // , Face::new(vec!(3, 1, 0), &ps) - // , Face::new(vec!(3, 2, 1), &ps) ); - - Polyeder{ points: ps, faces: fs } - } - - pub fn triangle(a :T) -> Polyeder { - let f0 :T = 0.into(); - let f3 :T = 3.into(); - let f6 :T = 6.into(); - let zi :T = T::sqrt(f3).unwrap() / f6 * a; - let zc :T = T::sqrt(f3).unwrap() / f3 * a; - let ah :T = a / 2.into(); - - let ps = vec!( Point::new(-ah, f0, -zi) - , Point::new( f0, f0, zc) - , Point::new( ah, f0, -zi) ); - - let fs = vec!(Face::new(vec!(0, 1, 2), &ps)); - - Polyeder{ points: ps, faces: fs } - } - - pub fn cube(a :T) -> Polyeder { - let ah :T = a / From::::from(2); - - let ps = vec!( Point::new(-ah, ah, -ah) // 0 => front 1 - , Point::new(-ah, -ah, -ah) // 1 => front 2 - , Point::new( ah, -ah, -ah) // 2 => front 3 - , Point::new( ah, ah, -ah) // 3 => front 4 - , Point::new(-ah, ah, ah) // 4 => back 1 - , Point::new(-ah, -ah, ah) // 5 => back 2 - , Point::new( ah, -ah, ah) // 6 => back 3 - , Point::new( ah, ah, ah) ); // 7 => back 4 - - let fs = vec!( Face::new(vec!(0, 1, 2, 3), &ps) // front - , Face::new(vec!(7, 6, 5, 4), &ps) // back - , Face::new(vec!(1, 5, 6, 2), &ps) // top - , Face::new(vec!(0, 3, 7, 4), &ps) // bottom - , Face::new(vec!(0, 4, 5, 1), &ps) // left - , Face::new(vec!(2, 6, 7, 3), &ps) ); // right - - Polyeder{ points: ps, faces: fs } - } -} - -impl Primitives for Polyeder -where T: Add + Sub + Neg - + Mul + Div - + Debug + Copy + Trig + From + PartialOrd { - // TODO Maybe this should also be an instance of Transformable… - fn transform(&self, m :&TMatrix) -> Self { - let Polyeder{ points: ps, faces: fs } = self; - - let mut p = Polyeder{ - points: ps.iter().map(|p| p.transform(m)).collect() - , faces: fs.to_vec() - }; - - // TODO alternatively we could rotate the normals too, but this cannot - // done with the original matrix… the question is, what is faster. - p.update_normals(); - p - } - - fn project( &self - , camera :&Camera - , light :&DirectLight - , color :u32 ) -> Vec<(Polygon, u32)> { - // Helper to create a Polygon from Coordinates… - // TODO probably there needs to be a Polygon constructor for this. - fn polygon(c :I) -> Polygon - where I: Iterator> { - Polygon(Coordinates(c.collect())) - } - - // this one does the projection... as the projection was the last - // matrix we do not need to do it here. - let to_coord = |p :&usize| { - let Point(v, _) = camera.project(self.points[*p]); - Coordinate(T::round(&v.x()), T::round(&v.y()), v.z() - 1.into()) - }; - let to_poly = |f :&Face| { - let pg = polygon(f.corners.iter().map(to_coord)); - let mut r :T = (((color >> 16) & 0xFF) as i32).into(); - let mut g :T = (((color >> 8) & 0xFF) as i32).into(); - let mut b :T = (((color ) & 0xFF) as i32).into(); - let lf :T = match f.normal { - None => 1.into(), - Some(n) => n.dot(light.dir()) - / (n.mag() * light.dir().mag()), - }; - - // this "if" represents a first simple backface culling - // approach. We only return face that face towards us. - if lf < 0.into() { - r = r * -lf; - g = g * -lf; - b = b * -lf; - - let c :u32 = (r.round() as u32) << 16 - | (g.round() as u32) << 8 - | (b.round() as u32); - - Some((pg, c)) - } else { - None - }}; - - self.faces.iter().filter_map(to_poly).collect() - } -} diff --git a/src/lib.rs b/src/lib.rs index 63bdda0..979e58c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,19 @@ -extern crate lazy_static; +extern crate easel3d; -pub type Error = &'static str; +mod easel3d_wasm; +use easel3d_wasm::{WasmCanvas, Color}; -pub mod easel; -pub mod transform; -pub mod trigonometry; -pub mod vector; -pub mod geometry; +use easel3d::easel::canvas::Canvas; +use easel3d::easel::fillable::Fillable; -mod utils; +use easel3d::math::transform::TMatrix; +use easel3d::math::vector::Vector; -use vector::Vector; -use easel::{Canvas, Coordinate, Drawable, Fillable}; -use geometry::{Camera, DirectLight, Polyeder, Primitives}; -use transform::{TMatrix}; +use easel3d::space::camera::Camera; +use easel3d::space::light::DirectLight; +use easel3d::space::polyeder::Polyeder; +use easel3d::space::primitives::Primitives; -use std::fmt::{Display, Formatter, Result}; -use std::sync::mpsc; -use std::time::Instant; use wasm_bindgen::prelude::*; // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global @@ -27,56 +23,37 @@ use wasm_bindgen::prelude::*; static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Color(u8, u8, u8, u8); - -#[wasm_bindgen] -pub struct View3d { width :u16 - , height :u16 - , size :usize +pub struct View3d { canvas :WasmCanvas , degree :i32 - //, start :Instant , tetrahedron :Polyeder , cube :Polyeder - , camera :Option> - , light :DirectLight - , zbuf :Vec - , image :Vec -} + , camera :Camera + , light :DirectLight } #[wasm_bindgen] impl View3d { pub fn new(width :u16, height :u16) -> Self { - let size = width as usize * height as usize; let light_vector = Vector(0.0, 0.0, 1.0); + let canvas = WasmCanvas::new(width, height); + let camera = Camera::::new(&canvas, 45); - let mut view3d = Self { width: width - , height: height - , size: size - , degree: 0 - //, start: Instant::now() - , tetrahedron: Polyeder::tetrahedron(100.0) - , cube: Polyeder::cube(56.25) - , camera: None - , light: DirectLight::new(light_vector) - , zbuf: vec!(0.0; size) - , image: vec!(Color(0, 0, 0, 0xFF); size) - }; - - view3d.camera = Some(Camera::::new(&view3d, 45)); - view3d + Self { canvas: canvas + , degree: 0 + , tetrahedron: Polyeder::tetrahedron(100.0) + , cube: Polyeder::cube(56.25) + , camera: camera + , light: DirectLight::new(light_vector) } } pub fn width(&self) -> u16 { - self.width + self.canvas.width() } pub fn height(&self) -> u16 { - self.height + self.canvas.height() } pub fn update(&mut self) { - //let deg = ((self.start.elapsed() / 25).as_millis() % 360) as i32; let t = TMatrix::translate(Vector(0.0, 0.0, 150.0)); let rz = TMatrix::rotate_z(self.degree); let rx = TMatrix::rotate_x(-self.degree*2); @@ -88,176 +65,22 @@ impl View3d { let objects = vec!( (self.tetrahedron.transform(&rot1), 0xFFFF00) , ( self.cube.transform(&rot2), 0x0000FF) ); - self.degree = (self.degree + 1) % 360; - - self.clear(); - - match self.camera { - None => {}, - Some(camera) => { - for (o, color) in objects { - for (pg, c) in o.project(&camera, &self.light, color) { - (&pg).fill(self, c); - } - } - }, - } - } - - pub fn image(&self) -> *const Color { - self.image.as_ptr() - } -} - -impl Canvas for View3d { - fn width(&self) -> u16 { - self.width - } - - fn height(&self) -> u16 { - self.height - } - - fn clear(&mut self) { - self.zbuf = vec!(0.0; self.size); - self.image = vec!(Color(0, 0, 0, 0xFF); self.size); - } - - fn set_pixel(&mut self, c :Coordinate, color :u32) { - let Coordinate(x, y, zr) = c; - let idx :usize = (y * (self.width as i32) + x) as usize; - - let r = ((color >> 16) & 0xFF) as u8; - let g = ((color >> 8) & 0xFF) as u8; - let b = ( color & 0xFF) as u8; - - if self.zbuf[idx] < zr { - self.zbuf[idx] = zr; - self.image[idx] = Color(r, g, b, 0xFF); - } - } - - // Empty implementations for now… mostly not needed because it is - // done from JavaScript… - fn init_events(&self) {} - fn start_events(&self, _ :mpsc::Sender) {} - fn draw( &mut self, _ :&dyn Drawable, _ :Coordinate, _ :u32 ) {} - fn put_text(&self, _ :Coordinate, _ :&str) {} - fn show(&self) {} -} - -#[wasm_bindgen] -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Cell { - Dead = 0, - Alive = 1, -} - -#[wasm_bindgen] -pub struct Universe { - width :u32, - height :u32, - cells :Vec, -} - -#[wasm_bindgen] -impl Universe { - pub fn new() -> Universe { - let width = 64; - let height = 64; - - let init_cells = |i :u32| { - if i % 2 == 0 || i % 7 == 0 { Cell::Alive } else { Cell::Dead } - }; - - let cells = (0..width * height).map(init_cells).collect(); - - Universe { - width: width, - height: height, - cells: cells, - } - } - - pub fn width(&self) -> u32 { - self.width - } - - pub fn height(&self) -> u32 { - self.height - } - - pub fn cells(&self) -> *const Cell { - self.cells.as_ptr() - } - - pub fn render(&self) -> String { - self.to_string() - } - - pub fn tick(&mut self) { - let mut next = self.cells.clone(); + let rlx = TMatrix::rotate_x(-self.degree/4); + let rly = TMatrix::rotate_y(-self.degree/1); + let light = self.light.transform(&TMatrix::combine(vec!(rlx, rly))); - for row in 0..self.height { - for col in 0..self.width { - let idx = self.get_index(row, col); - let cell = self.cells[idx]; - let live_neighbors = self.live_neighbor_count(row, col); + self.degree = (self.degree + 1) % (4*360); - // Game of life rules.... - let next_cell = match (cell, live_neighbors) { - (Cell::Alive, 2) | - (Cell::Alive, 3) => Cell::Alive, - (Cell::Alive, _) => Cell::Dead, - ( Cell::Dead, 3) => Cell::Alive, - ( otherwise, _) => otherwise, - }; + self.canvas.clear(); - next[idx] = next_cell; + for (o, color) in objects { + for (pg, c) in o.project(&self.camera, &light, color) { + (&pg).fill(&mut self.canvas, c); } } - - self.cells = next; - } - - fn get_index(&self, row :u32, col :u32) -> usize { - (row * self.width + col) as usize } - fn live_neighbor_count(&self, row :u32, col :u32) -> u8 { - let mut count = 0; - - for delta_row in [self.height - 1, 0, 1].iter().cloned() { - for delta_col in [self.width - 1, 0, 1].iter().cloned() { - if delta_row == 0 && delta_col == 0 { - continue; - } - - let neighbor_row = (row + delta_row) % self.height; - let neighbor_col = (col + delta_col) % self.width; - let idx = self.get_index(neighbor_row, neighbor_col); - count += self.cells[idx] as u8; - } - } - - count - } -} - -impl Display for Universe { - fn fmt(&self, f :&mut Formatter) -> Result { - for line in self.cells.as_slice().chunks(self.width as usize) { - for &cell in line { - let symbol = match cell { - Cell::Dead => ' ', - Cell::Alive => '*', - }; - write!(f, "{}", symbol)?; - } - write!(f, "\n")?; - } - - Ok(()) + pub fn image(&self) -> *const Color { + self.canvas.image() } } diff --git a/src/transform.rs b/src/transform.rs deleted file mode 100644 index e7e3142..0000000 --- a/src/transform.rs +++ /dev/null @@ -1,186 +0,0 @@ -// -// Transformation of vectors in a given coordinate system... -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::ops::{Add, Sub, Neg, Mul, Div}; -use std::fmt::Debug; - -use crate::Vector; -use crate::trigonometry::Trig; - -#[derive(Debug, Clone, Copy)] -pub struct TMatrix( (T, T, T, T) - , (T, T, T, T) - , (T, T, T, T) - , (T, T, T, T) ) - where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From + Copy; - -pub trait Transformable -where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From + Copy { - fn transform(&self, m :&TMatrix) -> Self; -} - -impl TMatrix -where T: Add + Sub + Neg - + Mul + Div - + Debug + Trig + From + Copy { - pub fn new( r1 :(T, T, T, T) - , r2 :(T, T, T, T) - , r3 :(T, T, T, T) - , r4 :(T, T, T, T) ) -> Self { - TMatrix(r1, r2, r3, r4) - } - - pub fn unit() -> Self { - Self::new( (1.into(), 0.into(), 0.into(), 0.into()) - , (0.into(), 1.into(), 0.into(), 0.into()) - , (0.into(), 0.into(), 1.into(), 0.into()) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn translate(v :Vector) -> Self { - let Vector(x, y, z) = v; - - Self::new( (1.into(), 0.into(), 0.into(), x) - , (0.into(), 1.into(), 0.into(), y) - , (0.into(), 0.into(), 1.into(), z) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn rotate_x(a :i32) -> Self { - let sin :T = Trig::sin(a); - let cos :T = Trig::cos(a); - - Self::new( (1.into(), 0.into(), 0.into(), 0.into()) - , (0.into(), cos , -sin , 0.into()) - , (0.into(), sin , cos , 0.into()) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn rotate_y(a :i32) -> Self { - let sin :T = Trig::sin(a); - let cos :T = Trig::cos(a); - - Self::new( (cos , 0.into(), sin , 0.into()) - , (0.into(), 1.into(), 0.into(), 0.into()) - , (-sin , 0.into(), cos , 0.into()) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn rotate_z(a :i32) -> Self { - let sin :T = Trig::sin(a); - let cos :T = Trig::cos(a); - - Self::new( (cos , -sin , 0.into(), 0.into()) - , (sin , cos , 0.into(), 0.into()) - , (0.into(), 0.into(), 1.into(), 0.into()) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn rotate_v(v :&Vector, a :i32) -> Self { - let Vector(x, y, z) = *v; - - let sin :T = Trig::sin(a); - let cos :T = Trig::cos(a); - - let zero :T = 0.into(); - let one :T = 1.into(); - - Self::new( ( (one - cos) * x * x + cos - , (one - cos) * x * y - sin * z - , (one - cos) * x * z + sin * y - , zero ) - , ( (one - cos) * x * y + sin * z - , (one - cos) * y * y + cos - , (one - cos) * y * z - sin * x - , zero ) - , ( (one - cos) * x * z - sin * y - , (one - cos) * y * z + sin * x - , (one - cos) * z * z + cos - , zero ) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn scale(v :Vector) -> Self { - let Vector(x, y, z) = v; - - Self::new( ( x, 0.into(), 0.into(), 0.into()) - , (0.into(), y, 0.into(), 0.into()) - , (0.into(), 0.into(), z, 0.into()) - , (0.into(), 0.into(), 0.into(), 1.into()) ) - } - - pub fn combine(mi :I) -> TMatrix - where I: IntoIterator> { - - mi.into_iter().fold(Self::unit(), |acc, x| x * acc) - } - - pub fn apply(&self, v :&Vector, w :T) -> (Vector, T) { - let TMatrix( (a11, a12, a13, a14) - , (a21, a22, a23, a24) - , (a31, a32, a33, a34) - , (a41, a42, a43, a44) ) = *self; - let Vector(x, y, z) = *v; - - let v = Vector( a11 * x + a12 * y + a13 * z + a14 * w - , a21 * x + a22 * y + a23 * z + a24 * w - , a31 * x + a32 * y + a33 * z + a34 * w ); - let w = a41 * x + a42 * y + a43 * z + a44 * w; - - //v.mul(&w.recip()) - (v, w) - } -} - -impl Mul for TMatrix -where T: Add + Sub + Neg - + Mul + Div - + Debug + Trig + From + Copy { - type Output = Self; - - // ATTENTION: This is not commutative, nor assoziative. - fn mul(self, other :Self) -> Self { - let TMatrix( (a11, a12, a13, a14) - , (a21, a22, a23, a24) - , (a31, a32, a33, a34) - , (a41, a42, a43, a44) ) = self; - let TMatrix( (b11, b12, b13, b14) - , (b21, b22, b23, b24) - , (b31, b32, b33, b34) - , (b41, b42, b43, b44) ) = other; - - TMatrix( ( a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41 - , a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42 - , a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43 - , a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44 ) - , ( a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41 - , a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42 - , a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43 - , a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44 ) - , ( a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41 - , a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42 - , a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43 - , a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44 ) - , ( a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41 - , a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42 - , a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43 - , a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44 ) ) - } -} diff --git a/src/trigonometry.rs b/src/trigonometry.rs deleted file mode 100644 index e52bb8c..0000000 --- a/src/trigonometry.rs +++ /dev/null @@ -1,143 +0,0 @@ -// -// Some trigonometic functions with Fractions results. -// Currently only sin, cos and tan are implemented. -// As I was unable to find a really good integral approximation for them I -// implement them as a table which is predefined using the floating point -// function f64::sin and then transformed into a fraction of a given -// PRECISION. -// These approximations are quite good and for a few edge cases -// even better than the floating point implementations. -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::ops::Div; -use std::ops::Neg; -use std::marker::Sized; -use crate::Error; - -pub trait Trig { - fn pi() -> Self; - fn recip(self) -> Self; - fn round(&self) -> i32; - fn sqrt(self) -> Result where Self: Sized; - fn sintab() -> Vec where Self: Sized; - fn tantab() -> Vec where Self: Sized; - - fn sin(d :i32) -> Self - where Self: Sized + Neg + Copy { - match d { - 0 ..=90 => Self::sintab()[d as usize], - 91 ..=180 => Self::sintab()[180 - d as usize], - 181..=270 => -Self::sintab()[d as usize - 180], - 271..=359 => -Self::sintab()[360 - d as usize], - _ => { - Self::sin(if d < 0 { d % 360 + 360 } else { d % 360 }) - }, - } - } - - fn cos(d :i32) -> Self - where Self: Sized + Neg + Copy { - match d { - 0 ..=90 => Self::sintab()[90 - d as usize], - 91 ..=180 => -Self::sintab()[90 - (180 - d as usize)], - 181..=270 => -Self::sintab()[90 - (d as usize - 180)], - 271..=359 => Self::sintab()[90 - (360 - d as usize)], - _ => { - Self::cos(if d < 0 { d % 360 + 360 } else { d % 360 }) - }, - } - } - - fn tan(d :i32) -> Self where Self: Sized + Copy { - match d { - 0 ..=179 => Self::tantab()[d as usize], - 180..=359 => Self::tantab()[d as usize - 180], - _ => { - Self::tan(if d < 0 { d % 360 + 360 } else { d % 360 }) - }, - } - } - - fn cot(d :i32) -> Self - where Self: Sized + Copy + From + Div { - Into::::into(1) / Self::tan(d) - } -} - -impl Trig for f64 { - fn pi() -> Self { - std::f64::consts::PI - } - - fn recip(self) -> Self { - self.recip() - } - - fn round(&self) -> i32 { - f64::round(*self) as i32 - } - - fn sqrt(self) -> Result { - let x = self.sqrt(); - match x.is_nan() { - true => Err("sqrt on negative undefined"), - false => Ok(x), - } - } - - fn sintab() -> Vec { - lazy_static::lazy_static! { - static ref SINTAB :Vec = - (0..=90).map(|x| _sin(x)).collect(); - } - - // f64 sin. (From 0° to 90°) - fn _sin(d: u32) -> f64 { - match d { - 0 => 0.0, - 90 => 1.0, - _ => (d as f64).to_radians().sin(), - } - } - - SINTAB.to_vec() - } - - fn tantab() -> Vec { - // This table exists only because the sin(α) / cos(α) method - // yields very large unreducable denominators in a lot of cases. - lazy_static::lazy_static! { - static ref TANTAB :Vec = - (0..180).map(|x| _tan(x)).collect(); - } - - // fractional tan from f64 tan. (From 0° to 179°) - fn _tan(d: u32) -> f64 { - match d { - 0 => 0.0, - 45 => 1.0, - 90 => std::f64::INFINITY, - 135 => -1.0, - _ => (d as f64).to_radians().tan(), - } - } - - TANTAB.to_vec() - } -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index b1d7929..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub fn set_panic_hook() { - // When the `console_error_panic_hook` feature is enabled, we can call the - // `set_panic_hook` function at least once during initialization, and then - // we will get better error messages if our code ever panics. - // - // For more details see - // https://github.com/rustwasm/console_error_panic_hook#readme - #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); -} diff --git a/src/vector.rs b/src/vector.rs deleted file mode 100644 index 28ad18c..0000000 --- a/src/vector.rs +++ /dev/null @@ -1,139 +0,0 @@ -// -// Stuff for manipulating 3 dimensional vectors. -// -// Georg Hopp -// -// Copyright © 2019 Georg Hopp -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// -use std::fmt::{Debug, Display, Formatter, Result}; -use std::ops::{Add, Sub, Neg, Mul, Div}; - -use crate::trigonometry::Trig; -use crate::transform::{TMatrix, Transformable}; - -#[derive(Debug, Eq, Clone, Copy)] -pub struct Vector(pub T, pub T, pub T) - where T: Add + Sub + Neg + Mul + Div + Trig + Copy; - -impl Vector -where T: Add + Sub + Neg - + Mul + Div + Trig + Copy { - pub fn x(self) -> T { self.0 } - pub fn y(self) -> T { self.1 } - pub fn z(self) -> T { self.2 } - - pub fn mag(self) -> T { - let Vector(x, y, z) = self; - (x * x + y * y + z * z).sqrt().unwrap() - } - - pub fn mul(self, s :&T) -> Self { - let Vector(x, y, z) = self; - Vector(x * *s, y * *s, z * *s) - } - - pub fn dot(self, other :Self) -> T { - let Vector(x1, y1, z1) = self; - let Vector(x2, y2, z2) = other; - - x1 * x2 + y1 * y2 + z1 * z2 - } - - pub fn norm(self) -> Self { - // TODO This can result in 0 or inf Vectors… - // Maybe we need to handle zero and inf magnitude here… - self.mul(&self.mag().recip()) - } - - pub fn distance(self, other :Self) -> T { - (self - other).mag() - } -} - -impl Display for Vector -where T: Add + Sub + Neg + Mul + Div + Trig + Display + Copy { - fn fmt(&self, f :&mut Formatter<'_>) -> Result { - let Vector(x, y, z) = self; - write!(f, "({}, {}, {})", x, y, z) - } -} - -impl PartialEq for Vector -where T: Add + Sub + Neg + Mul + Div + Trig + PartialEq + Copy { - fn eq(&self, other :&Self) -> bool { - let Vector(x1, y1, z1) = self; - let Vector(x2, y2, z2) = other; - x1 == x2 && y1 == y2 && z1 == z2 - } -} - -impl Add for Vector -where T: Add + Sub + Neg - + Mul + Div + Trig + Copy { - type Output = Self; - - fn add(self, other :Self) -> Self { - let Vector(x1, y1, z1) = self; - let Vector(x2, y2, z2) = other; - Vector(x1 + x2, y1 + y2, z1 + z2) - } -} - -impl Sub for Vector -where T: Add + Sub + Neg - + Mul + Div + Trig + Copy { - type Output = Self; - - fn sub(self, other :Self) -> Self { - self + -other - } -} - -impl Neg for Vector -where T: Add + Sub + Neg - + Mul + Div + Trig + Copy { - type Output = Self; - - fn neg(self) -> Self { - let Vector(x, y, z) = self; - Self(-x, -y, -z) - } -} - -impl Mul for Vector -where T: Add + Sub + Neg - + Mul + Div + Trig + Copy { - type Output = Self; - - fn mul(self, other :Self) -> Self { - let Vector(ax, ay, az) = self; - let Vector(bx, by, bz) = other; - - Vector( ay * bz - az * by - , az * bx - ax * bz - , ax * by - ay * bx ) - } -} - -impl Transformable for Vector -where T: Add + Sub + Neg - + Mul + Div - + Trig + Copy + Debug + From { - fn transform(&self, m :&TMatrix) -> Self { - let (v, _) = m.apply(self, 0.into()); - v - } -} diff --git a/www/.bin/create-wasm-app.js b/www/.bin/create-wasm-app.js deleted file mode 100755 index 2f42973..0000000 --- a/www/.bin/create-wasm-app.js +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env node - -const { spawn } = require("child_process"); -const fs = require("fs"); - -let folderName = '.'; - -if (process.argv.length >= 3) { - folderName = process.argv[2]; - if (!fs.existsSync(folderName)) { - fs.mkdirSync(folderName); - } -} - -const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]); - -clone.on("close", code => { - if (code !== 0) { - console.error("cloning the template failed!") - process.exit(code); - } else { - console.log("🦀 Rust + 🕸 Wasm = ❤"); - } -}); diff --git a/www/README.md b/www/README.md deleted file mode 100644 index d658bb1..0000000 --- a/www/README.md +++ /dev/null @@ -1,67 +0,0 @@ -
- -

create-wasm-app

- - An npm init template for kick starting a project that uses NPM packages containing Rust-generated WebAssembly and bundles them with Webpack. - -

- Build Status -

- -

- Usage - | - Chat -

- - Built with 🦀🕸 by The Rust and WebAssembly Working Group -
- -## About - -This template is designed for depending on NPM packages that contain -Rust-generated WebAssembly and using them to create a Website. - -* Want to create an NPM package with Rust and WebAssembly? [Check out - `wasm-pack-template`.](https://github.com/rustwasm/wasm-pack-template) -* Want to make a monorepo-style Website without publishing to NPM? Check out - [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) - and/or - [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). - -## 🚴 Usage - -``` -npm init wasm-app -``` - -## 🔋 Batteries Included - -- `.gitignore`: ignores `node_modules` -- `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you -- `README.md`: the file you are reading now! -- `index.html`: a bare bones html document that includes the webpack bundle -- `index.js`: example js file with a comment showing how to import and use a wasm pkg -- `package.json` and `package-lock.json`: - - pulls in devDependencies for using webpack: - - [`webpack`](https://www.npmjs.com/package/webpack) - - [`webpack-cli`](https://www.npmjs.com/package/webpack-cli) - - [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server) - - defines a `start` script to run `webpack-dev-server` -- `webpack.config.js`: configuration file for bundling your js with webpack - -## License - -Licensed under either of - -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/www/index.html b/www/index.html index 735c230..d7238b4 100644 --- a/www/index.html +++ b/www/index.html @@ -2,7 +2,7 @@ - wasm-game-of-life! + easel3d