#![no_std]
#![deny(missing_docs, unused, unsafe_code, warnings)]
use core::fmt;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Weight {
Normal,
Thick,
Doubled,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Style {
Em,
En,
Dot,
Curved,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Char {
pub up: Option<Weight>,
pub right: Option<Weight>,
pub left: Option<Weight>,
pub down: Option<Weight>,
pub style: Option<Style>,
pub ascii: bool,
}
impl fmt::Display for Char {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.into_char(), f)
}
}
impl From<Char> for char {
fn from(c: Char) -> char {
c.into_char()
}
}
impl Char {
pub fn empty() -> Self {
Self::default()
}
pub fn vertical(w: Weight) -> Self {
Self {
up: Some(w),
down: Some(w),
..Self::default()
}
}
pub fn horizontal(w: Weight) -> Self {
Self {
left: Some(w),
right: Some(w),
..Self::default()
}
}
pub fn cross(w: Weight) -> Self {
Self {
left: Some(w),
right: Some(w),
up: Some(w),
down: Some(w),
..Self::default()
}
}
pub fn upper_left(w: Weight) -> Self {
Self {
down: Some(w),
right: Some(w),
..Self::default()
}
}
pub fn upper_right(w: Weight) -> Self {
Self {
down: Some(w),
left: Some(w),
..Self::default()
}
}
pub fn lower_left(w: Weight) -> Self {
Self {
up: Some(w),
right: Some(w),
..Self::default()
}
}
pub fn lower_right(w: Weight) -> Self {
Self {
up: Some(w),
left: Some(w),
..Self::default()
}
}
pub fn up_tee(w: Weight) -> Self {
Self::cross(w).down(None)
}
pub fn right_tee(w: Weight) -> Self {
Self::cross(w).left(None)
}
pub fn left_tee(w: Weight) -> Self {
Self::cross(w).right(None)
}
pub fn down_tee(w: Weight) -> Self {
Self::cross(w).up(None)
}
pub fn up_half(w: Weight) -> Self {
Self::empty().up(w)
}
pub fn right_half(w: Weight) -> Self {
Self::empty().right(w)
}
pub fn left_half(w: Weight) -> Self {
Self::empty().left(w)
}
pub fn down_half(w: Weight) -> Self {
Self::empty().down(w)
}
pub fn up(self, w: impl Into<Option<Weight>>) -> Self {
Self {
up: w.into(),
..self
}
}
pub fn right(self, w: impl Into<Option<Weight>>) -> Self {
Self {
right: w.into(),
..self
}
}
pub fn left(self, w: impl Into<Option<Weight>>) -> Self {
Self {
left: w.into(),
..self
}
}
pub fn down(self, w: impl Into<Option<Weight>>) -> Self {
Self {
down: w.into(),
..self
}
}
pub fn style(self, s: impl Into<Option<Style>>) -> Self {
Self {
style: s.into(),
..self
}
}
pub fn ascii(self) -> Self {
Self {
ascii: true,
..self
}
}
#[inline]
pub fn weight(mut self, w: Weight) -> Self {
for w2 in self.weights().into_iter().flatten() {
*w2 = w;
}
self
}
#[inline]
pub fn fill(self, w: Weight) -> Self {
self.fill_from(Self::cross(w))
}
#[inline]
pub fn replace(
mut self,
from: impl Into<Option<Weight>>,
to: impl Into<Option<Weight>>,
) -> Self {
let from = from.into();
let to = to.into();
for w in self.weights() {
if *w == from {
*w = to
}
}
self
}
#[inline]
pub fn fill_from(mut self, mut that: Self) -> Self {
for (a, b) in
Iterator::zip(self.weights().into_iter(), that.weights().into_iter())
{
*a = a.or(*b);
}
self
}
#[inline]
pub fn crop_from(mut self, mut that: Self) -> Self {
for (a, b) in
Iterator::zip(self.weights().into_iter(), that.weights().into_iter())
{
*a = b.and(*a);
}
self
}
#[inline]
pub fn rotate_cw(mut self, turns: u32) -> Self {
let mut new = self;
let turns = turns as usize % 4;
for i in 0..4 {
*new.cw_weights()[(i + turns) % 4] = *self.cw_weights()[i];
}
new
}
pub fn rotate_ccw(self, turns: u32) -> Self {
self.rotate_cw(4 - (turns % 4))
}
pub fn hflip(self) -> Self {
Self {
left: self.right,
right: self.left,
..self
}
}
pub fn vflip(self) -> Self {
Self {
up: self.down,
down: self.up,
..self
}
}
pub fn into_char(mut self) -> char {
if self.ascii {
return self.into_ascii() as char;
}
let has_thick = self
.weights()
.iter()
.any(|w| matches!(w, Some(Weight::Thick)));
fn w2i(w: Option<Weight>) -> usize {
match w {
None => 0,
Some(Weight::Normal) => 1,
Some(Weight::Thick | Weight::Doubled) => 2,
}
}
let lut = if has_thick { &LUT_THICK } else { &LUT_DOUBLED };
let c = lut[w2i(self.down)][w2i(self.left)][w2i(self.right)][w2i(self.up)];
match (c, self.style) {
('─', Some(Style::Em)) => '╌',
('─', Some(Style::En)) => '┄',
('─', Some(Style::Dot)) => '┈',
('━', Some(Style::Em)) => '╍',
('━', Some(Style::En)) => '┅',
('━', Some(Style::Dot)) => '┉',
('│', Some(Style::Em)) => '╎',
('│', Some(Style::En)) => '┆',
('│', Some(Style::Dot)) => '┊',
('┃', Some(Style::Em)) => '╏',
('┃', Some(Style::En)) => '┇',
('┃', Some(Style::Dot)) => '┋',
('┌', Some(Style::Curved)) => '╭',
('┐', Some(Style::Curved)) => '╮',
('┘', Some(Style::Curved)) => '╯',
('└', Some(Style::Curved)) => '╰',
(c, _) => c,
}
}
#[rustfmt::skip]
pub fn into_ascii(self) -> u8 {
match self {
Self {
up: None, left: None, right: None, down: None,
..
} => b' ',
Self {
left: None, right: None,
style: Some(Style::Em | Style::En | Style::Dot),
..
} => b':',
Self {
left: None, right: None,
..
} => b'|',
Self {
up: None, down: None,
..
} => b'-',
Self {
up: None, right: None,
style: Some(Style::Curved),
..
} => b'.',
Self {
up: None, left: None,
style: Some(Style::Curved),
..
} => b'.',
Self {
down: None, right: None,
style: Some(Style::Curved),
..
} => b'\'',
Self {
down: None, left: None,
style: Some(Style::Curved),
..
} => b'\'',
_ => b'+',
}
}
fn weights(&mut self) -> [&mut Option<Weight>; 4] {
[
&mut self.up,
&mut self.right,
&mut self.left,
&mut self.down,
]
}
fn cw_weights(&mut self) -> [&mut Option<Weight>; 4] {
[
&mut self.up,
&mut self.right,
&mut self.down,
&mut self.left,
]
}
}
#[rustfmt::skip]
const LUT_THICK: [[[[char; 3]; 3]; 3]; 3] = [
[
[[' ', '╵', '╹'], ['╶', '└', '┖'], ['╺', '┕', '┗']],
[['╴', '┘', '┚'], ['─', '┴', '┸'], ['╼', '┶', '┺']],
[['╸', '┙', '┛'], ['╾', '┵', '┹'], ['━', '┷', '┻']],
],
[
[['╷', '│', '╿'], ['┌', '├', '┞'], ['┍', '┝', '┡']],
[['┐', '┤', '┦'], ['┬', '┼', '╀'], ['┮', '┾', '╄']],
[['┑', '┥', '┩'], ['┭', '┽', '╃'], ['┯', '┿', '╇']],
],
[
[['╻', '╽', '┃'], ['┎', '┟', '┠'], ['┎', '┢', '┣']],
[['┒', '┧', '┨'], ['┰', '╁', '╂'], ['┲', '╆', '╊']],
[['┓', '┪', '┫'], ['┱', '╅', '╉'], ['┳', '╈', '╋']],
],
];
#[rustfmt::skip]
const LUT_DOUBLED: [[[[char; 3]; 3]; 3]; 3] = [
[
[[' ', '╵', '║'], ['╶', '└', '╙'], ['═', '╘', '╚']],
[['╴', '┘', '╜'], ['─', '┴', '╨'], ['═', '╧', '╩']],
[['╸', '╛', '╝'], ['═', '╧', '╩'], ['═', '╧', '╩']],
],
[
[['╷', '│', '║'], ['┌', '├', '╟'], ['╒', '╞', '╠']],
[['┐', '┤', '╢'], ['┬', '┼', '╫'], ['╤', '╪', '╬']],
[['╕', '╡', '╣'], ['╤', '╪', '╬'], ['╤', '╪', '╬']],
],
[
[['║', '║', '║'], ['╓', '╟', '╟'], ['╔', '╠', '╠']],
[['╖', '╢', '╢'], ['╥', '╫', '╫'], ['╦', '╬', '╬']],
[['╗', '╣', '╣'], ['╦', '╬', '╬'], ['╦', '╬', '╬']],
],
];