summaryrefslogtreecommitdiffstats
path: root/packages/svgbob/src/buffer/cell_buffer/contacts.rs
blob: 02147d72c2ed849b1981878c2a20e6854544ff51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use super::endorse;
use super::endorse::Endorse;
use crate::buffer::fragment_buffer::FragmentSpan;
use crate::buffer::Span;
use crate::buffer::{fragment::Fragment, Cell};
use crate::Merge;
use std::fmt;

/// Contains a group of fragments that are touching each other
/// The purpose of Contacts is to group fragments together
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Contacts(pub Vec<FragmentSpan>);

impl AsRef<Vec<FragmentSpan>> for Contacts {
    fn as_ref(&self) -> &Vec<FragmentSpan> {
        &self.0
    }
}

impl AsMut<Vec<FragmentSpan>> for Contacts {
    fn as_mut(&mut self) -> &mut Vec<FragmentSpan> {
        &mut self.0
    }
}

impl Contacts {
    pub(crate) fn new(fragment: FragmentSpan) -> Self {
        Contacts(vec![fragment])
    }

    pub fn fragments(&self) -> Vec<&Fragment> {
        self.0.iter().map(|fs| &fs.fragment).collect()
    }

    pub fn cells(&self) -> Vec<Cell> {
        self.0.iter().flat_map(|fs| fs.cells()).collect()
    }

    pub fn span(&self) -> Span {
        let cell_chars: Vec<(Cell, char)> =
            self.0.iter().flat_map(|fs| fs.span.0.clone()).collect();
        cell_chars.into()
    }

    /// Check if any fragment can be group with any of the other fragment
    /// We use `.rev()` on this list of fragment since it has a high change of matching at the last
    /// added fragment of the next fragments to be checked.
    pub(crate) fn is_contacting_frag(&self, other_frag: &FragmentSpan) -> bool {
        self.as_ref()
            .iter()
            .rev()
            .any(|frag| frag.is_contacting(other_frag))
    }

    pub(crate) fn is_contacting(&self, other: &Self) -> bool {
        other
            .as_ref()
            .iter()
            .any(|other_frag| self.is_contacting_frag(other_frag))
    }

    ///TODO: return the FragmentSpan
    /// Endorse if the fragments in this group
    /// can be:
    ///  - rect
    ///  - rounded_rect
    pub(crate) fn endorse_rect(&self) -> Option<Fragment> {
        let fragments = self.fragments();
        if let Some(rect) = endorse::endorse_rect(&fragments) {
            Some(rect.into())
        } else {
            endorse::endorse_rounded_rect(&fragments)
                .map(|rounded_rect| rounded_rect.into())
        }
    }

    /// First phase of endorsing to shapes, in this case, rects and rounded_rects
    ///
    /// This function is calling on endorse methods that is applicable
    /// to fragments that are touching, to be promoted to a shape.
    /// These includes: rect, roundedrect,
    pub(crate) fn endorse_rects(
        contacts: Vec<Contacts>,
    ) -> Endorse<FragmentSpan, Contacts> {
        let mut accepted = vec![];
        let mut rejects: Vec<Contacts> = vec![];
        for contact in contacts {
            if let Some(fragment) = contact.endorse_rect() {
                let span = contact.span();
                let fragment_span = FragmentSpan::new(span, fragment);
                accepted.push(fragment_span);
            } else {
                rejects.push(contact);
            }
        }
        Endorse { accepted, rejects }
    }

    pub(crate) fn absolute_position(&self, cell: Cell) -> Self {
        Contacts(
            self.as_ref()
                .iter()
                .map(|frag| frag.absolute_position(cell))
                .collect(),
        )
    }

    pub fn is_bounded(&self, bound1: Cell, bound2: Cell) -> bool {
        self.cells()
            .iter()
            .all(|cell| cell.is_bounded(bound1, bound2))
    }

    pub fn hit_cell(&self, needle: Cell) -> bool {
        self.cells().iter().any(|cell| *cell == needle)
    }
}

impl Merge for Contacts {
    fn merge(&self, other: &Self) -> Option<Self> {
        if self.is_contacting(&other) {
            let mut fragment_spans: Vec<FragmentSpan> = self.0.clone();
            fragment_spans.extend_from_slice(&other.0);
            Some(Contacts(fragment_spans))
        } else {
            None
        }
    }
}

impl fmt::Display for Contacts {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for frag in self.as_ref().iter() {
            writeln!(f, "\t{}", frag)?;
        }
        Ok(())
    }
}