line_iterator.rs 2.81 KB
//
// …
//
// 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)
        }
    }
}