Showing
16 changed files
with
144 additions
and
1821 deletions
| 1 | [package] | 1 | [package] |
| 2 | -name = "wasm-game-of-life" | 2 | +name = "easel3d-wasm" |
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | authors = ["hopp@silpion.de"] | 4 | authors = ["hopp@silpion.de"] |
| 5 | edition = "2018" | 5 | edition = "2018" |
| @@ -7,29 +7,13 @@ edition = "2018" | @@ -7,29 +7,13 @@ edition = "2018" | ||
| 7 | [lib] | 7 | [lib] |
| 8 | crate-type = ["cdylib", "rlib"] | 8 | crate-type = ["cdylib", "rlib"] |
| 9 | 9 | ||
| 10 | -[features] | ||
| 11 | -default = ["console_error_panic_hook"] | ||
| 12 | - | ||
| 13 | [dependencies] | 10 | [dependencies] |
| 14 | wasm-bindgen = "0.2" | 11 | wasm-bindgen = "0.2" |
| 15 | -lazy_static = "1.4.0" | ||
| 16 | - | ||
| 17 | -# The `console_error_panic_hook` crate provides better debugging of panics by | ||
| 18 | -# logging them with `console.error`. This is great for development, but requires | ||
| 19 | -# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for | ||
| 20 | -# code size when deploying. | ||
| 21 | -console_error_panic_hook = { version = "0.1.1", optional = true } | ||
| 22 | - | ||
| 23 | -# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size | ||
| 24 | -# compared to the default allocator's ~10K. It is slower than the default | ||
| 25 | -# allocator, however. | ||
| 26 | -# | ||
| 27 | -# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. | 12 | +easel3d = { path = "../easel3d" } |
| 28 | wee_alloc = { version = "0.4.2", optional = true } | 13 | wee_alloc = { version = "0.4.2", optional = true } |
| 29 | 14 | ||
| 30 | [dev-dependencies] | 15 | [dev-dependencies] |
| 31 | wasm-bindgen-test = "0.2" | 16 | wasm-bindgen-test = "0.2" |
| 32 | 17 | ||
| 33 | [profile.release] | 18 | [profile.release] |
| 34 | -# Tell `rustc` to optimize for small code size. | ||
| 35 | opt-level = "s" | 19 | opt-level = "s" |
| 1 | -<div align="center"> | 1 | +# Easel3D-WASM |
| 2 | 2 | ||
| 3 | - <h1><code>wasm-pack-template</code></h1> | ||
| 4 | - | ||
| 5 | - <strong>A template for kick starting a Rust and WebAssembly project using <a href="https://github.com/rustwasm/wasm-pack">wasm-pack</a>.</strong> | ||
| 6 | - | ||
| 7 | - <p> | ||
| 8 | - <a href="https://travis-ci.org/rustwasm/wasm-pack-template"><img src="https://img.shields.io/travis/rustwasm/wasm-pack-template.svg?style=flat-square" alt="Build Status" /></a> | ||
| 9 | - </p> | ||
| 10 | - | ||
| 11 | - <h3> | ||
| 12 | - <a href="https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html">Tutorial</a> | ||
| 13 | - <span> | </span> | ||
| 14 | - <a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a> | ||
| 15 | - </h3> | ||
| 16 | - | ||
| 17 | - <sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub> | ||
| 18 | -</div> | ||
| 19 | - | ||
| 20 | -## About | ||
| 21 | - | ||
| 22 | -[**📚 Read this template tutorial! 📚**][template-docs] | ||
| 23 | - | ||
| 24 | -This template is designed for compiling Rust libraries into WebAssembly and | ||
| 25 | -publishing the resulting package to NPM. | ||
| 26 | - | ||
| 27 | -Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other | ||
| 28 | -templates and usages of `wasm-pack`. | ||
| 29 | - | ||
| 30 | -[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html | ||
| 31 | -[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html | ||
| 32 | - | ||
| 33 | -## 🚴 Usage | ||
| 34 | - | ||
| 35 | -### 🐑 Use `cargo generate` to Clone this Template | ||
| 36 | - | ||
| 37 | -[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) | ||
| 38 | - | ||
| 39 | -``` | ||
| 40 | -cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project | ||
| 41 | -cd my-project | ||
| 42 | -``` | ||
| 43 | - | ||
| 44 | -### 🛠️ Build with `wasm-pack build` | ||
| 45 | - | ||
| 46 | -``` | ||
| 47 | -wasm-pack build | ||
| 48 | -``` | ||
| 49 | - | ||
| 50 | -### 🔬 Test in Headless Browsers with `wasm-pack test` | ||
| 51 | - | ||
| 52 | -``` | ||
| 53 | -wasm-pack test --headless --firefox | ||
| 54 | -``` | ||
| 55 | - | ||
| 56 | -### 🎁 Publish to NPM with `wasm-pack publish` | ||
| 57 | - | ||
| 58 | -``` | ||
| 59 | -wasm-pack publish | ||
| 60 | -``` | ||
| 61 | - | ||
| 62 | -## 🔋 Batteries Included | ||
| 63 | - | ||
| 64 | -* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating | ||
| 65 | - between WebAssembly and JavaScript. | ||
| 66 | -* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) | ||
| 67 | - for logging panic messages to the developer console. | ||
| 68 | -* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized | ||
| 69 | - for small code size. | ||
| 70 | -# Rust playground | ||
| 71 | - | ||
| 72 | -Things I have recently done while learning the Rust programming language. | 3 | +WebAssembly demo application using Easel3D... |
| 73 | 4 | ||
| 74 | ## Synopsis | 5 | ## Synopsis |
| 75 | 6 | ||
| 76 | -Change in one of the toplevel subdirectories and try `cargo build` or | ||
| 77 | -`cargo run`. Maybe not everything is working oob. Feel free to fix whatever | ||
| 78 | -you want. | 7 | +Checkout this repository as well as the _easel3d_ repository to the same |
| 8 | +destination directory. | ||
| 79 | 9 | ||
| 80 | -## Description | 10 | +In the root of this repository call `wasm-pack build`. |
| 11 | +Then change to www and call `npm install`. | ||
| 81 | 12 | ||
| 82 | -Various small examples I have tried while learning rust. The biggest and | ||
| 83 | -currently most active project is **fractional** which started as an | ||
| 84 | -implamentation of a rational number data type and then switched to a 3D | ||
| 85 | -math playground visualizing using **XCB** (in future it might also use | ||
| 86 | -a **HTML5 Canvas** for drawing as WebAssembly application. | ||
| 87 | -Using fractions with 3D math has several drawbacks: | 13 | +To test the stuff call `npm run start` from www directory and in your browser |
| 14 | +open http:://localhost:8080/. | ||
| 88 | 15 | ||
| 89 | -1. A huge part of 3D math is non rational, like sin, cos, tan and sqrt. | ||
| 90 | -2. The numerator and denominator tend to become very huge while nearing to non | ||
| 91 | - rational numbers and reduction is difficult and time consuming. | ||
| 92 | -3. Because of 2 it is way slower than the floating point calculation (at least | ||
| 93 | - with a decent coprocessor). | 16 | +## Description |
| 94 | 17 | ||
| 95 | -Anyway, implementing the vector math stuff for both fractions and floating | ||
| 96 | -point was a nice playground for generics and traits. In future I might add | ||
| 97 | -another data type which implements the math as done by David Braben for the | ||
| 98 | -elite computer game. | 18 | +A demo application using easel3d to draw in an HTML5 canvas element in a web |
| 19 | +page. | ||
| 99 | 20 | ||
| 100 | ## Requirements | 21 | ## Requirements |
| 101 | 22 | ||
| 102 | -### Always | ||
| 103 | - | ||
| 104 | - A recent version of the Rust programming language as well as tooling. | 23 | - A recent version of the Rust programming language as well as tooling. |
| 105 | Currently I use Rust 1.39.0. | 24 | Currently I use Rust 1.39.0. |
| 25 | +- wasm-pack to build to wasm target | ||
| 26 | +- npm for Javascript code. | ||
| 27 | +- A browser capable of executing WebAssembly. | ||
| 106 | 28 | ||
| 107 | -### For fractional | 29 | +## Dependencies |
| 108 | 30 | ||
| 109 | -- A running X Server with **XCB** and **X11-SHM** extentions | 31 | +### Rust crates. |
| 110 | 32 | ||
| 111 | -## Dependencies | 33 | +- easel3d (from parent directory) |
| 34 | +- wasm-bindgen =0.2 | ||
| 35 | +- wee-alloc =0.4.2 (optional) | ||
| 36 | + | ||
| 37 | +Along with the dependencies of the external crates. `wasm-pack build` should | ||
| 38 | +take care of having them available. | ||
| 39 | + | ||
| 40 | +### Javascript | ||
| 112 | 41 | ||
| 113 | -... | 42 | +Various things npm installs. |
| 114 | 43 | ||
| 115 | ## Contributing | 44 | ## Contributing |
| 116 | 45 |
src/easel.rs
deleted
100644 → 0
| 1 | -// | ||
| 2 | -// This is an abstraction over a drawing environment. | ||
| 3 | -// Future note: z-Buffer is described here: | ||
| 4 | -// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes | ||
| 5 | -// | ||
| 6 | -// Georg Hopp <georg@steffers.org> | ||
| 7 | -// | ||
| 8 | -// Copyright © 2019 Georg Hopp | ||
| 9 | -// | ||
| 10 | -// This program is free software: you can redistribute it and/or modify | ||
| 11 | -// it under the terms of the GNU General Public License as published by | ||
| 12 | -// the Free Software Foundation, either version 3 of the License, or | ||
| 13 | -// (at your option) any later version. | ||
| 14 | -// | ||
| 15 | -// This program is distributed in the hope that it will be useful, | ||
| 16 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 18 | -// GNU General Public License for more details. | ||
| 19 | -// | ||
| 20 | -// You should have received a copy of the GNU General Public License | ||
| 21 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 22 | -// | ||
| 23 | -use std::cmp; | ||
| 24 | -use std::fmt::{Formatter, Debug, Display, Result}; | ||
| 25 | -use std::ops::{Add, Sub, Div}; | ||
| 26 | -use std::sync::mpsc; | ||
| 27 | - | ||
| 28 | -pub trait Easel { | ||
| 29 | - //fn canvas(&mut self, width :u16, height :u16) -> Option<&dyn Canvas>; | ||
| 30 | -} | ||
| 31 | - | ||
| 32 | -pub trait Canvas<T> { | ||
| 33 | - fn init_events(&self); | ||
| 34 | - fn start_events(&self, tx :mpsc::Sender<i32>); | ||
| 35 | - | ||
| 36 | - fn width(&self) -> u16; | ||
| 37 | - fn height(&self) -> u16; | ||
| 38 | - | ||
| 39 | - fn clear(&mut self); | ||
| 40 | - fn draw(&mut self, c :&dyn Drawable<T>, ofs :Coordinate<T>, color :u32); | ||
| 41 | - fn put_text(&self, ofs :Coordinate<T>, s :&str); | ||
| 42 | - fn set_pixel(&mut self, c :Coordinate<T>, color :u32); | ||
| 43 | - fn show(&self); | ||
| 44 | -} | ||
| 45 | - | ||
| 46 | -pub trait Drawable<T> { | ||
| 47 | - fn plot(&self) -> Coordinates<T>; | ||
| 48 | -} | ||
| 49 | - | ||
| 50 | -pub trait Fillable<T> | ||
| 51 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 52 | - + Debug + Copy + From<i32> { | ||
| 53 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32); | ||
| 54 | -} | ||
| 55 | - | ||
| 56 | -#[derive(Debug, Clone, Copy)] | ||
| 57 | -pub struct Coordinate<T>(pub i32, pub i32, pub T); | ||
| 58 | - | ||
| 59 | -#[derive(Debug, Clone)] | ||
| 60 | -pub struct Coordinates<T>(pub Vec<Coordinate<T>>); | ||
| 61 | - | ||
| 62 | -#[derive(Debug, Clone, Copy)] | ||
| 63 | -pub struct LineIterator<T> where T: Debug { | ||
| 64 | - a :Option<Coordinate<T>> | ||
| 65 | - , b :Coordinate<T> | ||
| 66 | - , dx :i32 | ||
| 67 | - , dy :i32 | ||
| 68 | - , dz :T | ||
| 69 | - , sx :i32 | ||
| 70 | - , sy :i32 | ||
| 71 | - , err :i32 | ||
| 72 | - , only_edges :bool | ||
| 73 | -} | ||
| 74 | - | ||
| 75 | -impl<T> Iterator for LineIterator<T> | ||
| 76 | -where T: Add<Output = T> + Debug + Copy + From<i32> { | ||
| 77 | - type Item = Coordinate<T>; | ||
| 78 | - | ||
| 79 | - fn next(&mut self) -> Option<Self::Item> { | ||
| 80 | - match self.a { | ||
| 81 | - None => None, | ||
| 82 | - Some(a) => { | ||
| 83 | - let Coordinate(ax, ay, az) = a; | ||
| 84 | - let Coordinate(bx, by, _) = self.b; | ||
| 85 | - | ||
| 86 | - if ax != bx || ay != by { | ||
| 87 | - match (2 * self.err >= self.dy, 2 * self.err <= self.dx ) { | ||
| 88 | - (true, false) => { | ||
| 89 | - let r = self.a; | ||
| 90 | - self.a = Some(Coordinate( ax + self.sx | ||
| 91 | - , ay | ||
| 92 | - , az + self.dz )); | ||
| 93 | - self.err = self.err + self.dy; | ||
| 94 | - if self.only_edges { self.next() } else { r } | ||
| 95 | - }, | ||
| 96 | - (false, true) => { | ||
| 97 | - let r = self.a; | ||
| 98 | - self.a = Some(Coordinate( ax | ||
| 99 | - , ay + self.sy | ||
| 100 | - , az + self.dz )); | ||
| 101 | - self.err = self.err + self.dx; | ||
| 102 | - r | ||
| 103 | - }, | ||
| 104 | - _ => { | ||
| 105 | - let r = self.a; | ||
| 106 | - self.a = Some(Coordinate( ax + self.sx | ||
| 107 | - , ay + self.sy | ||
| 108 | - , az + self.dz )); | ||
| 109 | - self.err = self.err + self.dx + self.dy; | ||
| 110 | - r | ||
| 111 | - }, | ||
| 112 | - } | ||
| 113 | - } else { | ||
| 114 | - self.a = None; | ||
| 115 | - Some(self.b) | ||
| 116 | - } | ||
| 117 | - } | ||
| 118 | - } | ||
| 119 | - } | ||
| 120 | -} | ||
| 121 | - | ||
| 122 | -impl<T> Coordinate<T> | ||
| 123 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 124 | - + Debug + Clone + Copy + From<i32> { | ||
| 125 | - fn iter(self, b :&Self, only_edges :bool) -> LineIterator<T> { | ||
| 126 | - let Coordinate(ax, ay, az) = self; | ||
| 127 | - let Coordinate(bx, by, bz) = *b; | ||
| 128 | - | ||
| 129 | - let dx = (bx - ax).abs(); | ||
| 130 | - let dy = -(by - ay).abs(); | ||
| 131 | - | ||
| 132 | - LineIterator { a: Some(self) | ||
| 133 | - , b: *b | ||
| 134 | - , dx: dx | ||
| 135 | - , dy: dy | ||
| 136 | - , dz: (bz - az) / cmp::max(dx, -dy).into() | ||
| 137 | - , sx: if ax < bx { 1 } else { -1 } | ||
| 138 | - , sy: if ay < by { 1 } else { -1 } | ||
| 139 | - , err: dx + dy | ||
| 140 | - , only_edges: only_edges | ||
| 141 | - } | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - fn line_iter(self, b :&Self) -> LineIterator<T> { | ||
| 145 | - self.iter(b, false) | ||
| 146 | - } | ||
| 147 | - | ||
| 148 | - fn line(self, b :&Self) -> Vec<Self> { | ||
| 149 | - self.line_iter(b).collect() | ||
| 150 | - } | ||
| 151 | - | ||
| 152 | - fn edge_iter(self, b :&Self) -> LineIterator<T> { | ||
| 153 | - self.iter(b, true) | ||
| 154 | - } | ||
| 155 | - | ||
| 156 | - fn edge(self, b :&Self) -> Vec<Self> { | ||
| 157 | - self.edge_iter(b).collect() | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - fn face(edges :&[Self]) -> Vec<Self> { | ||
| 161 | - edges.to_vec() | ||
| 162 | - } | ||
| 163 | -} | ||
| 164 | - | ||
| 165 | -impl<T> Display for Coordinate<T> { | ||
| 166 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 167 | - write!(f, "<{},{}>", self.0, self.1) | ||
| 168 | - } | ||
| 169 | -} | ||
| 170 | - | ||
| 171 | -impl<T> Display for Coordinates<T> where T: Copy { | ||
| 172 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 173 | - let Coordinates(is) = self; | ||
| 174 | - | ||
| 175 | - let c = match is[..] { | ||
| 176 | - [] => String::from(""), | ||
| 177 | - [a] => format!("{}", a), | ||
| 178 | - _ => { | ||
| 179 | - let mut a = format!("{}", is[0]); | ||
| 180 | - for i in is[1..].iter() { | ||
| 181 | - a = a + &format!(",{}", i); | ||
| 182 | - } | ||
| 183 | - a | ||
| 184 | - } | ||
| 185 | - }; | ||
| 186 | - | ||
| 187 | - write!(f, "Coordinates[{}]", c) | ||
| 188 | - } | ||
| 189 | -} | ||
| 190 | - | ||
| 191 | - | ||
| 192 | -#[derive(Debug, Clone, Copy)] | ||
| 193 | -pub struct Point<T>(pub Coordinate<T>); | ||
| 194 | - | ||
| 195 | -impl<T> Drawable<T> for Point<T> where T: Copy { | ||
| 196 | - fn plot(&self) -> Coordinates<T> { | ||
| 197 | - let Point(c) = *self; | ||
| 198 | - Coordinates(vec!(c)) | ||
| 199 | - } | ||
| 200 | -} | ||
| 201 | - | ||
| 202 | -impl<T> Display for Point<T> { | ||
| 203 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 204 | - let Point(p) = self; | ||
| 205 | - write!(f, "Point[{}]", p) | ||
| 206 | - } | ||
| 207 | -} | ||
| 208 | - | ||
| 209 | -#[derive(Debug, Clone, Copy)] | ||
| 210 | -pub struct Line<T>(pub Coordinate<T>, pub Coordinate<T>); | ||
| 211 | - | ||
| 212 | -impl<T> Drawable<T> for Line<T> | ||
| 213 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 214 | - + Debug + Clone + Copy + From<i32> { | ||
| 215 | - fn plot(&self) -> Coordinates<T> { | ||
| 216 | - let Line(a, b) = *self; | ||
| 217 | - Coordinates(a.line(&b)) | ||
| 218 | - } | ||
| 219 | -} | ||
| 220 | - | ||
| 221 | -impl<T> Display for Line<T> { | ||
| 222 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 223 | - let Line(a, b) = self; | ||
| 224 | - write!(f, "Line[{},{}]", a, b) | ||
| 225 | - } | ||
| 226 | -} | ||
| 227 | - | ||
| 228 | -#[derive(Debug, Clone)] | ||
| 229 | -pub struct Polyline<T>(pub Coordinates<T>); | ||
| 230 | - | ||
| 231 | -impl<T> Drawable<T> for Polyline<T> | ||
| 232 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 233 | - + Debug + Clone + Copy + From<i32> { | ||
| 234 | - fn plot(&self) -> Coordinates<T> { | ||
| 235 | - let Polyline(Coordinates(cs)) = self; | ||
| 236 | - | ||
| 237 | - match cs[..] { | ||
| 238 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | ||
| 239 | - [a] => Coordinates(vec!(a)), | ||
| 240 | - [a, b] => Coordinates(a.line(&b)), | ||
| 241 | - _ => { | ||
| 242 | - let (a, b) = (cs[0], cs[1]); | ||
| 243 | - let mut r = a.line(&b); | ||
| 244 | - let mut i = b; | ||
| 245 | - for j in cs[2..].iter() { | ||
| 246 | - r.append(&mut i.line(j)[1..].to_vec()); | ||
| 247 | - i = *j; | ||
| 248 | - } | ||
| 249 | - Coordinates(r) | ||
| 250 | - }, | ||
| 251 | - } | ||
| 252 | - } | ||
| 253 | -} | ||
| 254 | - | ||
| 255 | -impl<T> Display for Polyline<T> where T: Copy { | ||
| 256 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 257 | - let Polyline(a) = self; | ||
| 258 | - write!(f, "PLine[{}]", a) | ||
| 259 | - } | ||
| 260 | -} | ||
| 261 | - | ||
| 262 | -#[derive(Debug, Clone, Copy)] | ||
| 263 | -enum Direction { Left, Right } | ||
| 264 | - | ||
| 265 | -#[derive(Debug, Clone)] | ||
| 266 | -pub struct Polygon<T>(pub Coordinates<T>); | ||
| 267 | - | ||
| 268 | -#[derive(Debug, Clone)] | ||
| 269 | -enum VertexIteratorMode { Vertex, Edge } | ||
| 270 | -#[derive(Debug, Clone)] | ||
| 271 | -pub struct VertexIterator<'a,T> where T: Debug { | ||
| 272 | - p :&'a Polygon<T>, | ||
| 273 | - top :usize, | ||
| 274 | - current :Option<usize>, | ||
| 275 | - edge :Option<LineIterator<T>>, | ||
| 276 | - mode :VertexIteratorMode, | ||
| 277 | - direction :Direction, | ||
| 278 | -} | ||
| 279 | - | ||
| 280 | -impl<'a,T> VertexIterator<'a,T> | ||
| 281 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 282 | - + Debug + Copy + From<i32> { | ||
| 283 | - fn edge(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 284 | - let top = p.vert_min(direction); | ||
| 285 | - let next = p.next_y(top, direction); | ||
| 286 | - let edge = match next { | ||
| 287 | - None => None, | ||
| 288 | - Some(next) => Some(p.vertex(top).edge_iter(&p.vertex(next))), | ||
| 289 | - }; | ||
| 290 | - | ||
| 291 | - VertexIterator { p: p | ||
| 292 | - , top: top | ||
| 293 | - , current: next | ||
| 294 | - , edge: edge | ||
| 295 | - , mode: VertexIteratorMode::Edge | ||
| 296 | - , direction: direction } | ||
| 297 | - } | ||
| 298 | - | ||
| 299 | - fn vertex(p :&'a Polygon<T>, direction :Direction) -> Self { | ||
| 300 | - let top = p.vert_min(direction); | ||
| 301 | - let next = p.next_y(top, direction); | ||
| 302 | - | ||
| 303 | - VertexIterator { p: p | ||
| 304 | - , top: top | ||
| 305 | - , current: next | ||
| 306 | - , edge: None | ||
| 307 | - , mode: VertexIteratorMode::Vertex | ||
| 308 | - , direction: direction } | ||
| 309 | - } | ||
| 310 | - | ||
| 311 | - // if this yields "None" we are finished. | ||
| 312 | - fn next_edge(&mut self) -> Option<LineIterator<T>> { | ||
| 313 | - let current = self.current?; | ||
| 314 | - let next = self.p.next_y(current, self.direction)?; | ||
| 315 | - let mut edge = self.p.vertex(current).edge_iter(&self.p.vertex(next)); | ||
| 316 | - | ||
| 317 | - match edge.next() { | ||
| 318 | - // It should be impossible that a new edge iterator has no values | ||
| 319 | - // at all… anyway, just in case I handle it here. | ||
| 320 | - None => self.next_edge(), | ||
| 321 | - Some(_) => { | ||
| 322 | - self.current = Some(next); | ||
| 323 | - self.edge = Some(edge); | ||
| 324 | - self.edge | ||
| 325 | - }, | ||
| 326 | - } | ||
| 327 | - } | ||
| 328 | -} | ||
| 329 | - | ||
| 330 | -impl<'a,T> Iterator for VertexIterator<'a,T> | ||
| 331 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 332 | - + Debug + Copy + From<i32> { | ||
| 333 | - type Item = Coordinate<T>; | ||
| 334 | - | ||
| 335 | - fn next(&mut self) -> Option<Self::Item> { | ||
| 336 | - match self.mode { | ||
| 337 | - VertexIteratorMode::Edge => { | ||
| 338 | - // if for whatever reason edge is "None" finish this iterator. | ||
| 339 | - let next = self.edge.as_mut()?.next(); | ||
| 340 | - | ||
| 341 | - match next { | ||
| 342 | - Some(_) => next, | ||
| 343 | - None => { | ||
| 344 | - self.next_edge()?; | ||
| 345 | - self.next() | ||
| 346 | - }, | ||
| 347 | - } | ||
| 348 | - }, | ||
| 349 | - VertexIteratorMode::Vertex => { | ||
| 350 | - let current = self.current?; | ||
| 351 | - self.current = self.p.next_y(current, self.direction); | ||
| 352 | - Some(self.p.vertex(current)) | ||
| 353 | - }, | ||
| 354 | - } | ||
| 355 | - } | ||
| 356 | -} | ||
| 357 | - | ||
| 358 | -impl<T> Polygon<T> | ||
| 359 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 360 | - + Copy + Debug + From<i32> { | ||
| 361 | - #[inline] | ||
| 362 | - fn vertex(&self, v :usize) -> Coordinate<T> { | ||
| 363 | - let Polygon(Coordinates(cs)) = self; | ||
| 364 | - cs[v] | ||
| 365 | - } | ||
| 366 | - | ||
| 367 | - fn vert_min<'a>(&'a self, d :Direction) -> usize { | ||
| 368 | - let Polygon(Coordinates(cs)) = self; | ||
| 369 | - | ||
| 370 | - type ICoord<'a,T> = (usize, &'a Coordinate<T>); | ||
| 371 | - | ||
| 372 | - // TODO I guess the problem here is that it does not account for the | ||
| 373 | - // same y vertex on the beggining and the end. So i guess correct | ||
| 374 | - // would be finding the first one and then dependings on the | ||
| 375 | - // given direction either search left or right for same y's. | ||
| 376 | - let fold = |acc :Option<ICoord<'a,T>>, x :ICoord<'a,T>| | ||
| 377 | - match acc { | ||
| 378 | - None => Some(x), | ||
| 379 | - Some(a) => { | ||
| 380 | - let Coordinate(_, ay, _) = a.1; | ||
| 381 | - let Coordinate(_, xy, _) = x.1; | ||
| 382 | - if xy < ay {Some(x)} else {Some(a)} | ||
| 383 | - }, | ||
| 384 | - }; | ||
| 385 | - | ||
| 386 | - let mut min = cs.iter().enumerate().fold(None, fold).unwrap().0; | ||
| 387 | - let mut next = self.step(min, d); | ||
| 388 | - | ||
| 389 | - while self.vertex(min).1 == self.vertex(next).1 { | ||
| 390 | - min = next; | ||
| 391 | - next = self.step(min, d); | ||
| 392 | - } | ||
| 393 | - | ||
| 394 | - min | ||
| 395 | - } | ||
| 396 | - | ||
| 397 | - fn left_edge(&self) -> VertexIterator<T> { | ||
| 398 | - VertexIterator::edge(self, Direction::Left) | ||
| 399 | - } | ||
| 400 | - | ||
| 401 | - fn right_edge(&self) -> VertexIterator<T> { | ||
| 402 | - VertexIterator::edge(self, Direction::Right) | ||
| 403 | - } | ||
| 404 | - | ||
| 405 | - fn left_vertices(&self) -> VertexIterator<T> { | ||
| 406 | - VertexIterator::vertex(self, Direction::Left) | ||
| 407 | - } | ||
| 408 | - | ||
| 409 | - fn right_vertices(&self) -> VertexIterator<T> { | ||
| 410 | - VertexIterator::vertex(self, Direction::Right) | ||
| 411 | - } | ||
| 412 | - | ||
| 413 | - fn left(&self, v :usize) -> usize { | ||
| 414 | - let Polygon(Coordinates(cs)) = self; | ||
| 415 | - | ||
| 416 | - match v { | ||
| 417 | - 0 => cs.len() - 1, | ||
| 418 | - _ => v - 1, | ||
| 419 | - } | ||
| 420 | - } | ||
| 421 | - | ||
| 422 | - fn right(&self, v :usize) -> usize { | ||
| 423 | - let Polygon(Coordinates(cs)) = self; | ||
| 424 | - | ||
| 425 | - (v + 1) % cs.len() | ||
| 426 | - } | ||
| 427 | - | ||
| 428 | - fn step(&self, v :usize, d :Direction) -> usize { | ||
| 429 | - match d { | ||
| 430 | - Direction::Left => self.left(v), | ||
| 431 | - Direction::Right => self.right(v), | ||
| 432 | - } | ||
| 433 | - } | ||
| 434 | - | ||
| 435 | - fn next_y(&self, c :usize, d :Direction) -> Option<usize> { | ||
| 436 | - fn inner<T>( p :&Polygon<T> | ||
| 437 | - , c :usize | ||
| 438 | - , n :usize | ||
| 439 | - , d :Direction) -> Option<usize> | ||
| 440 | - where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 441 | - + Copy + Debug + From<i32> { | ||
| 442 | - if c == n { | ||
| 443 | - None | ||
| 444 | - } else { | ||
| 445 | - let Coordinate(_, cy, _) = p.vertex(c); | ||
| 446 | - let Coordinate(_, ny, _) = p.vertex(n); | ||
| 447 | - | ||
| 448 | - if ny < cy { None } else { Some(n) } | ||
| 449 | - } | ||
| 450 | - } | ||
| 451 | - | ||
| 452 | - inner(self, c, self.step(c, d), d) | ||
| 453 | - } | ||
| 454 | - | ||
| 455 | - pub fn debug(&self) { | ||
| 456 | - let mut left = self.left_vertices(); | ||
| 457 | - let mut right = self.right_vertices(); | ||
| 458 | - | ||
| 459 | - if left.find(|l| right.find(|r| l.0 == r.0).is_some()).is_some() { | ||
| 460 | - let left :Vec<Coordinate<T>> = self.left_vertices().collect(); | ||
| 461 | - let right :Vec<Coordinate<T>> = self.right_vertices().collect(); | ||
| 462 | - | ||
| 463 | - println!("==="); | ||
| 464 | - println!("== poly : {:?}", self); | ||
| 465 | - println!("== ltop : {:?}", self.vert_min(Direction::Left)); | ||
| 466 | - println!("== rtop : {:?}", self.vert_min(Direction::Right)); | ||
| 467 | - println!("== left : {:?}", left); | ||
| 468 | - println!("== right : {:?}", right); | ||
| 469 | - println!("==="); | ||
| 470 | - } | ||
| 471 | - } | ||
| 472 | -} | ||
| 473 | - | ||
| 474 | -impl<T> Drawable<T> for Polygon<T> | ||
| 475 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 476 | - + Debug + Clone + Copy + From<i32> { | ||
| 477 | - fn plot(&self) -> Coordinates<T> { | ||
| 478 | - let Polygon(Coordinates(cs)) = self; | ||
| 479 | - | ||
| 480 | - match cs[..] { | ||
| 481 | - [] => Coordinates(Vec::<Coordinate<T>>::new()), | ||
| 482 | - [a] => Coordinates(vec!(a)), | ||
| 483 | - [a, b] => Coordinates(a.line(&b)), | ||
| 484 | - _ => { | ||
| 485 | - let (a, b) = (cs[0], cs[1]); | ||
| 486 | - let mut r = a.line(&b); | ||
| 487 | - let mut i = b; | ||
| 488 | - for j in cs[2..].iter() { | ||
| 489 | - r.append(&mut i.line(j)[1..].to_vec()); | ||
| 490 | - i = *j; | ||
| 491 | - } | ||
| 492 | - let mut j = a.line(&i); | ||
| 493 | - let l = j.len(); | ||
| 494 | - if l > 1 { | ||
| 495 | - r.append(&mut j[1..l-1].to_vec()); | ||
| 496 | - } | ||
| 497 | - Coordinates(r) | ||
| 498 | - }, | ||
| 499 | - } | ||
| 500 | - } | ||
| 501 | -} | ||
| 502 | - | ||
| 503 | -impl<T> Fillable<T> for Polygon<T> | ||
| 504 | -where T: Add<Output = T> + Sub<Output = T> + Div<Output = T> | ||
| 505 | - + Debug + Clone + Copy + From<i32> { | ||
| 506 | - fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32) { | ||
| 507 | - let scanlines = self.left_edge().zip(self.right_edge()); | ||
| 508 | - | ||
| 509 | - for l in scanlines.flat_map(|(l, r)| l.line_iter(&r)) { | ||
| 510 | - canvas.set_pixel(l, color); | ||
| 511 | - } | ||
| 512 | - } | ||
| 513 | -} | ||
| 514 | - | ||
| 515 | -impl<T> Display for Polygon<T> where T: Copy { | ||
| 516 | - fn fmt(&self, f: &mut Formatter<'_>) -> Result { | ||
| 517 | - let Polygon(a) = self; | ||
| 518 | - write!(f, "Poly[{}]", a) | ||
| 519 | - } | ||
| 520 | -} |
src/easel3d_wasm.rs
0 → 100644
| 1 | +use std::sync::mpsc; | ||
| 2 | + | ||
| 3 | +use easel3d::easel::canvas::{Canvas, Vertex}; | ||
| 4 | +use easel3d::easel::drawable::Drawable; | ||
| 5 | + | ||
| 6 | +#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 7 | +pub struct Color(u8, u8, u8, u8); | ||
| 8 | + | ||
| 9 | +pub struct WasmCanvas { width :u16 | ||
| 10 | + , height :u16 | ||
| 11 | + , size :usize | ||
| 12 | + , zbuf :Vec<f64> | ||
| 13 | + , image :Vec<Color> } | ||
| 14 | + | ||
| 15 | +impl WasmCanvas { | ||
| 16 | + pub fn new(width :u16, height :u16) -> Self { | ||
| 17 | + let size = width as usize * height as usize; | ||
| 18 | + | ||
| 19 | + Self { width: width | ||
| 20 | + , height: height | ||
| 21 | + , size: size | ||
| 22 | + , zbuf: vec!(0.0; size) | ||
| 23 | + , image: vec!(Color(0, 0, 0, 0xFF); size) } | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + pub fn image(&self) -> *const Color { | ||
| 27 | + self.image.as_ptr() | ||
| 28 | + } | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +impl Canvas<f64> for WasmCanvas { | ||
| 32 | + #[inline] | ||
| 33 | + fn width(&self) -> u16 { | ||
| 34 | + self.width | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + #[inline] | ||
| 38 | + fn height(&self) -> u16 { | ||
| 39 | + self.height | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + fn clear(&mut self) { | ||
| 43 | + self.zbuf = vec!(0.0; self.size); | ||
| 44 | + self.image = vec!(Color(0, 0, 0, 0xFF); self.size); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + fn set_pixel(&mut self, c :Vertex<f64>, color :u32) { | ||
| 48 | + let (x, y, zr) = c.as_tuple(); | ||
| 49 | + let idx :usize = (y * (self.width as i32) + x) as usize; | ||
| 50 | + | ||
| 51 | + let r = ((color >> 16) & 0xFF) as u8; | ||
| 52 | + let g = ((color >> 8) & 0xFF) as u8; | ||
| 53 | + let b = ( color & 0xFF) as u8; | ||
| 54 | + | ||
| 55 | + if self.zbuf[idx] < zr { | ||
| 56 | + self.zbuf[idx] = zr; | ||
| 57 | + self.image[idx] = Color(r, g, b, 0xFF); | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + // Empty implementations for now… mostly not needed because it is | ||
| 62 | + // done from JavaScript… | ||
| 63 | + fn init_events(&self) {} | ||
| 64 | + fn start_events(&self, _ :mpsc::Sender<i32>) {} | ||
| 65 | + fn draw(&mut self, _ :&dyn Drawable<f64>, _ :u32) {} | ||
| 66 | + fn put_text(&self, _ :Vertex<f64>, _ :&str) {} | ||
| 67 | + fn show(&self) {} | ||
| 68 | +} |
src/geometry.rs
deleted
100644 → 0
| 1 | -// | ||
| 2 | -// Basic geometric things... | ||
| 3 | -// | ||
| 4 | -// Georg Hopp <georg@steffers.org> | ||
| 5 | -// | ||
| 6 | -// Copyright © 2019 Georg Hopp | ||
| 7 | -// | ||
| 8 | -// This program is free software: you can redistribute it and/or modify | ||
| 9 | -// it under the terms of the GNU General Public License as published by | ||
| 10 | -// the Free Software Foundation, either version 3 of the License, or | ||
| 11 | -// (at your option) any later version. | ||
| 12 | -// | ||
| 13 | -// This program is distributed in the hope that it will be useful, | ||
| 14 | -// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | -// GNU General Public License for more details. | ||
| 17 | -// | ||
| 18 | -// You should have received a copy of the GNU General Public License | ||
| 19 | -// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | -// | ||
| 21 | -use std::convert::{From, Into}; | ||
| 22 | -use std::ops::{Add,Sub,Neg,Mul,Div}; | ||
| 23 | -use std::fmt::Debug; | ||
| 24 | - | ||
| 25 | -use crate::easel::{Canvas, Coordinate, Coordinates, Polygon}; | ||
| 26 | -use crate::transform::{TMatrix, Transformable}; | ||
| 27 | -use crate::trigonometry::Trig; | ||
| 28 | -use crate::vector::Vector; | ||
| 29 | - | ||
| 30 | -#[derive(Debug, Clone)] | ||
| 31 | -pub struct Face<T> | ||
| 32 | -where T: Add + Sub + Neg + Mul + Div + Copy + Trig { | ||
| 33 | - corners :Vec<usize>, | ||
| 34 | - normal :Option<Vector<T>>, | ||
| 35 | -} | ||
| 36 | - | ||
| 37 | -#[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 38 | -pub struct Point<T>(pub Vector<T>, T) | ||
| 39 | - where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig; | ||
| 40 | - | ||
| 41 | -impl<T> Point<T> | ||
| 42 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 43 | - + Mul<Output = T> + Div<Output = T> | ||
| 44 | - + PartialEq + Trig + Copy + From<i32> { | ||
| 45 | - pub fn new(x :T, y :T, z :T) -> Self { | ||
| 46 | - Self(Vector(x, y, z), 1.into()) | ||
| 47 | - } | ||
| 48 | -} | ||
| 49 | - | ||
| 50 | -impl<T> Add for Point<T> | ||
| 51 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 52 | - + Mul<Output = T> + Div<Output = T> | ||
| 53 | - + PartialEq + Trig + Copy { | ||
| 54 | - type Output = Self; | ||
| 55 | - | ||
| 56 | - fn add(self, other :Self) -> Self { | ||
| 57 | - let Point(v1, w1) = self; | ||
| 58 | - let Point(v2, w2) = other; | ||
| 59 | - Self(v1 + v2, w1 + w2) | ||
| 60 | - } | ||
| 61 | -} | ||
| 62 | - | ||
| 63 | -impl<T> Neg for Point<T> | ||
| 64 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 65 | - + Mul<Output = T> + Div<Output = T> | ||
| 66 | - + PartialEq + Trig + Copy { | ||
| 67 | - type Output = Self; | ||
| 68 | - | ||
| 69 | - fn neg(self) -> Self { | ||
| 70 | - let Point(v, w) = self; | ||
| 71 | - Self(-v, -w) | ||
| 72 | - } | ||
| 73 | -} | ||
| 74 | - | ||
| 75 | -impl<T> Sub for Point<T> | ||
| 76 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 77 | - + Mul<Output = T> + Div<Output = T> | ||
| 78 | - + PartialEq + Trig + Copy { | ||
| 79 | - type Output = Self; | ||
| 80 | - | ||
| 81 | - fn sub(self, other :Self) -> Self { | ||
| 82 | - self + -other | ||
| 83 | - } | ||
| 84 | -} | ||
| 85 | - | ||
| 86 | -impl<T> Mul for Point<T> | ||
| 87 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 88 | - + Mul<Output = T> + Div<Output = T> | ||
| 89 | - + PartialEq + Trig + Copy + From<i32> { | ||
| 90 | - type Output = Self; | ||
| 91 | - | ||
| 92 | - fn mul(self, other :Self) -> Self { | ||
| 93 | - let a :Vector<T> = self.into(); | ||
| 94 | - let b :Vector<T> = other.into(); | ||
| 95 | - | ||
| 96 | - Point(a * b, 1.into()) | ||
| 97 | - } | ||
| 98 | -} | ||
| 99 | - | ||
| 100 | -impl<T> From<Vector<T>> for Point<T> | ||
| 101 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 102 | - + Mul<Output = T> + Div<Output = T> | ||
| 103 | - + PartialEq + Trig + Copy + From<i32> { | ||
| 104 | - fn from(v :Vector<T>) -> Self { | ||
| 105 | - Point(v, 1.into()) | ||
| 106 | - } | ||
| 107 | -} | ||
| 108 | - | ||
| 109 | -impl<T> Into<Vector<T>> for Point<T> | ||
| 110 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 111 | - + Mul<Output = T> + Div<Output = T> | ||
| 112 | - + PartialEq + Trig + Copy + From<i32> { | ||
| 113 | - fn into(self) -> Vector<T> { | ||
| 114 | - let Point(v, w) = self; | ||
| 115 | - | ||
| 116 | - if w == 0.into() { | ||
| 117 | - v | ||
| 118 | - } else { | ||
| 119 | - v.mul(&w.recip()) | ||
| 120 | - } | ||
| 121 | - } | ||
| 122 | -} | ||
| 123 | - | ||
| 124 | -impl<T> Transformable<T> for Point<T> | ||
| 125 | -where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> | ||
| 126 | - + Mul<Output = T> + Div<Output = T> | ||
| 127 | - + PartialEq + Debug + Trig + Copy + From<i32> { | ||
| 128 | - fn transform(&self, m :&TMatrix<T>) -> Self { | ||
| 129 | - let Point(v, w) = *self; | ||
| 130 | - let (v, w) = m.apply(&v, w); | ||
| 131 | - | ||
| 132 | - if w == 0.into() { | ||
| 133 | - v.into() | ||
| 134 | - } else { | ||
| 135 | - v.mul(&w.recip()).into() | ||
| 136 | - } | ||
| 137 | - } | ||
| 138 | -} | ||
| 139 | - | ||
| 140 | -#[derive(Debug)] | ||
| 141 | -pub struct Polyeder<T> | ||
| 142 | -where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig { | ||
| 143 | - points :Vec<Point<T>>, | ||
| 144 | - faces :Vec<Face<T>>, | ||
| 145 | -} | ||
| 146 | - | ||
| 147 | -pub trait Primitives<T> | ||
| 148 | -where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> { | ||
| 149 | - fn transform(&self, m :&TMatrix<T>) -> Self; | ||
| 150 | - fn project( &self | ||
| 151 | - , camera :&Camera<T> | ||
| 152 | - , light :&DirectLight<T> | ||
| 153 | - , col :u32 ) -> Vec<(Polygon<T>, u32)>; | ||
| 154 | -} | ||
| 155 | - | ||
| 156 | -#[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; | 1 | +extern crate easel3d; |
| 2 | 2 | ||
| 3 | -pub type Error = &'static str; | 3 | +mod easel3d_wasm; |
| 4 | +use easel3d_wasm::{WasmCanvas, Color}; | ||
| 4 | 5 | ||
| 5 | -pub mod easel; | ||
| 6 | -pub mod transform; | ||
| 7 | -pub mod trigonometry; | ||
| 8 | -pub mod vector; | ||
| 9 | -pub mod geometry; | 6 | +use easel3d::easel::canvas::Canvas; |
| 7 | +use easel3d::easel::fillable::Fillable; | ||
| 10 | 8 | ||
| 11 | -mod utils; | 9 | +use easel3d::math::transform::TMatrix; |
| 10 | +use easel3d::math::vector::Vector; | ||
| 12 | 11 | ||
| 13 | -use vector::Vector; | ||
| 14 | -use easel::{Canvas, Coordinate, Drawable, Fillable}; | ||
| 15 | -use geometry::{Camera, DirectLight, Polyeder, Primitives}; | ||
| 16 | -use transform::{TMatrix}; | 12 | +use easel3d::space::camera::Camera; |
| 13 | +use easel3d::space::light::DirectLight; | ||
| 14 | +use easel3d::space::polyeder::Polyeder; | ||
| 15 | +use easel3d::space::primitives::Primitives; | ||
| 17 | 16 | ||
| 18 | -use std::fmt::{Display, Formatter, Result}; | ||
| 19 | -use std::sync::mpsc; | ||
| 20 | -use std::time::Instant; | ||
| 21 | use wasm_bindgen::prelude::*; | 17 | use wasm_bindgen::prelude::*; |
| 22 | 18 | ||
| 23 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global | 19 | // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global |
| @@ -27,56 +23,37 @@ use wasm_bindgen::prelude::*; | @@ -27,56 +23,37 @@ use wasm_bindgen::prelude::*; | ||
| 27 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; | 23 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
| 28 | 24 | ||
| 29 | #[wasm_bindgen] | 25 | #[wasm_bindgen] |
| 30 | -#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 31 | -pub struct Color(u8, u8, u8, u8); | ||
| 32 | - | ||
| 33 | -#[wasm_bindgen] | ||
| 34 | -pub struct View3d { width :u16 | ||
| 35 | - , height :u16 | ||
| 36 | - , size :usize | 26 | +pub struct View3d { canvas :WasmCanvas |
| 37 | , degree :i32 | 27 | , degree :i32 |
| 38 | - //, start :Instant | ||
| 39 | , tetrahedron :Polyeder<f64> | 28 | , tetrahedron :Polyeder<f64> |
| 40 | , cube :Polyeder<f64> | 29 | , cube :Polyeder<f64> |
| 41 | - , camera :Option<Camera<f64>> | ||
| 42 | - , light :DirectLight<f64> | ||
| 43 | - , zbuf :Vec<f64> | ||
| 44 | - , image :Vec<Color> | ||
| 45 | -} | 30 | + , camera :Camera<f64> |
| 31 | + , light :DirectLight<f64> } | ||
| 46 | 32 | ||
| 47 | #[wasm_bindgen] | 33 | #[wasm_bindgen] |
| 48 | impl View3d { | 34 | impl View3d { |
| 49 | pub fn new(width :u16, height :u16) -> Self { | 35 | 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); | 36 | let light_vector = Vector(0.0, 0.0, 1.0); |
| 37 | + let canvas = WasmCanvas::new(width, height); | ||
| 38 | + let camera = Camera::<f64>::new(&canvas, 45); | ||
| 52 | 39 | ||
| 53 | - let mut view3d = Self { width: width | ||
| 54 | - , height: height | ||
| 55 | - , size: size | ||
| 56 | - , degree: 0 | ||
| 57 | - //, start: Instant::now() | ||
| 58 | - , tetrahedron: Polyeder::tetrahedron(100.0) | ||
| 59 | - , cube: Polyeder::cube(56.25) | ||
| 60 | - , camera: None | ||
| 61 | - , light: DirectLight::new(light_vector) | ||
| 62 | - , zbuf: vec!(0.0; size) | ||
| 63 | - , image: vec!(Color(0, 0, 0, 0xFF); size) | ||
| 64 | - }; | ||
| 65 | - | ||
| 66 | - view3d.camera = Some(Camera::<f64>::new(&view3d, 45)); | ||
| 67 | - view3d | 40 | + Self { canvas: canvas |
| 41 | + , degree: 0 | ||
| 42 | + , tetrahedron: Polyeder::tetrahedron(100.0) | ||
| 43 | + , cube: Polyeder::cube(56.25) | ||
| 44 | + , camera: camera | ||
| 45 | + , light: DirectLight::new(light_vector) } | ||
| 68 | } | 46 | } |
| 69 | 47 | ||
| 70 | pub fn width(&self) -> u16 { | 48 | pub fn width(&self) -> u16 { |
| 71 | - self.width | 49 | + self.canvas.width() |
| 72 | } | 50 | } |
| 73 | 51 | ||
| 74 | pub fn height(&self) -> u16 { | 52 | pub fn height(&self) -> u16 { |
| 75 | - self.height | 53 | + self.canvas.height() |
| 76 | } | 54 | } |
| 77 | 55 | ||
| 78 | pub fn update(&mut self) { | 56 | pub fn update(&mut self) { |
| 79 | - //let deg = ((self.start.elapsed() / 25).as_millis() % 360) as i32; | ||
| 80 | let t = TMatrix::translate(Vector(0.0, 0.0, 150.0)); | 57 | let t = TMatrix::translate(Vector(0.0, 0.0, 150.0)); |
| 81 | let rz = TMatrix::rotate_z(self.degree); | 58 | let rz = TMatrix::rotate_z(self.degree); |
| 82 | let rx = TMatrix::rotate_x(-self.degree*2); | 59 | let rx = TMatrix::rotate_x(-self.degree*2); |
| @@ -88,176 +65,22 @@ impl View3d { | @@ -88,176 +65,22 @@ impl View3d { | ||
| 88 | let objects = vec!( (self.tetrahedron.transform(&rot1), 0xFFFF00) | 65 | let objects = vec!( (self.tetrahedron.transform(&rot1), 0xFFFF00) |
| 89 | , ( self.cube.transform(&rot2), 0x0000FF) ); | 66 | , ( self.cube.transform(&rot2), 0x0000FF) ); |
| 90 | 67 | ||
| 91 | - self.degree = (self.degree + 1) % 360; | ||
| 92 | - | ||
| 93 | - self.clear(); | ||
| 94 | - | ||
| 95 | - match self.camera { | ||
| 96 | - None => {}, | ||
| 97 | - Some(camera) => { | ||
| 98 | - for (o, color) in objects { | ||
| 99 | - for (pg, c) in o.project(&camera, &self.light, color) { | ||
| 100 | - (&pg).fill(self, c); | ||
| 101 | - } | ||
| 102 | - } | ||
| 103 | - }, | ||
| 104 | - } | ||
| 105 | - } | ||
| 106 | - | ||
| 107 | - pub fn image(&self) -> *const Color { | ||
| 108 | - self.image.as_ptr() | ||
| 109 | - } | ||
| 110 | -} | ||
| 111 | - | ||
| 112 | -impl Canvas<f64> for View3d { | ||
| 113 | - fn width(&self) -> u16 { | ||
| 114 | - self.width | ||
| 115 | - } | ||
| 116 | - | ||
| 117 | - fn height(&self) -> u16 { | ||
| 118 | - self.height | ||
| 119 | - } | ||
| 120 | - | ||
| 121 | - fn clear(&mut self) { | ||
| 122 | - self.zbuf = vec!(0.0; self.size); | ||
| 123 | - self.image = vec!(Color(0, 0, 0, 0xFF); self.size); | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - fn set_pixel(&mut self, c :Coordinate<f64>, color :u32) { | ||
| 127 | - let Coordinate(x, y, zr) = c; | ||
| 128 | - let idx :usize = (y * (self.width as i32) + x) as usize; | ||
| 129 | - | ||
| 130 | - let r = ((color >> 16) & 0xFF) as u8; | ||
| 131 | - let g = ((color >> 8) & 0xFF) as u8; | ||
| 132 | - let b = ( color & 0xFF) as u8; | ||
| 133 | - | ||
| 134 | - if self.zbuf[idx] < zr { | ||
| 135 | - self.zbuf[idx] = zr; | ||
| 136 | - self.image[idx] = Color(r, g, b, 0xFF); | ||
| 137 | - } | ||
| 138 | - } | ||
| 139 | - | ||
| 140 | - // Empty implementations for now… mostly not needed because it is | ||
| 141 | - // done from JavaScript… | ||
| 142 | - fn init_events(&self) {} | ||
| 143 | - fn start_events(&self, _ :mpsc::Sender<i32>) {} | ||
| 144 | - fn draw( &mut self, _ :&dyn Drawable<f64>, _ :Coordinate<f64>, _ :u32 ) {} | ||
| 145 | - fn put_text(&self, _ :Coordinate<f64>, _ :&str) {} | ||
| 146 | - fn show(&self) {} | ||
| 147 | -} | ||
| 148 | - | ||
| 149 | -#[wasm_bindgen] | ||
| 150 | -#[repr(u8)] | ||
| 151 | -#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| 152 | -pub enum Cell { | ||
| 153 | - Dead = 0, | ||
| 154 | - Alive = 1, | ||
| 155 | -} | ||
| 156 | - | ||
| 157 | -#[wasm_bindgen] | ||
| 158 | -pub struct Universe { | ||
| 159 | - width :u32, | ||
| 160 | - height :u32, | ||
| 161 | - cells :Vec<Cell>, | ||
| 162 | -} | ||
| 163 | - | ||
| 164 | -#[wasm_bindgen] | ||
| 165 | -impl Universe { | ||
| 166 | - pub fn new() -> Universe { | ||
| 167 | - let width = 64; | ||
| 168 | - let height = 64; | ||
| 169 | - | ||
| 170 | - let init_cells = |i :u32| { | ||
| 171 | - if i % 2 == 0 || i % 7 == 0 { Cell::Alive } else { Cell::Dead } | ||
| 172 | - }; | ||
| 173 | - | ||
| 174 | - let cells = (0..width * height).map(init_cells).collect(); | ||
| 175 | - | ||
| 176 | - Universe { | ||
| 177 | - width: width, | ||
| 178 | - height: height, | ||
| 179 | - cells: cells, | ||
| 180 | - } | ||
| 181 | - } | ||
| 182 | - | ||
| 183 | - pub fn width(&self) -> u32 { | ||
| 184 | - self.width | ||
| 185 | - } | ||
| 186 | - | ||
| 187 | - pub fn height(&self) -> u32 { | ||
| 188 | - self.height | ||
| 189 | - } | ||
| 190 | - | ||
| 191 | - pub fn cells(&self) -> *const Cell { | ||
| 192 | - self.cells.as_ptr() | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - pub fn render(&self) -> String { | ||
| 196 | - self.to_string() | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - pub fn tick(&mut self) { | ||
| 200 | - let mut next = self.cells.clone(); | 68 | + let rlx = TMatrix::rotate_x(-self.degree/4); |
| 69 | + let rly = TMatrix::rotate_y(-self.degree/1); | ||
| 70 | + let light = self.light.transform(&TMatrix::combine(vec!(rlx, rly))); | ||
| 201 | 71 | ||
| 202 | - for row in 0..self.height { | ||
| 203 | - for col in 0..self.width { | ||
| 204 | - let idx = self.get_index(row, col); | ||
| 205 | - let cell = self.cells[idx]; | ||
| 206 | - let live_neighbors = self.live_neighbor_count(row, col); | 72 | + self.degree = (self.degree + 1) % (4*360); |
| 207 | 73 | ||
| 208 | - // Game of life rules.... | ||
| 209 | - let next_cell = match (cell, live_neighbors) { | ||
| 210 | - (Cell::Alive, 2) | | ||
| 211 | - (Cell::Alive, 3) => Cell::Alive, | ||
| 212 | - (Cell::Alive, _) => Cell::Dead, | ||
| 213 | - ( Cell::Dead, 3) => Cell::Alive, | ||
| 214 | - ( otherwise, _) => otherwise, | ||
| 215 | - }; | 74 | + self.canvas.clear(); |
| 216 | 75 | ||
| 217 | - next[idx] = next_cell; | 76 | + for (o, color) in objects { |
| 77 | + for (pg, c) in o.project(&self.camera, &light, color) { | ||
| 78 | + (&pg).fill(&mut self.canvas, c); | ||
| 218 | } | 79 | } |
| 219 | } | 80 | } |
| 220 | - | ||
| 221 | - self.cells = next; | ||
| 222 | - } | ||
| 223 | - | ||
| 224 | - fn get_index(&self, row :u32, col :u32) -> usize { | ||
| 225 | - (row * self.width + col) as usize | ||
| 226 | } | 81 | } |
| 227 | 82 | ||
| 228 | - fn live_neighbor_count(&self, row :u32, col :u32) -> u8 { | ||
| 229 | - let mut count = 0; | ||
| 230 | - | ||
| 231 | - for delta_row in [self.height - 1, 0, 1].iter().cloned() { | ||
| 232 | - for delta_col in [self.width - 1, 0, 1].iter().cloned() { | ||
| 233 | - if delta_row == 0 && delta_col == 0 { | ||
| 234 | - continue; | ||
| 235 | - } | ||
| 236 | - | ||
| 237 | - let neighbor_row = (row + delta_row) % self.height; | ||
| 238 | - let neighbor_col = (col + delta_col) % self.width; | ||
| 239 | - let idx = self.get_index(neighbor_row, neighbor_col); | ||
| 240 | - count += self.cells[idx] as u8; | ||
| 241 | - } | ||
| 242 | - } | ||
| 243 | - | ||
| 244 | - count | ||
| 245 | - } | ||
| 246 | -} | ||
| 247 | - | ||
| 248 | -impl Display for Universe { | ||
| 249 | - fn fmt(&self, f :&mut Formatter) -> Result { | ||
| 250 | - for line in self.cells.as_slice().chunks(self.width as usize) { | ||
| 251 | - for &cell in line { | ||
| 252 | - let symbol = match cell { | ||
| 253 | - Cell::Dead => ' ', | ||
| 254 | - Cell::Alive => '*', | ||
| 255 | - }; | ||
| 256 | - write!(f, "{}", symbol)?; | ||
| 257 | - } | ||
| 258 | - write!(f, "\n")?; | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - Ok(()) | 83 | + pub fn image(&self) -> *const Color { |
| 84 | + self.canvas.image() | ||
| 262 | } | 85 | } |
| 263 | } | 86 | } |
src/transform.rs
deleted
100644 → 0
| 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 | -} |
src/trigonometry.rs
deleted
100644 → 0
| 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 | -} |
src/utils.rs
deleted
100644 → 0
| 1 | -pub fn set_panic_hook() { | ||
| 2 | - // When the `console_error_panic_hook` feature is enabled, we can call the | ||
| 3 | - // `set_panic_hook` function at least once during initialization, and then | ||
| 4 | - // we will get better error messages if our code ever panics. | ||
| 5 | - // | ||
| 6 | - // For more details see | ||
| 7 | - // https://github.com/rustwasm/console_error_panic_hook#readme | ||
| 8 | - #[cfg(feature = "console_error_panic_hook")] | ||
| 9 | - console_error_panic_hook::set_once(); | ||
| 10 | -} |
src/vector.rs
deleted
100644 → 0
| 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 | -} |
www/.bin/create-wasm-app.js
deleted
100755 → 0
| 1 | -#!/usr/bin/env node | ||
| 2 | - | ||
| 3 | -const { spawn } = require("child_process"); | ||
| 4 | -const fs = require("fs"); | ||
| 5 | - | ||
| 6 | -let folderName = '.'; | ||
| 7 | - | ||
| 8 | -if (process.argv.length >= 3) { | ||
| 9 | - folderName = process.argv[2]; | ||
| 10 | - if (!fs.existsSync(folderName)) { | ||
| 11 | - fs.mkdirSync(folderName); | ||
| 12 | - } | ||
| 13 | -} | ||
| 14 | - | ||
| 15 | -const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]); | ||
| 16 | - | ||
| 17 | -clone.on("close", code => { | ||
| 18 | - if (code !== 0) { | ||
| 19 | - console.error("cloning the template failed!") | ||
| 20 | - process.exit(code); | ||
| 21 | - } else { | ||
| 22 | - console.log("🦀 Rust + 🕸 Wasm = ❤"); | ||
| 23 | - } | ||
| 24 | -}); |
www/README.md
deleted
100644 → 0
| 1 | -<div align="center"> | ||
| 2 | - | ||
| 3 | - <h1><code>create-wasm-app</code></h1> | ||
| 4 | - | ||
| 5 | - <strong>An <code>npm init</code> template for kick starting a project that uses NPM packages containing Rust-generated WebAssembly and bundles them with Webpack.</strong> | ||
| 6 | - | ||
| 7 | - <p> | ||
| 8 | - <a href="https://travis-ci.org/rustwasm/create-wasm-app"><img src="https://img.shields.io/travis/rustwasm/create-wasm-app.svg?style=flat-square" alt="Build Status" /></a> | ||
| 9 | - </p> | ||
| 10 | - | ||
| 11 | - <h3> | ||
| 12 | - <a href="#usage">Usage</a> | ||
| 13 | - <span> | </span> | ||
| 14 | - <a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a> | ||
| 15 | - </h3> | ||
| 16 | - | ||
| 17 | - <sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub> | ||
| 18 | -</div> | ||
| 19 | - | ||
| 20 | -## About | ||
| 21 | - | ||
| 22 | -This template is designed for depending on NPM packages that contain | ||
| 23 | -Rust-generated WebAssembly and using them to create a Website. | ||
| 24 | - | ||
| 25 | -* Want to create an NPM package with Rust and WebAssembly? [Check out | ||
| 26 | - `wasm-pack-template`.](https://github.com/rustwasm/wasm-pack-template) | ||
| 27 | -* Want to make a monorepo-style Website without publishing to NPM? Check out | ||
| 28 | - [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) | ||
| 29 | - and/or | ||
| 30 | - [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). | ||
| 31 | - | ||
| 32 | -## 🚴 Usage | ||
| 33 | - | ||
| 34 | -``` | ||
| 35 | -npm init wasm-app | ||
| 36 | -``` | ||
| 37 | - | ||
| 38 | -## 🔋 Batteries Included | ||
| 39 | - | ||
| 40 | -- `.gitignore`: ignores `node_modules` | ||
| 41 | -- `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you | ||
| 42 | -- `README.md`: the file you are reading now! | ||
| 43 | -- `index.html`: a bare bones html document that includes the webpack bundle | ||
| 44 | -- `index.js`: example js file with a comment showing how to import and use a wasm pkg | ||
| 45 | -- `package.json` and `package-lock.json`: | ||
| 46 | - - pulls in devDependencies for using webpack: | ||
| 47 | - - [`webpack`](https://www.npmjs.com/package/webpack) | ||
| 48 | - - [`webpack-cli`](https://www.npmjs.com/package/webpack-cli) | ||
| 49 | - - [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server) | ||
| 50 | - - defines a `start` script to run `webpack-dev-server` | ||
| 51 | -- `webpack.config.js`: configuration file for bundling your js with webpack | ||
| 52 | - | ||
| 53 | -## License | ||
| 54 | - | ||
| 55 | -Licensed under either of | ||
| 56 | - | ||
| 57 | -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) | ||
| 58 | -* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) | ||
| 59 | - | ||
| 60 | -at your option. | ||
| 61 | - | ||
| 62 | -### Contribution | ||
| 63 | - | ||
| 64 | -Unless you explicitly state otherwise, any contribution intentionally | ||
| 65 | -submitted for inclusion in the work by you, as defined in the Apache-2.0 | ||
| 66 | -license, shall be dual licensed as above, without any additional terms or | ||
| 67 | -conditions. |
| 1 | -import { View3d } from "wasm-game-of-life"; | ||
| 2 | -import { memory } from "wasm-game-of-life/wasm_game_of_life_bg"; | 1 | +import { View3d } from "easel3d-wasm"; |
| 2 | +import { memory } from "easel3d-wasm/easel3d_wasm_bg"; | ||
| 3 | 3 | ||
| 4 | // 3D canvas stuff | 4 | // 3D canvas stuff |
| 5 | -const view3d = View3d.new(300, 300); | 5 | +const view3d = View3d.new(301, 301); |
| 6 | 6 | ||
| 7 | const view3d_canvas = document.getElementById("view3d"); | 7 | const view3d_canvas = document.getElementById("view3d"); |
| 8 | view3d_canvas.width = view3d.width(); | 8 | view3d_canvas.width = view3d.width(); |
| @@ -17,7 +17,6 @@ const view3d_renderLoop = () => { | @@ -17,7 +17,6 @@ const view3d_renderLoop = () => { | ||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | const drawView3d = () => { | 19 | const drawView3d = () => { |
| 20 | - const view3d_imagePtr = view3d.image(); | ||
| 21 | const view3d_image = new ImageData( | 20 | const view3d_image = new ImageData( |
| 22 | new Uint8ClampedArray( memory.buffer | 21 | new Uint8ClampedArray( memory.buffer |
| 23 | , view3d.image() | 22 | , view3d.image() |
| 1 | { | 1 | { |
| 2 | - "name": "create-wasm-app", | 2 | + "name": "easel3d-wasm", |
| 3 | "version": "0.1.0", | 3 | "version": "0.1.0", |
| 4 | "lockfileVersion": 1, | 4 | "lockfileVersion": 1, |
| 5 | "requires": true, | 5 | "requires": true, |
| @@ -1486,6 +1486,9 @@ | @@ -1486,6 +1486,9 @@ | ||
| 1486 | "stream-shift": "^1.0.0" | 1486 | "stream-shift": "^1.0.0" |
| 1487 | } | 1487 | } |
| 1488 | }, | 1488 | }, |
| 1489 | + "easel3d-wasm": { | ||
| 1490 | + "version": "file:../pkg" | ||
| 1491 | + }, | ||
| 1489 | "ee-first": { | 1492 | "ee-first": { |
| 1490 | "version": "1.1.1", | 1493 | "version": "1.1.1", |
| 1491 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | 1494 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", |
| @@ -2836,12 +2839,6 @@ | @@ -2836,12 +2839,6 @@ | ||
| 2836 | "minimalistic-assert": "^1.0.1" | 2839 | "minimalistic-assert": "^1.0.1" |
| 2837 | } | 2840 | } |
| 2838 | }, | 2841 | }, |
| 2839 | - "hello-wasm-pack": { | ||
| 2840 | - "version": "0.1.0", | ||
| 2841 | - "resolved": "https://registry.npmjs.org/hello-wasm-pack/-/hello-wasm-pack-0.1.0.tgz", | ||
| 2842 | - "integrity": "sha512-3hx0GDkDLf/a9ThCMV2qG4mwza8N/MCtm8aeFFc/cdBCL2zMJ1kW1wjNl7xPqD1lz8Yl5+uhnc/cpui4dLwz/w==", | ||
| 2843 | - "dev": true | ||
| 2844 | - }, | ||
| 2845 | "hmac-drbg": { | 2842 | "hmac-drbg": { |
| 2846 | "version": "1.0.1", | 2843 | "version": "1.0.1", |
| 2847 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", | 2844 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", |
| @@ -5511,9 +5508,6 @@ | @@ -5511,9 +5508,6 @@ | ||
| 5511 | "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", | 5508 | "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", |
| 5512 | "dev": true | 5509 | "dev": true |
| 5513 | }, | 5510 | }, |
| 5514 | - "wasm-game-of-life": { | ||
| 5515 | - "version": "file:../pkg" | ||
| 5516 | - }, | ||
| 5517 | "watchpack": { | 5511 | "watchpack": { |
| 5518 | "version": "1.6.0", | 5512 | "version": "1.6.0", |
| 5519 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", | 5513 | "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", |
| 1 | { | 1 | { |
| 2 | - "name": "create-wasm-app", | 2 | + "name": "easel3d-wasm", |
| 3 | "version": "0.1.0", | 3 | "version": "0.1.0", |
| 4 | "description": "create an app to consume rust-generated wasm packages", | 4 | "description": "create an app to consume rust-generated wasm packages", |
| 5 | "main": "index.js", | 5 | "main": "index.js", |
| 6 | - "bin": { | ||
| 7 | - "create-wasm-app": ".bin/create-wasm-app.js" | ||
| 8 | - }, | ||
| 9 | "scripts": { | 6 | "scripts": { |
| 10 | "build": "webpack --config webpack.config.js", | 7 | "build": "webpack --config webpack.config.js", |
| 11 | "start": "webpack-dev-server" | 8 | "start": "webpack-dev-server" |
| 12 | }, | 9 | }, |
| 13 | "repository": { | 10 | "repository": { |
| 14 | "type": "git", | 11 | "type": "git", |
| 15 | - "url": "git+https://github.com/rustwasm/create-wasm-app.git" | 12 | + "url": "https://git.silpion.de/scm/~hopp/easel3d-wasm.git" |
| 16 | }, | 13 | }, |
| 17 | "keywords": [ | 14 | "keywords": [ |
| 18 | "webassembly", | 15 | "webassembly", |
| 19 | "wasm", | 16 | "wasm", |
| 20 | "rust", | 17 | "rust", |
| 21 | - "webpack" | 18 | + "webpack", |
| 19 | + "easel3d" | ||
| 22 | ], | 20 | ], |
| 23 | - "author": "Ashley Williams <ashley666ashley@gmail.com>", | ||
| 24 | - "license": "(MIT OR Apache-2.0)", | ||
| 25 | - "bugs": { | ||
| 26 | - "url": "https://github.com/rustwasm/create-wasm-app/issues" | ||
| 27 | - }, | ||
| 28 | - "homepage": "https://github.com/rustwasm/create-wasm-app#readme", | 21 | + "author": "Georg Hopp <hopp@silpion.de>", |
| 22 | + "license": "GPL-3.0-or-later", | ||
| 29 | "devDependencies": { | 23 | "devDependencies": { |
| 30 | - "wasm-game-of-life": "file:../pkg", | ||
| 31 | - "hello-wasm-pack": "^0.1.0", | ||
| 32 | "webpack": "^4.29.3", | 24 | "webpack": "^4.29.3", |
| 33 | "webpack-cli": "^3.1.0", | 25 | "webpack-cli": "^3.1.0", |
| 34 | "webpack-dev-server": "^3.1.5", | 26 | "webpack-dev-server": "^3.1.5", |
| 35 | "copy-webpack-plugin": "^5.0.0" | 27 | "copy-webpack-plugin": "^5.0.0" |
| 36 | }, | 28 | }, |
| 37 | "dependencies": { | 29 | "dependencies": { |
| 38 | - "wasm-game-of-life": "file:../pkg" | 30 | + "easel3d-wasm": "file:../pkg" |
| 39 | } | 31 | } |
| 40 | } | 32 | } |
Please
register
or
login
to post a comment