A demo application using easel3d to draw in an HTML5 canvas element in a web page.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

520 lines
16 KiB

//
// 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)
}
}