14 changed files with 794 additions and 793 deletions
-
520src/easel.rs
-
119src/easel/canvas.rs
-
26src/easel/drawable.rs
-
31src/easel/fillable.rs
-
45src/easel/line.rs
-
90src/easel/line_iterator.rs
-
41src/easel/mod.rs
-
42src/easel/point.rs
-
176src/easel/polygon.rs
-
61src/easel/polyline.rs
-
122src/easel/vertex_iterator.rs
-
22src/geometry.rs
-
282src/lib.rs
-
10src/utils.rs
@ -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,119 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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, Div, Sub};
|
||||
|
use std::sync::mpsc;
|
||||
|
|
||||
|
use super::drawable::Drawable;
|
||||
|
use super::line_iterator::LineIterator;
|
||||
|
|
||||
|
// A 2D drawing surface.
|
||||
|
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>, color :u32);
|
||||
|
fn set_pixel(&mut self, c :Vertex<T>, color :u32);
|
||||
|
fn put_text(&self, ofs :Vertex<T>, s :&str);
|
||||
|
fn show(&self);
|
||||
|
}
|
||||
|
|
||||
|
// A Vertex is a position on a 2D drawing surface along other stuff
|
||||
|
// that needs to iterate between those coordinates.
|
||||
|
#[derive(Debug, Clone, Copy)]
|
||||
|
pub struct Vertex<T>{ x :i32 // canvas x coordinate
|
||||
|
, y :i32 // canvas y coordinate
|
||||
|
, zr :T } // z reciprocal from 3D projection.
|
||||
|
|
||||
|
pub type Vertices<T> = Vec<Vertex<T>>;
|
||||
|
|
||||
|
impl<T> Vertex<T>
|
||||
|
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
||||
|
+ Debug + Clone + Copy + From<i32> {
|
||||
|
pub fn new(x :i32, y :i32, zr :T) -> Self {
|
||||
|
Vertex{x, y, zr}
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn as_tuple(&self) -> (i32, i32, T) {
|
||||
|
(self.x, self.y, self.zr)
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn same_x(&self, b :&Self) -> bool {
|
||||
|
self.x == b.x
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn same_y(&self, b :&Self) -> bool {
|
||||
|
self.y == b.y
|
||||
|
}
|
||||
|
|
||||
|
#[inline]
|
||||
|
pub fn same_position(&self, b :&Self) -> bool {
|
||||
|
self.same_x(b) && self.same_y(b)
|
||||
|
}
|
||||
|
|
||||
|
fn iter(self, b :Self, only_edges :bool) -> LineIterator<T> {
|
||||
|
LineIterator::new(self, b, only_edges)
|
||||
|
}
|
||||
|
|
||||
|
pub fn line_iter(self, b :Self) -> LineIterator<T> {
|
||||
|
self.iter(b, false)
|
||||
|
}
|
||||
|
|
||||
|
pub fn line(self, b :Self) -> Vec<Self> {
|
||||
|
self.line_iter(b).collect()
|
||||
|
}
|
||||
|
|
||||
|
pub 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 Vertex<T> {
|
||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
|
write!(f, "<{},{}>", self.x, self.y)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<T> Add for Vertex<T> where T: Add<Output=T> {
|
||||
|
type Output = Self;
|
||||
|
|
||||
|
fn add(self, other :Self) -> Vertex<T> {
|
||||
|
Vertex{ x: self.x + other.x
|
||||
|
, y: self.y + other.y
|
||||
|
, zr: self.zr + other.zr }
|
||||
|
}
|
||||
|
}
|
||||
@ -0,0 +1,26 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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 super::canvas::Vertices;
|
||||
|
|
||||
|
pub trait Drawable<T> {
|
||||
|
fn plot(&self) -> Vertices<T>;
|
||||
|
}
|
||||
@ -0,0 +1,31 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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;
|
||||
|
use std::ops::{Add, Div, Sub};
|
||||
|
|
||||
|
use super::canvas::Canvas;
|
||||
|
|
||||
|
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);
|
||||
|
}
|
||||
@ -0,0 +1,45 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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, Div, Sub};
|
||||
|
|
||||
|
use super::canvas::{Vertex, Vertices};
|
||||
|
use super::drawable::Drawable;
|
||||
|
|
||||
|
#[derive(Debug, Clone, Copy)]
|
||||
|
pub struct Line<T>(pub Vertex<T>, pub Vertex<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) -> Vertices<T> {
|
||||
|
let Line(a, b) = *self;
|
||||
|
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)
|
||||
|
}
|
||||
|
}
|
||||
@ -0,0 +1,90 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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::max;
|
||||
|
use std::fmt::Debug;
|
||||
|
use std::ops::{Add, Div, Sub};
|
||||
|
|
||||
|
use super::canvas::Vertex;
|
||||
|
|
||||
|
#[derive(Debug, Clone, Copy)]
|
||||
|
pub struct LineIterator<T> where T: Debug {
|
||||
|
a :Option<Vertex<T>>
|
||||
|
, b :Vertex<T>
|
||||
|
, dx :i32 |
||||
|
, dy :i32 |
||||
|
, incx :Vertex<T>
|
||||
|
, incy :Vertex<T>
|
||||
|
, incxy :Vertex<T>
|
||||
|
, err :i32 |
||||
|
, edges :bool |
||||
|
}
|
||||
|
|
||||
|
impl<T> LineIterator<T>
|
||||
|
where T: Add<Output=T> + Div<Output=T> + Sub<Output=T>
|
||||
|
+ Debug + Clone + Copy + From<i32> {
|
||||
|
pub fn new(a :Vertex<T>, b :Vertex<T>, edges :bool) -> LineIterator<T> {
|
||||
|
let (ax, ay, azr) = a.as_tuple();
|
||||
|
let (bx, by, bzr) = b.as_tuple();
|
||||
|
|
||||
|
let dx = (bx - ax).abs();
|
||||
|
let dy = -(by - ay).abs();
|
||||
|
let dz = (bzr - azr) / max(dx, -dy).into();
|
||||
|
|
||||
|
let sx = if ax < bx { 1 } else { -1 };
|
||||
|
let sy = if ay < by { 1 } else { -1 };
|
||||
|
|
||||
|
LineIterator { a: Some(a)
|
||||
|
, b: b
|
||||
|
, dx: dx
|
||||
|
, dy: dy
|
||||
|
, incx: Vertex::new( sx, 0.into(), dz)
|
||||
|
, incy: Vertex::new(0.into(), sy, dz)
|
||||
|
, incxy: Vertex::new( sx, sy, dz)
|
||||
|
, err: dx + dy
|
||||
|
, edges: edges }
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<T> Iterator for LineIterator<T>
|
||||
|
where T: Add<Output=T> + Div<Output=T> + Sub<Output=T>
|
||||
|
+ Debug + Copy + From<i32> {
|
||||
|
type Item = Vertex<T>;
|
||||
|
|
||||
|
// Bresenham based line iteration.
|
||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
if ! self.a?.same_position(&self.b) {
|
||||
|
let ret = self.a;
|
||||
|
let inc = match (2*self.err >= self.dy, 2*self.err <= self.dx ) {
|
||||
|
(true, false) => ( self.incx, self.dy, self.edges),
|
||||
|
(false, true) => ( self.incy, self.dx, false),
|
||||
|
_ => (self.incxy, self.dx+self.dy, false),
|
||||
|
};
|
||||
|
|
||||
|
self.a = Some(self.a? + inc.0);
|
||||
|
self.err = self.err + inc.1;
|
||||
|
if inc.2 { self.next() } else { ret }
|
||||
|
} else {
|
||||
|
self.a = None;
|
||||
|
Some(self.b)
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
@ -0,0 +1,41 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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/>.
|
||||
|
//
|
||||
|
|
||||
|
pub trait Easel {
|
||||
|
}
|
||||
|
|
||||
|
// Trait to implement for a concrete canvas as well as struct for canvas
|
||||
|
// coordinates (which also includes z reciprocal)
|
||||
|
pub mod canvas;
|
||||
|
|
||||
|
// Traits that new drawing primitives must implement to be drawn on a canvas.
|
||||
|
pub mod drawable;
|
||||
|
pub mod fillable;
|
||||
|
|
||||
|
// Drawing primitives
|
||||
|
pub mod line;
|
||||
|
pub mod point;
|
||||
|
pub mod polygon;
|
||||
|
pub mod polyline;
|
||||
|
|
||||
|
// Helper iterators to find all positions a primitive needs to draw.
|
||||
|
mod line_iterator;
|
||||
|
mod vertex_iterator;
|
||||
@ -0,0 +1,42 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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::{Display, Formatter, Result};
|
||||
|
|
||||
|
use super::canvas::{Vertex, Vertices};
|
||||
|
use super::drawable::Drawable;
|
||||
|
|
||||
|
#[derive(Debug, Clone, Copy)]
|
||||
|
pub struct Point<T>(pub Vertex<T>);
|
||||
|
|
||||
|
impl<T> Drawable<T> for Point<T> where T: Copy {
|
||||
|
fn plot(&self) -> Vertices<T> {
|
||||
|
let Point(c) = *self;
|
||||
|
vec!(c)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<T> Display for Point<T> {
|
||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||
|
let Point(p) = self;
|
||||
|
write!(f, "Point[{}]", p)
|
||||
|
}
|
||||
|
}
|
||||
@ -0,0 +1,176 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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;
|
||||
|
use std::ops::{Add, Div, Sub};
|
||||
|
|
||||
|
use super::canvas::{Canvas, Vertex, Vertices};
|
||||
|
use super::drawable::Drawable;
|
||||
|
use super::fillable::Fillable;
|
||||
|
use super::vertex_iterator::{Direction, VertexIterator};
|
||||
|
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct Polygon<T>(pub Vertices<T>);
|
||||
|
|
||||
|
impl<T> Polygon<T>
|
||||
|
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
|
||||
|
+ Copy + Debug + From<i32> {
|
||||
|
#[inline]
|
||||
|
pub fn vertex(&self, v :usize) -> Vertex<T> {
|
||||
|
let Polygon(cs) = self;
|
||||
|
cs[v]
|
||||
|
}
|
||||
|
|
||||
|
pub fn vert_min<'a>(&'a self, d :Direction) -> usize {
|
||||
|
let Polygon(cs) = self;
|
||||
|
|
||||
|
type ICoord<'a, T> = (usize, &'a Vertex<T>);
|
||||
|
|
||||
|
let fold = |acc :Option<ICoord<'a,T>>, x :ICoord<'a, T>|
|
||||
|
match acc {
|
||||
|
None => Some(x),
|
||||
|
Some(a) => {
|
||||
|
let (_, ay, _) = a.1.as_tuple();
|
||||
|
let (_, xy, _) = x.1.as_tuple();
|
||||
|
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).same_y(&self.vertex(next)) {
|
||||
|
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::line(self, Direction::Left)
|
||||
|
}
|
||||
|
|
||||
|
fn right_vertices(&self) -> VertexIterator<T> {
|
||||
|
VertexIterator::line(self, Direction::Right)
|
||||
|
}
|
||||
|
|
||||
|
fn left(&self, v :usize) -> usize {
|
||||
|
let Polygon(cs) = self;
|
||||
|
|
||||
|
match v {
|
||||
|
0 => cs.len() - 1,
|
||||
|
_ => v - 1,
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn right(&self, v :usize) -> usize {
|
||||
|
let Polygon(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),
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
pub 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 (_, cy, _) = p.vertex(c).as_tuple();
|
||||
|
let (_, ny, _) = p.vertex(n).as_tuple();
|
||||
|
|
||||
|
if ny < cy { None } else { Some(n) }
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
inner(self, c, self.step(c, d), d)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
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) -> Vertices<T> {
|
||||
|
let Polygon(cs) = self;
|
||||
|
|
||||
|
match cs[..] {
|
||||
|
[] => Vec::<Vertex<T>>::new(),
|
||||
|
[a] => vec!(a),
|
||||
|
[a, b] => 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());
|
||||
|
}
|
||||
|
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());
|
||||
|
let vertices = |(l, r) :(Vertex<T>, Vertex<T>)| l.line_iter(r);
|
||||
|
|
||||
|
for p in scanlines.flat_map(vertices) {
|
||||
|
canvas.set_pixel(p, 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,61 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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;
|
||||
|
use std::ops::{Add, Div, Sub};
|
||||
|
|
||||
|
use super::canvas::{Vertex, Vertices};
|
||||
|
use super::drawable::Drawable;
|
||||
|
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct Polyline<T>(pub Vertices<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) -> Vertices<T> {
|
||||
|
let Polyline(cs) = self;
|
||||
|
|
||||
|
match cs[..] {
|
||||
|
[] => Vec::<Vertex<T>>::new(),
|
||||
|
[a] => vec!(a),
|
||||
|
[a, b] => 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;
|
||||
|
}
|
||||
|
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)
|
||||
|
}
|
||||
|
}
|
||||
|
*/
|
||||
@ -0,0 +1,122 @@ |
|||||
|
//
|
||||
|
// …
|
||||
|
//
|
||||
|
// 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;
|
||||
|
use std::ops::{Add, Sub, Div};
|
||||
|
|
||||
|
use super::line_iterator::LineIterator;
|
||||
|
use super::canvas::Vertex;
|
||||
|
use super::polygon::Polygon;
|
||||
|
|
||||
|
#[derive(Debug, Clone, Copy)]
|
||||
|
pub enum Direction {
|
||||
|
Left,
|
||||
|
Right,
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
enum VertexIteratorMode {
|
||||
|
Line,
|
||||
|
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> {
|
||||
|
pub fn line(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::Line
|
||||
|
, direction: direction }
|
||||
|
}
|
||||
|
|
||||
|
pub fn edge(p :&'a Polygon<T>, direction :Direction) -> Self {
|
||||
|
let mut vi = Self::line(p, direction);
|
||||
|
|
||||
|
vi.mode = VertexIteratorMode::Edge;
|
||||
|
vi.edge = match vi.current {
|
||||
|
None => None,
|
||||
|
Some(next) => Some(p.vertex(vi.top).edge_iter(p.vertex(next))),
|
||||
|
};
|
||||
|
|
||||
|
vi
|
||||
|
}
|
||||
|
|
||||
|
// 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 = Vertex<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::Line => {
|
||||
|
let current = self.current?;
|
||||
|
self.current = self.p.next_y(current, self.direction);
|
||||
|
Some(self.p.vertex(current))
|
||||
|
},
|
||||
|
}
|
||||
|
}
|
||||
|
}
|
||||
@ -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();
|
|
||||
}
|
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue