ruscii 0.2.0

Terminal graphics engine
Documentation
use super::terminal::{Canvas, Color, Style};
use super::spatial::{Vec2};

use num::cast::ToPrimitive;

#[derive(Debug, Clone)]
pub struct RectCharset {
    pub top: char,
    pub bottom: char,
    pub left: char,
    pub right: char,
    pub top_left: char,
    pub top_right: char,
    pub bottom_left: char,
    pub bottom_right: char,
}

impl RectCharset {
    pub fn simple_lines() -> RectCharset {
        RectCharset::from("──││┌┐└┘")
    }

    pub fn simple_round_lines() -> RectCharset {
        RectCharset::from("──││╭╮╰╯")
    }

    pub fn double_lines() -> RectCharset {
        RectCharset::from("══║║╔╗╚╝")
    }
}

impl From<&str> for RectCharset {
    fn from(item: &str) -> Self {
        if item.len() < 8 {
            panic!("Build a RectCharset requires at least 8 characters.");
        }

        let mut chars = item.chars();
        RectCharset {
            top: chars.next().unwrap(),
            bottom: chars.next().unwrap(),
            left: chars.next().unwrap(),
            right: chars.next().unwrap(),
            top_left: chars.next().unwrap(),
            top_right: chars.next().unwrap(),
            bottom_left: chars.next().unwrap(),
            bottom_right: chars.next().unwrap(),
        }
    }
}

pub trait Drawable {
    fn draw(&self, pencil: Pencil);
}

pub struct Pencil<'a> {
    origin: Vec2,
    scale: Vec2,
    foreground: Color,
    background: Color,
    style: Style,
    canvas: &'a mut Canvas,
}

impl<'a> Pencil<'a> {
    pub fn new(canvas: &'a mut Canvas) -> Pencil {
        Pencil {
            origin: Vec2::new(),
            scale: Vec2::xy(1, 1),
            foreground: canvas.default_element().foreground,
            background: canvas.default_element().background,
            style: canvas.default_element().style,
            canvas,
        }
    }

    pub fn new_one(&mut self) -> Pencil {
        Pencil {
            origin: self.origin,
            scale: self.scale,
            foreground: self.foreground,
            background: self.background,
            style: self.style,
            canvas: self.canvas,
        }
    }

    fn draw_element(&mut self, position: Vec2, value: char) {
        match self.canvas.elem_mut(position) {
            Some(element) => {
                element.value = value;
                element.foreground = self.foreground;
                element.background = self.background;
                element.style = self.style;
            },
            None => (),
        };
    }

    fn canvas_position(&self, position: Vec2) -> Vec2 {
        self.origin + position * self.scale
    }

    pub fn origin(&self) -> Vec2 {
        self.origin
    }

    pub fn scale(&self) -> Vec2 {
        self.scale
    }

    pub fn dimension(&self) -> Vec2 {
        self.canvas.dimension() - self.origin
    }

    pub fn foreground(&self) -> &Color {
        &self.foreground
    }

    pub fn background(&self) -> &Color {
        &self.background
    }

    pub fn style(&self) -> &Style {
        &self.style
    }

    pub fn set_origin(&mut self, position: Vec2) -> &mut Pencil<'a> {
        self.origin = position;
        self
    }

    pub fn move_origin(&mut self, displacement: Vec2) -> &mut Pencil<'a> {
        self.origin += displacement;
        self
    }

    pub fn set_scale(&mut self, scale: Vec2) -> &mut Pencil<'a> {
        self.scale = scale;
        self
    }

    pub fn set_foreground(&mut self, color: Color) -> &mut Pencil<'a> {
        self.foreground = color;
        self
    }

    pub fn set_background(&mut self, color: Color) -> &mut Pencil<'a> {
        self.background = color;
        self
    }

    pub fn set_style(&mut self, style: Style) -> &mut Pencil<'a> {
        self.style = style;
        self
    }

    pub fn draw_char(&mut self, value: char, position: Vec2) -> &mut Pencil<'a> {
        self.draw_element(self.canvas_position(position), value);
        self
    }

    pub fn draw_text(&mut self, text: &str, position: Vec2) -> &mut Pencil<'a> {
        let width = self.canvas.dimension().x;
        for (i, value) in text.chars().enumerate() {
            let elem_pos = self.canvas_position(position) + Vec2::x(i);
            let elem_pos = Vec2::xy(elem_pos.x % width, elem_pos.y + elem_pos.x / width);
            self.draw_element(elem_pos, value);
        }
        self
    }

    pub fn draw_vline<T: ToPrimitive>(&mut self, value: char, position: Vec2, size: T) -> &mut Pencil<'a> {
        let elem_pos = self.canvas_position(position);
        for i in 0..size.to_usize().unwrap() * self.scale.y as usize {
            self.draw_element(elem_pos + Vec2::y(i), value);
        }
        self
    }

    pub fn draw_hline<T: ToPrimitive>(&mut self, value: char, position: Vec2, size: T) -> &mut Pencil<'a> {
        let elem_pos = self.canvas_position(position);
        for i in 0..size.to_usize().unwrap() * self.scale.x as usize {
            self.draw_element(elem_pos + Vec2::x(i), value);
        }
        self
    }

    pub fn draw_rect(&mut self, charset: &RectCharset, position: Vec2, dimension: Vec2) -> &mut Pencil<'a> {
        self.move_origin(position)
            .draw_hline(charset.top, Vec2::x(0), dimension.x - 1)
            .draw_hline(charset.bottom, Vec2::xy(0, dimension.y - 1), dimension.x - 1)
            .draw_vline(charset.left, Vec2::y(0), dimension.y - 1)
            .draw_vline(charset.right, Vec2::xy(dimension.x - 1, 0), dimension.y - 1)
            .draw_char(charset.top_left, Vec2::xy(0, 0))
            .draw_char(charset.top_right, Vec2::x(dimension.x - 1))
            .draw_char(charset.bottom_left, Vec2::y(dimension.y - 1))
            .draw_char(charset.bottom_right, dimension - Vec2::xy(1, 1))
            .move_origin(-position)
    }

    pub fn draw<D: Drawable>(&mut self, drawable: &D, position: Vec2) -> &mut Pencil<'a> {
        let mut new_pencil = self.new_one();
        new_pencil.move_origin(position);
        drawable.draw(new_pencil);
        self
    }
}