From b36e20a2a0e225bdaa78e396d231864e5fef6de0 Mon Sep 17 00:00:00 2001 From: Georg Hopp Date: Tue, 17 Dec 2019 21:22:15 +0100 Subject: [PATCH] Add Drawable primitives --- fractional/src/easel.rs | 235 ++++++++++++++++++++++++++++++++++++++++ fractional/src/lib.rs | 1 + fractional/src/main.rs | 72 +++++------- 3 files changed, 263 insertions(+), 45 deletions(-) create mode 100644 fractional/src/easel.rs diff --git a/fractional/src/easel.rs b/fractional/src/easel.rs new file mode 100644 index 0000000..4d7ef90 --- /dev/null +++ b/fractional/src/easel.rs @@ -0,0 +1,235 @@ +// +// This is an abstraction over a drawing environment. +// +// Georg Hopp +// +// 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 . +// +use std::cmp; +use std::fmt::{Formatter, Display, Result}; + +pub trait Easel { + fn canvas(self) -> dyn Canvas; +} + +pub trait Canvas { + fn draw(self, c :&dyn Drawable, ofs :Coordinate); +} + +pub trait Drawable { + fn plot(&self) -> Coordinates; +} + +#[derive(Debug, Clone, Copy)] +pub struct Coordinate(pub i32, pub i32); + +#[derive(Debug, Clone)] +pub struct Coordinates(pub Vec); + +impl Coordinate { + // Tail recursive Bresenham line with integer incremental error. + fn line(self, b :&Self) -> Vec { + fn inner( v :&mut [Coordinate] + , bx :i32, by :i32 + , dx :i32, dy :i32 + , sx :i32, sy :i32 + , err :i32) { + let Coordinate(x, y) = v[0]; + + if x != bx || y != by { + let doinc = (2*err >= dy, 2*err <= dx); + let (x, y, err) = match doinc { + (true, false) => (x + sx, y, err + dy), + (false, true) => ( x, y + sy, err + dx), + _ => (x + sx, y + sy, err + dx + dy ), + }; + v[1] = Coordinate(x, y); + inner(&mut v[1..], bx, by, dx, dy, sx, sy, err); + } + } + + let Coordinate(ax, ay) = self; + let Coordinate(bx, by) = *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 mut v :Vec = vec!( Coordinate(0, 0) + ; cmp::max(dx, -dy) as usize + 1); + v[0] = Coordinate(ax, ay); + inner(&mut v, bx, by, dx, dy, sx, sy, dx + dy); + v + } +} + +impl Display for Coordinate { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "<{},{}>", self.0, self.1) + } +} + +impl Display for Coordinates { + 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(pub Coordinate); + +impl Drawable for Point{ + fn plot(&self) -> Coordinates { + let Point(c) = *self; + Coordinates(vec!(c)) + } +} + +impl Display for Point { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let Point(p) = self; + write!(f, "Point[{}]", p) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Line(pub Coordinate, pub Coordinate); + +impl Drawable for Line { + fn plot(&self) -> Coordinates { + let Line(a, b) = *self; + Coordinates(a.line(&b)) + } +} + +impl Display for Line { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let Line(a, b) = self; + write!(f, "Line[{},{}]", a, b) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Rectangle(pub Coordinate, pub Coordinate); + +impl Drawable for Rectangle { + fn plot(&self) -> Coordinates { + let Rectangle(a, c) = *self; + let Coordinate(ax, ay) = a; + let Coordinate(cx, cy) = 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(pub Coordinates); + +impl Drawable for Polyline { + fn plot(&self) -> Coordinates { + let Polyline(Coordinates(cs)) = self; + + match cs[..] { + [] => Coordinates(Vec::::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 Display for Polyline { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let Polyline(a) = self; + write!(f, "PLine[{}]", a) + } +} + +#[derive(Debug, Clone)] +pub struct Polygon(pub Coordinates); + +impl Drawable for Polygon { + fn plot(&self) -> Coordinates { + let Polygon(Coordinates(cs)) = self; + + match cs[..] { + [] => Coordinates(Vec::::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 = i.line(&a); + let l = j.len(); + r.append(&mut j[1..l-1].to_vec()); + Coordinates(r) + }, + } + } +} + +impl Display for Polygon { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let Polygon(a) = self; + write!(f, "Poly[{}]", a) + } +} diff --git a/fractional/src/lib.rs b/fractional/src/lib.rs index 49ccdd6..e04b8c2 100644 --- a/fractional/src/lib.rs +++ b/fractional/src/lib.rs @@ -23,6 +23,7 @@ extern crate lazy_static; pub type Error = &'static str; pub mod continuous; +pub mod easel; pub mod fractional; pub mod transform; pub mod trigonometry; diff --git a/fractional/src/main.rs b/fractional/src/main.rs index 8c358a9..5c8a757 100644 --- a/fractional/src/main.rs +++ b/fractional/src/main.rs @@ -18,7 +18,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // -use std::cmp; use std::convert::{TryFrom, TryInto, Into}; use std::f64::consts::PI as FPI; use std::fmt::Display; @@ -26,47 +25,13 @@ use std::num::TryFromIntError; use std::ops::{Add,Sub,Neg,Mul,Div}; use fractional::continuous::Continuous; +use fractional::easel::{ Coordinate, Coordinates, Drawable, Line, Polyline + , Polygon, Rectangle}; use fractional::fractional::{Fractional, from_vector}; use fractional::trigonometry::Trig; use fractional::vector::{Vector}; use fractional::transform::{TMatrix, translate, rotate_x, rotate_y, rotate_z, rotate_v}; -// Tail recursive Bresenham line with integer incremental error. -fn line(a :(u32, u32), b :(u32, u32)) -> Vec<(u32, u32)>{ - fn inner( v :&mut [(u32, u32)] - , bx :u32, by :u32 - , dx :i32, dy :i32 - , sx :i32, sy :i32 - , err :i32) { - let (x, y) = v[0]; - - if x != bx || y != by { - let (x, y, err) = match (2*err as i32 >= dy, 2*err as i32 <= dx) { - (true, false) => ((x as i32 + sx) as u32, y, err + dy), - (false, true) => (x, (y as i32 + sy) as u32, err + dx), - _ => ( (x as i32 + sx) as u32 - , (y as i32 + sy) as u32 - , err + dx + dy ), - }; - v[1] = (x, y); - inner(&mut v[1..], bx, by, dx, dy, sx, sy, err); - } - } - - let (ax, ay) = a; - let (bx, by) = b; - - let dx = (bx as i32 - ax as i32).abs(); - let sx :i32 = if ax < bx { 1 } else { -1 }; - let dy = -(by as i32 - ay as i32).abs(); - let sy :i32 = if ay < by { 1 } else { -1 }; - - let mut v :Vec<(u32, u32)> = vec!((0, 0); cmp::max(dx, -dy) as usize + 1); - v[0] = (ax, ay); - inner(&mut v, bx, by, dx, dy, sx, sy, dx + dy); - v -} - fn mean(v: &Vec) -> Result { let r = v.iter().fold(0, |acc, x| acc + x); let l = i64::try_from(v.len())?; @@ -268,14 +233,31 @@ fn _transform(v :Vector, v1 :Vector, v2 :Vector, v3 :Vector) } fn _line() { - println!("{:>14} : {:?}", "Line", line((0,1), (6,4))); - println!("{:>14} : {:?}", "Line", line((0,4), (6,1))); - println!("{:>14} : {:?}", "Line", line((6,1), (0,4))); - println!("{:>14} : {:?}", "Line", line((6,4), (0,1))); - println!("{:>14} : {:?}", "Line", line((0,1), (6,8))); - println!("{:>14} : {:?}", "Line", line((0,8), (6,1))); - println!("{:>14} : {:?}", "Line", line((6,1), (0,8))); - println!("{:>14} : {:?}", "Line", line((6,8), (0,1))); + let a = (Coordinate(0, 1), Coordinate(6, 4)); + let b = (Coordinate(0, 4), Coordinate(6, 1)); + let c = (Coordinate(1, 0), Coordinate(6, 8)); + let d = (Coordinate(1, 8), Coordinate(6, 0)); + + for i in [a, b, c, d].iter() { + println!("{:>14} : {}", Line(i.0, i.1), Line(i.0, i.1).plot()); + println!("{:>14} : {}", Line(i.1, i.0), Line(i.1, i.0).plot()); + } + + println!(); + let r = Rectangle(Coordinate(1, 1), Coordinate(10, 5)); + println!("{:>14} : {}", r, r.plot()); + + println!(); + let pl = Polyline( + Coordinates(vec!(a.0, a.1, b.0, b.1, c.0, c.1, d.0, d.1))); + println!("{:>14} : {}", pl, pl.plot()); + + println!(); + let pg = Polygon( + Coordinates(vec!( Coordinate( 0, -20) + , Coordinate( 20, 20) + , Coordinate(-20, 20) ))); + println!("{:>14} : {}", pg, pg.plot()); } fn main() {