easel.rs 12.5 KB
//
// 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 show(&self);
}

pub trait Drawable<T> {
    fn plot(&self) -> Coordinates<T>;
}

pub trait Fillable<T> {
    fn fill(&self) -> Coordinates<T>;
}

#[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>>);

impl<T> Coordinate<T>
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
       + Clone + Copy + From<i32> {
    // Tail recursive Bresenham line with integer incremental error.
    fn line(self, b :&Self) -> Vec<Self> {
        fn inner<T>( v :&mut [Coordinate<T>]
                  , bx :i32, by :i32
                  , dx :i32, dy :i32
                  , sx :i32, sy :i32
                  , dz :T, err :i32)
            where T: Add<Output = T> + Copy {

            let Coordinate(x, y, z) = v[0];

            if x != bx || y != by {
                let (x, y, z, err) = match (2*err >= dy, 2*err <= dx) {
                    (true, false) => (x + sx,      y, z + dz,      err + dy ),
                    (false, true) => (     x, y + sy, z + dz,      err + dx ),
                    _             => (x + sx, y + sy, z + dz, err + dx + dy ),
                };
                v[1] = Coordinate(x, y, z);
                inner(&mut v[1..], bx, by, dx, dy, sx, sy, dz, err);
            }
        }

        let Coordinate(ax, ay, az) = self;
        let Coordinate(bx, by, bz) = *b;

        let dx      = (bx - ax).abs();
        let sx :i32 = if ax < bx { 1 } else { -1 };
        let dy      = (by - ay).abs();
        let sy :i32 = if ay < by { 1 } else { -1 };
        let size    = cmp::max(dx, dy);
        let dz      = (bz - az) / size.into();

        let mut v :Vec<Self> = vec!( Coordinate(0, 0, 0.into())
                                   ; (size as usize) + 1);
        v[0] = Coordinate(ax, ay, az);
        inner(&mut v, bx, by, dx, -dy, sx, sy, dz, dx - dy);
        v
    }
}

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

// In 3D a rectangle is not as trivial as in 2D, it might be somehow rotate
// and thus we need to specify a Z offset for the other two corners.
// As I do not need rectangle at all I just comment out this code for now.
/*
#[derive(Debug, Clone, Copy)]
pub struct Rectangle<T>(pub Coordinate<T>, pub Coordinate<T>);

impl<T> Drawable<T> for Rectangle<T> {
    fn plot(&self) -> Coordinates<T> {
        let Rectangle(a, c)    = *self;
        let Coordinate(ax, ay, az) = a;
        let Coordinate(cx, cy, cz) = c;
        let b = Coordinate(cx, ay);
        let d = Coordinate(ax, cy);

        let mut r = a.line(&b);
        r.append(&mut b.line(&c)[1..].to_vec());
        r.append(&mut c.line(&d)[1..].to_vec());
        let mut i = d.line(&a);
        let     l = i.len();
        r.append(&mut i[1..l-1].to_vec());

        Coordinates(r)
    }
}

impl Display for Rectangle {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let Rectangle(a, b) = self;
        write!(f, "Rec[{},{}]", 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>
       + 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)]
pub struct Polygon<T>(pub Coordinates<T>);

impl<T> Drawable<T> for Polygon<T>
where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
       + 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) -> Coordinates<T> {

        /* bresenham kind of thing to get the outer x values for each y of one
         * edge of the polygon. */
        fn walk_edge<T>( a :Coordinate<T>
                       , b :Coordinate<T> ) -> Vec<Coordinate<T>>
            where T: Add<Output = T> + Sub<Output = T> + Div<Output = T>
                   + From<i32> + Debug + Copy {

            let Coordinate(mut x, mut y, mut z) = a;
            let Coordinate(   bx,    by,    bz) = b;

            // next should be called with the negative of this… but dz depends
            // on the positive this.
            let     dy  = -(by - y).abs();
            let     dx  = (bx - x).abs();
            let     sx  = if x < bx { 1 } else { -1 };
            let     dz  = (bz - z) / (-dy).into();
            let mut err = dx + dy;

            let mut v = Vec::<Coordinate<T>>::with_capacity((-dy) as usize);

            while y != by {
                match (2*err >= dy, 2*err <= dx) {
                    (true, false) => { x = x + sx
                                     ; err = err + dy },
                    (false, true) => { v.push(Coordinate(x, y, z))
                                     ; y   = y + 1
                                     ; z   = z + dz
                                     ; err = err + dx },
                    _             => { v.push(Coordinate(x, y, z))
                                     ; x   = x + sx
                                     ; y   = y + 1
                                     ; z   = z + dz
                                     ; err = err + dx + dy },
                }
            }

            v
        }

        fn next_y<T>( cs :&[Coordinate<T>]
                    ,  c :usize
                    ,  f :&dyn Fn(usize) -> usize) -> Option<usize> {
            fn inner<T>( cs :&[Coordinate<T>]
                        , c :usize
                        , n :usize
                        , f :&dyn Fn(usize) -> usize) -> Option<usize> {
                if c == n {
                    None
                } else {
                    let Coordinate(_, cy, _) = cs[c];
                    let Coordinate(_, ny, _) = cs[n];

                    match ny.cmp(&cy) {
                        cmp::Ordering::Less    => None,
                        cmp::Ordering::Equal   => inner(cs, c, f(n), f),
                        cmp::Ordering::Greater => Some(n),
                    }
                }
            }

            inner(cs, c, f(c), f)
        }

        let Polygon(Coordinates(cs)) = self;

        let vert_min = cs.iter().enumerate()
                     . fold( None
                           , |acc, x| 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)} } } )
                     . unwrap().0;

        println!("== vert_min: [{:?}] - {:?}", vert_min, cs[vert_min]);

        let right = |x :usize| (x + 1) % cs.len();
        let left  = |x :usize| if x == 0 { cs.len() - 1 } else { x - 1 };

        let mut r = (vert_min, next_y(cs, vert_min, &right));
        let mut l = (vert_min, next_y(cs, vert_min, &left));

        let mut l_edge :Vec<Coordinate<T>> = Vec::new();
        let mut r_edge :Vec<Coordinate<T>> = Vec::new();

        while l.1 != None || r.1 != None {
            match l.1 {
                None    => {},
                Some(a) => {
                    println!("==        l: [{:?}] - {:?}", l, cs[a]);
                    l_edge.append(&mut walk_edge(cs[l.0], cs[a]));
                    l = (a, next_y(cs, a, &left));
                },
            }

            match r.1 {
                None    => {},
                Some(a) => {
                    println!("==        r: [{:?}] - {:?}", r, cs[a]);
                    r_edge.append(&mut walk_edge(cs[r.0], cs[a]));
                    r = (a, next_y(cs, a, &right));
                }
            }
        }

        println!("== [{}] {:?}", l_edge.len(), l_edge);
        println!("== [{}] {:?}", r_edge.len(), r_edge);

        // TODO we always miss the last scanline…
        // TODO check what happend with at least 2 vertices with same y and
        //      different x…
        // loop though edges…

        Coordinates(Vec::<Coordinate<T>>::new())
    }
}

impl<T> Display for Polygon<T> where T: Copy {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let Polygon(a) = self;
        write!(f, "Poly[{}]", a)
    }
}