16 changed files with 144 additions and 1821 deletions
-
20Cargo.toml
-
121README.md
-
520src/easel.rs
-
68src/easel3d_wasm.rs
-
377src/geometry.rs
-
237src/lib.rs
-
186src/transform.rs
-
143src/trigonometry.rs
-
10src/utils.rs
-
139src/vector.rs
-
24www/.bin/create-wasm-app.js
-
67www/README.md
-
2www/index.html
-
7www/index.js
-
14www/package-lock.json
-
22www/package.json
@ -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 <georg@steffers.org>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
//
|
|
||||
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<T> {
|
|
||||
fn init_events(&self);
|
|
||||
fn start_events(&self, tx :mpsc::Sender<i32>);
|
|
||||
|
|
||||
fn width(&self) -> u16;
|
|
||||
fn height(&self) -> u16;
|
|
||||
|
|
||||
fn clear(&mut self);
|
|
||||
fn draw(&mut self, c :&dyn Drawable<T>, ofs :Coordinate<T>, color :u32);
|
|
||||
fn put_text(&self, ofs :Coordinate<T>, s :&str);
|
|
||||
fn set_pixel(&mut self, c :Coordinate<T>, color :u32);
|
|
||||
fn show(&self);
|
|
||||
}
|
|
||||
|
|
||||
pub trait Drawable<T> {
|
|
||||
fn plot(&self) -> Coordinates<T>;
|
|
||||
}
|
|
||||
|
|
||||
pub trait Fillable<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Copy + From<i32> {
|
|
||||
fn fill(&self, canvas :&mut dyn Canvas<T>, color :u32);
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Debug, Clone, Copy)]
|
|
||||
pub struct Coordinate<T>(pub i32, pub i32, pub T);
|
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
pub struct Coordinates<T>(pub Vec<Coordinate<T>>);
|
|
||||
|
|
||||
#[derive(Debug, Clone, Copy)]
|
|
||||
pub struct LineIterator<T> where T: Debug {
|
|
||||
a :Option<Coordinate<T>>
|
|
||||
, b :Coordinate<T>
|
|
||||
, dx :i32 |
|
||||
, dy :i32 |
|
||||
, dz :T
|
|
||||
, sx :i32 |
|
||||
, sy :i32 |
|
||||
, err :i32 |
|
||||
, only_edges :bool |
|
||||
}
|
|
||||
|
|
||||
impl<T> Iterator for LineIterator<T>
|
|
||||
where T: Add<Output = T> + Debug + Copy + From<i32> {
|
|
||||
type Item = Coordinate<T>;
|
|
||||
|
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||
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<T> Coordinate<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Clone + Copy + From<i32> {
|
|
||||
fn iter(self, b :&Self, only_edges :bool) -> LineIterator<T> {
|
|
||||
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<T> {
|
|
||||
self.iter(b, false)
|
|
||||
}
|
|
||||
|
|
||||
fn line(self, b :&Self) -> Vec<Self> {
|
|
||||
self.line_iter(b).collect()
|
|
||||
}
|
|
||||
|
|
||||
fn edge_iter(self, b :&Self) -> LineIterator<T> {
|
|
||||
self.iter(b, true)
|
|
||||
}
|
|
||||
|
|
||||
fn edge(self, b :&Self) -> Vec<Self> {
|
|
||||
self.edge_iter(b).collect()
|
|
||||
}
|
|
||||
|
|
||||
fn face(edges :&[Self]) -> Vec<Self> {
|
|
||||
edges.to_vec()
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Display for Coordinate<T> {
|
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
||||
write!(f, "<{},{}>", self.0, self.1)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Display for Coordinates<T> 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<T>(pub Coordinate<T>);
|
|
||||
|
|
||||
impl<T> Drawable<T> for Point<T> where T: Copy {
|
|
||||
fn plot(&self) -> Coordinates<T> {
|
|
||||
let Point(c) = *self;
|
|
||||
Coordinates(vec!(c))
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Display for Point<T> {
|
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
||||
let Point(p) = self;
|
|
||||
write!(f, "Point[{}]", p)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Debug, Clone, Copy)]
|
|
||||
pub struct Line<T>(pub Coordinate<T>, pub Coordinate<T>);
|
|
||||
|
|
||||
impl<T> Drawable<T> for Line<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Clone + Copy + From<i32> {
|
|
||||
fn plot(&self) -> Coordinates<T> {
|
|
||||
let Line(a, b) = *self;
|
|
||||
Coordinates(a.line(&b))
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Display for Line<T> {
|
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
||||
let Line(a, b) = self;
|
|
||||
write!(f, "Line[{},{}]", a, b)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
pub struct Polyline<T>(pub Coordinates<T>);
|
|
||||
|
|
||||
impl<T> Drawable<T> for Polyline<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Clone + Copy + From<i32> {
|
|
||||
fn plot(&self) -> Coordinates<T> {
|
|
||||
let Polyline(Coordinates(cs)) = self;
|
|
||||
|
|
||||
match cs[..] {
|
|
||||
[] => Coordinates(Vec::<Coordinate<T>>::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<T> Display for Polyline<T> 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<T>(pub Coordinates<T>);
|
|
||||
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
enum VertexIteratorMode { Vertex, Edge }
|
|
||||
#[derive(Debug, Clone)]
|
|
||||
pub struct VertexIterator<'a,T> where T: Debug {
|
|
||||
p :&'a Polygon<T>,
|
|
||||
top :usize,
|
|
||||
current :Option<usize>,
|
|
||||
edge :Option<LineIterator<T>>,
|
|
||||
mode :VertexIteratorMode,
|
|
||||
direction :Direction,
|
|
||||
}
|
|
||||
|
|
||||
impl<'a,T> VertexIterator<'a,T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Copy + From<i32> {
|
|
||||
fn edge(p :&'a Polygon<T>, 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<T>, 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<LineIterator<T>> {
|
|
||||
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<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Copy + From<i32> {
|
|
||||
type Item = Coordinate<T>;
|
|
||||
|
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||
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<T> Polygon<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Copy + Debug + From<i32> {
|
|
||||
#[inline]
|
|
||||
fn vertex(&self, v :usize) -> Coordinate<T> {
|
|
||||
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<T>);
|
|
||||
|
|
||||
// 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<ICoord<'a,T>>, 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<T> {
|
|
||||
VertexIterator::edge(self, Direction::Left)
|
|
||||
}
|
|
||||
|
|
||||
fn right_edge(&self) -> VertexIterator<T> {
|
|
||||
VertexIterator::edge(self, Direction::Right)
|
|
||||
}
|
|
||||
|
|
||||
fn left_vertices(&self) -> VertexIterator<T> {
|
|
||||
VertexIterator::vertex(self, Direction::Left)
|
|
||||
}
|
|
||||
|
|
||||
fn right_vertices(&self) -> VertexIterator<T> {
|
|
||||
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<usize> {
|
|
||||
fn inner<T>( p :&Polygon<T>
|
|
||||
, c :usize |
|
||||
, n :usize |
|
||||
, d :Direction) -> Option<usize>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Copy + Debug + From<i32> {
|
|
||||
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<Coordinate<T>> = self.left_vertices().collect();
|
|
||||
let right :Vec<Coordinate<T>> = 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<T> Drawable<T> for Polygon<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Clone + Copy + From<i32> {
|
|
||||
fn plot(&self) -> Coordinates<T> {
|
|
||||
let Polygon(Coordinates(cs)) = self;
|
|
||||
|
|
||||
match cs[..] {
|
|
||||
[] => Coordinates(Vec::<Coordinate<T>>::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<T> Fillable<T> for Polygon<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Clone + Copy + From<i32> {
|
|
||||
fn fill(&self, canvas :&mut dyn Canvas<T>, 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<T> Display for Polygon<T> where T: Copy {
|
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
|
||||
let Polygon(a) = self;
|
|
||||
write!(f, "Poly[{}]", a)
|
|
||||
}
|
|
||||
}
|
|
||||
@ -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<f64>
|
||||
|
, image :Vec<Color> }
|
||||
|
|
||||
|
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<f64> 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<f64>, 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<i32>) {}
|
||||
|
fn draw(&mut self, _ :&dyn Drawable<f64>, _ :u32) {}
|
||||
|
fn put_text(&self, _ :Vertex<f64>, _ :&str) {}
|
||||
|
fn show(&self) {}
|
||||
|
}
|
||||
@ -1,377 +0,0 @@ |
|||||
//
|
|
||||
// Basic geometric things...
|
|
||||
//
|
|
||||
// Georg Hopp <georg@steffers.org>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
//
|
|
||||
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<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Copy + Trig {
|
|
||||
corners :Vec<usize>,
|
|
||||
normal :Option<Vector<T>>,
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||
pub struct Point<T>(pub Vector<T>, T)
|
|
||||
where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig;
|
|
||||
|
|
||||
impl<T> Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy + From<i32> {
|
|
||||
pub fn new(x :T, y :T, z :T) -> Self {
|
|
||||
Self(Vector(x, y, z), 1.into())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Add for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ 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<T> Neg for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy {
|
|
||||
type Output = Self;
|
|
||||
|
|
||||
fn neg(self) -> Self {
|
|
||||
let Point(v, w) = self;
|
|
||||
Self(-v, -w)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Sub for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy {
|
|
||||
type Output = Self;
|
|
||||
|
|
||||
fn sub(self, other :Self) -> Self {
|
|
||||
self + -other
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Mul for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy + From<i32> {
|
|
||||
type Output = Self;
|
|
||||
|
|
||||
fn mul(self, other :Self) -> Self {
|
|
||||
let a :Vector<T> = self.into();
|
|
||||
let b :Vector<T> = other.into();
|
|
||||
|
|
||||
Point(a * b, 1.into())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> From<Vector<T>> for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy + From<i32> {
|
|
||||
fn from(v :Vector<T>) -> Self {
|
|
||||
Point(v, 1.into())
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Into<Vector<T>> for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Trig + Copy + From<i32> {
|
|
||||
fn into(self) -> Vector<T> {
|
|
||||
let Point(v, w) = self;
|
|
||||
|
|
||||
if w == 0.into() {
|
|
||||
v
|
|
||||
} else {
|
|
||||
v.mul(&w.recip())
|
|
||||
}
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Transformable<T> for Point<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Debug + Trig + Copy + From<i32> {
|
|
||||
fn transform(&self, m :&TMatrix<T>) -> 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<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + PartialEq + Copy + Trig {
|
|
||||
points :Vec<Point<T>>,
|
|
||||
faces :Vec<Face<T>>,
|
|
||||
}
|
|
||||
|
|
||||
pub trait Primitives<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
|
|
||||
fn transform(&self, m :&TMatrix<T>) -> Self;
|
|
||||
fn project( &self
|
|
||||
, camera :&Camera<T>
|
|
||||
, light :&DirectLight<T>
|
|
||||
, col :u32 ) -> Vec<(Polygon<T>, u32)>;
|
|
||||
}
|
|
||||
|
|
||||
#[derive(Debug, Clone, Copy)]
|
|
||||
pub struct Camera<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
|
|
||||
width :T,
|
|
||||
height :T,
|
|
||||
distance :T,
|
|
||||
project :TMatrix<T>,
|
|
||||
}
|
|
||||
|
|
||||
pub struct DirectLight<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Debug + Copy + Trig + From<i32> {
|
|
||||
direction: Vector<T>,
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Camera<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Debug + Copy + Trig + From<i32> {
|
|
||||
// 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<T>, 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<T> {
|
|
||||
self.project
|
|
||||
}
|
|
||||
|
|
||||
pub fn project(&self, p :Point<T>) -> Point<T> {
|
|
||||
p.transform(&self.project)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> DirectLight<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Copy + Trig + From<i32> {
|
|
||||
pub fn new(v :Vector<T>) -> Self {
|
|
||||
DirectLight{ direction: v }
|
|
||||
}
|
|
||||
|
|
||||
pub fn dir(&self) -> Vector<T> {
|
|
||||
self.direction
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Face<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Debug + Copy + Trig + From<i32> {
|
|
||||
fn new(corners :Vec<usize>, ps :&[Point<T>]) -> Self {
|
|
||||
let mut f = Face{ corners: corners, normal: None };
|
|
||||
f.update_normal(ps);
|
|
||||
f
|
|
||||
}
|
|
||||
|
|
||||
fn update_normal(&mut self, ps :&[Point<T>]) {
|
|
||||
let edge10 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[0]]).into();
|
|
||||
let edge12 :Vector<T> = (ps[self.corners[1]] - ps[self.corners[2]]).into();
|
|
||||
self.normal = Some(edge10 * edge12);
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Polyeder<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ PartialEq + Debug + Copy + Trig + From<i32> {
|
|
||||
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<T> {
|
|
||||
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<T> {
|
|
||||
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<T> {
|
|
||||
let ah :T = a / From::<i32>::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<T> Primitives<T> for Polyeder<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Copy + Trig + From<i32> + PartialOrd {
|
|
||||
// TODO Maybe this should also be an instance of Transformable…
|
|
||||
fn transform(&self, m :&TMatrix<T>) -> 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<T>
|
|
||||
, light :&DirectLight<T>
|
|
||||
, color :u32 ) -> Vec<(Polygon<T>, u32)> {
|
|
||||
// Helper to create a Polygon from Coordinates…
|
|
||||
// TODO probably there needs to be a Polygon constructor for this.
|
|
||||
fn polygon<I, T>(c :I) -> Polygon<T>
|
|
||||
where I: Iterator<Item = Coordinate<T>> {
|
|
||||
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<T>| {
|
|
||||
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()
|
|
||||
}
|
|
||||
}
|
|
||||
@ -1,186 +0,0 @@ |
|||||
//
|
|
||||
// Transformation of vectors in a given coordinate system...
|
|
||||
//
|
|
||||
// Georg Hopp <georg@steffers.org>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
//
|
|
||||
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, T) )
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From<i32> + Copy;
|
|
||||
|
|
||||
pub trait Transformable<T>
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Debug + Trig + From<i32> + Copy {
|
|
||||
fn transform(&self, m :&TMatrix<T>) -> Self;
|
|
||||
}
|
|
||||
|
|
||||
impl<T> TMatrix<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Trig + From<i32> + 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<T>) -> 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<T>, 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<T>) -> 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<I>(mi :I) -> TMatrix<T>
|
|
||||
where I: IntoIterator<Item = TMatrix<T>> {
|
|
||||
|
|
||||
mi.into_iter().fold(Self::unit(), |acc, x| x * acc)
|
|
||||
}
|
|
||||
|
|
||||
pub fn apply(&self, v :&Vector<T>, w :T) -> (Vector<T>, 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<T> Mul for TMatrix<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ Debug + Trig + From<i32> + 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 ) )
|
|
||||
}
|
|
||||
}
|
|
||||
@ -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 <georg@steffers.org>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
//
|
|
||||
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<Self, Error> where Self: Sized;
|
|
||||
fn sintab() -> Vec<Self> where Self: Sized;
|
|
||||
fn tantab() -> Vec<Self> where Self: Sized;
|
|
||||
|
|
||||
fn sin(d :i32) -> Self
|
|
||||
where Self: Sized + Neg<Output = Self> + 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<Output = Self> + 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<i32> + Div<Output = Self> {
|
|
||||
Into::<Self>::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<Self, Error> {
|
|
||||
let x = self.sqrt();
|
|
||||
match x.is_nan() {
|
|
||||
true => Err("sqrt on negative undefined"),
|
|
||||
false => Ok(x),
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
fn sintab() -> Vec<Self> {
|
|
||||
lazy_static::lazy_static! {
|
|
||||
static ref SINTAB :Vec<f64> =
|
|
||||
(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<Self> {
|
|
||||
// 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<f64> =
|
|
||||
(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()
|
|
||||
}
|
|
||||
}
|
|
||||
@ -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();
|
|
||||
}
|
|
||||
@ -1,139 +0,0 @@ |
|||||
//
|
|
||||
// Stuff for manipulating 3 dimensional vectors.
|
|
||||
//
|
|
||||
// Georg Hopp <georg@steffers.org>
|
|
||||
//
|
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||
//
|
|
||||
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<T>(pub T, pub T, pub T)
|
|
||||
where T: Add + Sub + Neg + Mul + Div + Trig + Copy;
|
|
||||
|
|
||||
impl<T> Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T> + 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<T> Display for Vector<T>
|
|
||||
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<T> PartialEq for Vector<T>
|
|
||||
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<T> Add for Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T> + 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<T> Sub for Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T> + Trig + Copy {
|
|
||||
type Output = Self;
|
|
||||
|
|
||||
fn sub(self, other :Self) -> Self {
|
|
||||
self + -other
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Neg for Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T> + Trig + Copy {
|
|
||||
type Output = Self;
|
|
||||
|
|
||||
fn neg(self) -> Self {
|
|
||||
let Vector(x, y, z) = self;
|
|
||||
Self(-x, -y, -z)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
impl<T> Mul for Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T> + 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<T> Transformable<T> for Vector<T>
|
|
||||
where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T>
|
|
||||
+ Mul<Output = T> + Div<Output = T>
|
|
||||
+ Trig + Copy + Debug + From<i32> {
|
|
||||
fn transform(&self, m :&TMatrix<T>) -> Self {
|
|
||||
let (v, _) = m.apply(self, 0.into());
|
|
||||
v
|
|
||||
}
|
|
||||
}
|
|
||||
@ -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 = ❤"); |
|
||||
} |
|
||||
}); |
|
||||
@ -1,67 +0,0 @@ |
|||||
<div align="center"> |
|
||||
|
|
||||
<h1><code>create-wasm-app</code></h1> |
|
||||
|
|
||||
<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> |
|
||||
|
|
||||
<p> |
|
||||
<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> |
|
||||
</p> |
|
||||
|
|
||||
<h3> |
|
||||
<a href="#usage">Usage</a> |
|
||||
<span> | </span> |
|
||||
<a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a> |
|
||||
</h3> |
|
||||
|
|
||||
<sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub> |
|
||||
</div> |
|
||||
|
|
||||
## 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. |
|
||||
@ -1,40 +1,32 @@ |
|||||
{ |
{ |
||||
"name": "create-wasm-app", |
|
||||
|
"name": "easel3d-wasm", |
||||
"version": "0.1.0", |
"version": "0.1.0", |
||||
"description": "create an app to consume rust-generated wasm packages", |
"description": "create an app to consume rust-generated wasm packages", |
||||
"main": "index.js", |
"main": "index.js", |
||||
"bin": { |
|
||||
"create-wasm-app": ".bin/create-wasm-app.js" |
|
||||
}, |
|
||||
"scripts": { |
"scripts": { |
||||
"build": "webpack --config webpack.config.js", |
"build": "webpack --config webpack.config.js", |
||||
"start": "webpack-dev-server" |
"start": "webpack-dev-server" |
||||
}, |
}, |
||||
"repository": { |
"repository": { |
||||
"type": "git", |
"type": "git", |
||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git" |
|
||||
|
"url": "https://git.silpion.de/scm/~hopp/easel3d-wasm.git" |
||||
}, |
}, |
||||
"keywords": [ |
"keywords": [ |
||||
"webassembly", |
"webassembly", |
||||
"wasm", |
"wasm", |
||||
"rust", |
"rust", |
||||
"webpack" |
|
||||
|
"webpack", |
||||
|
"easel3d" |
||||
], |
], |
||||
"author": "Ashley Williams <ashley666ashley@gmail.com>", |
|
||||
"license": "(MIT OR Apache-2.0)", |
|
||||
"bugs": { |
|
||||
"url": "https://github.com/rustwasm/create-wasm-app/issues" |
|
||||
}, |
|
||||
"homepage": "https://github.com/rustwasm/create-wasm-app#readme", |
|
||||
|
"author": "Georg Hopp <hopp@silpion.de>", |
||||
|
"license": "GPL-3.0-or-later", |
||||
"devDependencies": { |
"devDependencies": { |
||||
"wasm-game-of-life": "file:../pkg", |
|
||||
"hello-wasm-pack": "^0.1.0", |
|
||||
"webpack": "^4.29.3", |
"webpack": "^4.29.3", |
||||
"webpack-cli": "^3.1.0", |
"webpack-cli": "^3.1.0", |
||||
"webpack-dev-server": "^3.1.5", |
"webpack-dev-server": "^3.1.5", |
||||
"copy-webpack-plugin": "^5.0.0" |
"copy-webpack-plugin": "^5.0.0" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"wasm-game-of-life": "file:../pkg" |
|
||||
|
"easel3d-wasm": "file:../pkg" |
||||
} |
} |
||||
} |
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue