use self::Signal::Strong;
use crate::{
map::{ASCII_PROPERTIES, UNICODE_PROPERTIES},
Fragment, Point, Settings,
};
use std::{cmp, fmt, sync::Arc};
#[derive(PartialEq, Clone)]
pub enum Signal {
Faint,
Weak,
Medium,
Strong,
}
impl Signal {
fn intensity(&self) -> u8 {
match self {
Signal::Faint => 1,
Signal::Weak => 2,
Signal::Medium => 3,
Signal::Strong => 4,
}
}
}
impl PartialOrd for Signal {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.intensity().cmp(&other.intensity()))
}
}
#[derive(Clone)]
pub struct Property {
pub ch: char,
signature: Vec<(Signal, Vec<Fragment>)>,
pub behavior: Arc<
dyn Fn(
&Settings,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
) -> Vec<(bool, Vec<Fragment>)>
+ Sync
+ Send,
>,
}
impl fmt::Debug for Property {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
"{{ char: {}, has {} signature }}",
self.ch,
self.signature.len()
)
}
}
impl Property {
pub fn new(
ch: char,
signature: Vec<(Signal, Vec<Fragment>)>,
behavior: Arc<
dyn Fn(
&Settings,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
&Property,
) -> Vec<(bool, Vec<Fragment>)>
+ Sync
+ Send,
>,
) -> Self {
Property {
ch,
signature,
behavior,
}
}
pub(in crate) fn from_char<'a>(ch: char) -> Option<&'a Property> {
match ASCII_PROPERTIES.get(&ch) {
Some(property) => Some(property),
None => UNICODE_PROPERTIES.get(&ch),
}
}
pub fn empty() -> Self {
Property {
ch: ' ',
signature: vec![],
behavior: Arc::new(|_, _, _, _, _, _, _, _, _| vec![]),
}
}
pub(in crate) fn with_strong_fragments(ch: char, fragments: Vec<Fragment>) -> Self {
Property {
ch,
signature: vec![(Signal::Strong, fragments)],
behavior: Arc::new(|_, _, _, _, _, _, _, _, _| vec![(true, vec![])]),
}
}
fn signature_fragments_with_signal(&self, signal: Signal) -> Vec<Fragment> {
let mut fragments: Vec<Fragment> = self
.signature
.iter()
.filter_map(|(sig, fragments)| {
if *sig == signal {
Some(fragments)
} else {
None
}
})
.flatten()
.map(Clone::clone)
.collect();
fragments.sort();
fragments.dedup();
fragments
}
pub(in crate) fn is(&self, ch: char) -> bool {
self.ch == ch
}
pub(crate) fn is_alphabet(&self) -> bool {
self.ch.is_alphabetic() && self.ch != '_'
}
pub(in crate) fn match_signature(&self, fragments: &Vec<Fragment>) -> bool {
let signature_fragments = self.signature_fragments_with_signal(Strong);
signature_fragments == *fragments
}
pub(in crate) fn match_property(&self, fragments: &Vec<Fragment>) -> bool {
false
}
pub(in crate) fn line_overlap(&self, a: Point, b: Point) -> bool {
self.line_overlap_with_signal(a, b, Signal::Medium)
}
pub(in crate) fn line_strongly_overlap(&self, a: Point, b: Point) -> bool {
self.line_overlap_with_signal(a, b, Signal::Strong)
}
pub(in crate) fn line_weakly_overlap(&self, a: Point, b: Point) -> bool {
self.line_overlap_with_signal(a, b, Signal::Weak)
}
pub(in crate) fn has_endpoint(&self, p: Point) -> bool {
self.signature
.iter()
.any(|(_signal, signature)| signature.iter().any(|fragment| fragment.has_endpoint(p)))
}
fn line_overlap_with_signal(&self, a: Point, b: Point, required_signal: Signal) -> bool {
self.signature
.iter()
.filter(|(signal, _signature)| *signal >= required_signal)
.any(|(_signal, signature)| {
signature.iter().any(|fragment| fragment.line_overlap(a, b))
})
}
pub(in crate) fn arcs_to(&self, a: Point, b: Point) -> bool {
self.signature
.iter()
.any(|(_signal, signature)| signature.iter().any(|fragment| fragment.arcs_to(a, b)))
}
pub(in crate) fn fragments(
&self,
settings: &Settings,
top_left: &Property,
top: &Property,
top_right: &Property,
left: &Property,
right: &Property,
bottom_left: &Property,
bottom: &Property,
bottom_right: &Property,
) -> Vec<Fragment> {
let bool_fragments = self.behavior.as_ref()(
settings,
top_left,
top,
top_right,
left,
right,
bottom_left,
bottom,
bottom_right,
);
let cell_fragments: Vec<Fragment> =
bool_fragments
.into_iter()
.fold(vec![], |mut acc, (passed, fragments)| {
if passed {
acc.extend(fragments);
};
acc
});
cell_fragments
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::buffer::CellGrid;
#[test]
fn test_overlap() {
let _a = CellGrid::a();
let _b = CellGrid::b();
let c = CellGrid::c();
let _d = CellGrid::d();
let _e = CellGrid::e();
let _f = CellGrid::f();
let _g = CellGrid::g();
let _h = CellGrid::h();
let _i = CellGrid::i();
let _j = CellGrid::j();
let k = CellGrid::k();
let _l = CellGrid::l();
let m = CellGrid::m();
let _n = CellGrid::n();
let o = CellGrid::o();
let _p = CellGrid::p();
let _q = CellGrid::q();
let _r = CellGrid::r();
let _s = CellGrid::s();
let _t = CellGrid::t();
let _u = CellGrid::u();
let _v = CellGrid::v();
let w = CellGrid::w();
let _x = CellGrid::x();
let _y = CellGrid::y();
let dash = Property::from_char('-').expect("should have 1");
assert!(dash.line_overlap(k, o));
assert!(!dash.line_overlap(c, w));
let vert = Property::from_char('|').expect("should have 1");
assert!(!vert.line_overlap(k, o));
assert!(vert.line_overlap(c, w));
let plus = Property::from_char('+').expect("should have 1");
assert!(plus.line_overlap(k, o));
assert!(plus.line_overlap(c, w));
assert!(plus.line_overlap(m, o));
assert!(plus.line_overlap(m, w));
assert!(plus.line_overlap(c, m));
assert!(plus.line_overlap(k, m));
}
}