nd_vector 0.1.0

[WIP] Lengthen! Shrink! Iterate! Scale! Twist and turn to your imagination along any dimension on a vector!
Documentation
use crate::VectorGrid;

const BOARDER_ROOF:   (&str, &str, &str) = (" ", "_", " ");
const BOARDER_WALL:   (&str,       &str) = ("",      "");
const BOARDER_FLOOR:  (&str, &str, &str) = (" ", "", " ");
const BOARDER_CORNER: (&str,      &str) = ("",        " ");


pub trait VectorGridItemDisplay {
    fn display_str(&self) -> String;
}

#[derive(Default, Clone)]
pub enum UnicodeShadeItemDisplay {
    #[default]
    Empty,
    Light,
    Medium,
    Dark,
    Full,
}

impl VectorGridItemDisplay for UnicodeShadeItemDisplay {
    fn display_str(&self) -> String {
        match self {
            UnicodeShadeItemDisplay::Empty => "  ",
            UnicodeShadeItemDisplay::Light => "░░",
            UnicodeShadeItemDisplay::Medium => "▒▒",
            UnicodeShadeItemDisplay::Dark => "▓▓",
            UnicodeShadeItemDisplay::Full => "██",
        }.to_string()
    }
}

impl VectorGridItemDisplay for bool {
    fn display_str(&self) -> String {
        if *self {
            "██".to_string()
        } else {
            "░░".to_string()
        }
    }
}

impl VectorGridItemDisplay for u8 {
    fn display_str(&self) -> String {
        self.to_string()
    }
}

impl<T: VectorGridItemDisplay> VectorGridItemDisplay for Option<T> {
    fn display_str(&self) -> String {
        match self {
            Some(some) => {
                some.display_str()
            }
            None => {
                String::new()
            }
        }
    }
}

fn display_lines_2d<ItemType: VectorGridItemDisplay>(items: &[ItemType], width: usize, thickness: usize) -> Vec<String> {
    let mut item_display_boundries = [0; 2];
    let display_items: Vec<String> = items.iter().map(|item| {
        let string = item.display_str();
        let mut height = 0;
        for line in string.lines() {
            let length = line.chars().count();
            if length > item_display_boundries[0] {
                item_display_boundries[0] = length;
            }
            height += 1
        }
        if height > item_display_boundries[1] {
            item_display_boundries[1] = height;
        }
        string
    }).collect();

    // ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
    let mut output = vec![[" ".repeat(thickness).as_str(), BOARDER_ROOF.0, &BOARDER_ROOF.1.repeat(width * item_display_boundries[0]), BOARDER_ROOF.2].concat()];

    for layer in 1..=thickness {
        output.push([
            " ".repeat(thickness - layer + 1),
            ["̲╱", &"_".repeat(item_display_boundries[0].max(1) - 1)].concat().repeat(width),
            "".to_string(),
            "".repeat(layer)
        ].concat())
    }

    let mut print_lines = Vec::new();
    for (i, item) in display_items.iter().enumerate() {
        if i % width == 0 {
            print_lines = vec![String::from(BOARDER_WALL.0); item_display_boundries[1]]
        }

        let item_lines: Vec<&str> = item.lines().collect();
        for (i, print_line) in print_lines.iter_mut().enumerate() {
            print_line.push_str(&format!("{:width$}", item_lines.get(i).unwrap_or(&""), width = item_display_boundries[0]));
        }

        if (i + 1) % width == 0 {
            let mut perspective = (display_items.len() / width - i / width) * item_display_boundries[1];
            for line in print_lines.iter() {
                perspective -= 1;
                let side = if perspective < thickness {
                    [BOARDER_WALL.1.repeat(perspective + 1), "".to_string()].concat()
                } else {
                    BOARDER_WALL.1.repeat(thickness + 1)
                };
                output.push([line.as_str(), &side].concat());
            }
        }
    }

    let floor_width = (((items.len() - 1) % width) + 1) * item_display_boundries[0];
    let floor_gap = width * item_display_boundries[0] - floor_width;

    if floor_gap != 0 {
        for (i, print_line) in print_lines.iter().enumerate() {
            let y = item_display_boundries[1] - i - 1;
            let mut side = if y < thickness {
                [BOARDER_WALL.1.repeat(y + 1), "".to_string()].concat()
            } else {
                BOARDER_WALL.1.repeat(thickness + 1)
            };
            if i == 0 {
                side.push_str(&[&BOARDER_FLOOR.1.repeat(floor_gap - side.chars().count()), BOARDER_CORNER.1].concat());
            }
            output.push([print_line.as_str(), &side].concat());
        }
    }
    
    output.push([BOARDER_FLOOR.0, &BOARDER_FLOOR.1.repeat(floor_width), BOARDER_FLOOR.2].concat());
    output
}

impl<ItemType: VectorGridItemDisplay> VectorGrid<ItemType> {

    pub fn print(&self) {
        println!("VectorGrid: Shape [{}]", self.shape());

        if self.defined_shape.len() == 1 {
            for line in display_lines_2d(&self.items, self.defined_shape[0].get(), 0) {
                println!("{:length$}", line, length = 50)
            }
        } else if self.defined_shape.len() == 2 {
            let step_size = self.defined_shape[0].get() * self.defined_shape[1].get();
            let mut i = 0;


            while i < self.items.len() {
                for line in display_lines_2d(&self.items[i..(i + step_size).min(self.items.len())], self.defined_shape[0].get(), self.items.len() / step_size) {
                    println!("{:length$}", line, length = 50)
                }
                i += step_size
            } 
        }
    }
}