summaryrefslogtreecommitdiffstats
path: root/packages/svgbob/src/buffer/cell_buffer/contacts.rs
blob: 7805b78037016b4f2933fcdf96bb78ce41ae63c7 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use super::endorse;
use crate::buffer::fragment_buffer::FragmentSpan;
use crate::buffer::Span;
use crate::buffer::{fragment::Fragment, Cell};
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)]
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())
        }
    }

    pub(crate) fn group_recursive(groups: Vec<Contacts>) -> Vec<Contacts> {
        let original_len = groups.len();
        let grouped = Self::second_pass_groupable(groups);
        // continue calling group recursive until the original len
        // has not decreased
        if grouped.len() < original_len {
            Self::group_recursive(grouped)
        } else {
            grouped
        }
    }

    fn second_pass_groupable(groups: Vec<Contacts>) -> Vec<Contacts> {
        let mut new_groups: Vec<Contacts> = vec![];
        for group in groups.into_iter() {
            let is_grouped = new_groups.iter_mut().any(|new_group| {
                if new_group.is_contacting(&group) {
                    new_group.as_mut().extend_from_slice(group.as_ref());
                    true
                } else {
                    false
                }
            });
            if !is_grouped {
                new_groups.push(group);
            }
        }
        new_groups
    }

    /// 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(
        groups: Vec<Contacts>,
    ) -> (Vec<FragmentSpan>, Vec<Contacts>) {
        let mut fragments = vec![];
        let mut un_endorsed_rect: Vec<Contacts> = vec![];
        for group in groups {
            if let Some(fragment) = group.endorse_rect() {
                let span = group.span();
                let fragment_span = FragmentSpan::new(span, fragment);
                fragments.push(fragment_span);
            } else {
                un_endorsed_rect.push(group);
            }
        }
        (fragments, un_endorsed_rect)
    }

    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 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(())
    }
}