svgbob/buffer/cell_buffer/
span.rs

1use crate::{
2    buffer::{
3        cell_buffer::{Contacts, Endorse},
4        fragment_buffer::FragmentSpan,
5        FragmentBuffer, Property, PropertyBuffer, StringBuffer,
6    },
7    fragment,
8    fragment::Circle,
9    map::{circle_map, UNICODE_FRAGMENTS},
10    Cell, Fragment, Merge, Point, Settings,
11};
12use itertools::Itertools;
13use std::{
14    fmt,
15    ops::{Deref, DerefMut},
16};
17
18/// A describes where a char came from relative to the source ascii text
19/// The primary purpose of span is to group adjacent cell together
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Span(pub Vec<(Cell, char)>);
22
23impl Deref for Span {
24    type Target = Vec<(Cell, char)>;
25
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30
31pub struct Bounds {
32    top_left: Cell,
33    bottom_right: Cell,
34}
35
36impl DerefMut for Span {
37    fn deref_mut(&mut self) -> &mut Self::Target {
38        &mut self.0
39    }
40}
41
42impl From<Vec<(Cell, char)>> for Span {
43    fn from(cell_chars: Vec<(Cell, char)>) -> Self {
44        Span(cell_chars)
45    }
46}
47
48impl Span {
49    pub(crate) fn new(cell: Cell, ch: char) -> Self {
50        Span(vec![(cell, ch)])
51    }
52
53    pub(super) fn is_adjacent(&self, cell: &Cell) -> bool {
54        self.iter()
55            .rev()
56            .any(|(ex_cell, _)| ex_cell.is_adjacent(cell))
57    }
58
59    /// if any cell of this span is adjacent to any cell of the other
60    /// Use .rev() to check the last cell of this Span against the first cell of the other Span
61    /// They have a high change of matching faster
62    pub(super) fn can_merge(&self, other: &Self) -> bool {
63        self.iter().rev().any(|(cell, _)| {
64            other
65                .iter()
66                .any(|(other_cell, _)| cell.is_adjacent(other_cell))
67        })
68    }
69
70    /// paste the other Span at cell location `loc`
71    pub fn paste_at(&self, loc: Cell, other: &Self) -> Self {
72        let mut this = self.clone();
73        for (cell, ch) in other.deref() {
74            this.push((*cell + loc, *ch));
75        }
76        this.sort();
77        this.dedup();
78        this
79    }
80
81    fn top_left(&self) -> Cell {
82        let bounds = self.bounds().expect("must have bounds");
83        bounds.0
84    }
85
86    pub fn localize_point(&self, point: Point) -> Point {
87        self.top_left().localize_point(point)
88    }
89
90    /// returns the top_left most cell which aligns the top most and the left most cell.
91    pub(crate) fn bounds(&self) -> Option<(Cell, Cell)> {
92        if let Some((min_y, max_y)) =
93            self.iter().map(|(cell, _)| cell.y).minmax().into_option()
94        {
95            if let Some((min_x, max_x)) =
96                self.iter().map(|(cell, _)| cell.x).minmax().into_option()
97            {
98                Some((Cell::new(min_x, min_y), Cell::new(max_x, max_y)))
99            } else {
100                None
101            }
102        } else {
103            None
104        }
105    }
106
107    pub fn cell_bounds(&self) -> Option<Bounds> {
108        if let Some((top_left, top_right)) = self.bounds() {
109            Some(Bounds::new(top_left, top_right))
110        } else {
111            None
112        }
113    }
114
115    /// shift the cells relative to the top_left most bound
116    pub(crate) fn localize(self) -> Self {
117        if let Some((tl, _br)) = self.bounds() {
118            let mut new_self = Span(vec![]);
119            for (cell, ch) in self.iter() {
120                let local_cell = tl.localize_cell(*cell);
121                new_self.push((local_cell, *ch));
122            }
123            new_self
124        } else {
125            self
126        }
127    }
128
129    /// convert this span into fragments applying endorsement
130    /// of group into fragments
131    ///
132    ///
133    /// TODO: return the rejects as Span, instead of Contacts
134    pub(crate) fn endorse(self) -> Endorse<FragmentSpan, Span> {
135        // try to endorse as circles or arcs
136        let (mut accepted, un_endorsed_span): (Vec<FragmentSpan>, Span) =
137            self.endorse_to_arcs_and_circles();
138
139        // convert into contacts and try to endorse as rects fragments
140        let un_endorsed_contacts: Vec<Contacts> = un_endorsed_span.into();
141        let rect_endorsed: Endorse<FragmentSpan, Contacts> =
142            Contacts::endorse_rects(un_endorsed_contacts);
143
144        accepted.extend(rect_endorsed.accepted);
145
146        let re_endorsed = Self::re_endorse(rect_endorsed.rejects);
147
148        let mut endorsed = Endorse {
149            accepted,
150            rejects: vec![],
151        };
152        endorsed.extend(re_endorsed);
153        endorsed
154    }
155
156    /// re try endorsing the contacts into arc and circles by converting it to span first
157    fn re_endorse(rect_rejects: Vec<Contacts>) -> Endorse<FragmentSpan, Span> {
158        // convert back to span
159        let span_rejects: Vec<Span> = rect_rejects
160            .into_iter()
161            .map(|contact| contact.span())
162            .collect();
163
164        let span_rejects: Vec<Span> = Span::merge_recursive(span_rejects);
165
166        // try to endorse as circles or arcs one more time
167        let (accepted, rejects): (Vec<Vec<FragmentSpan>>, Vec<Span>) =
168            span_rejects
169                .into_iter()
170                .map(|span| span.endorse_to_arcs_and_circles())
171                .unzip();
172
173        Endorse {
174            accepted: accepted.into_iter().flatten().collect(),
175            rejects,
176        }
177    }
178
179    /// endorse this span into circles, half_circle, quarter_circle only
180    fn endorse_to_arcs_and_circles(self) -> (Vec<FragmentSpan>, Span) {
181        let mut accepted = vec![];
182        let (top_left, _) = self.bounds().expect("must have bounds");
183        let un_endorsed_span: Span = if let Some((circle, un_endorsed_span)) =
184            circle_map::endorse_circle_span(&self)
185        {
186            let circle = circle.absolute_position(top_left);
187            let circle_frag_span =
188                FragmentSpan::new(self.clone(), circle.into());
189            accepted.push(circle_frag_span);
190            un_endorsed_span
191        } else if let Some((three_quarters_arc, un_endorsed_span)) =
192            circle_map::endorse_three_quarters_arc_span(&self)
193        {
194            let three_quarters_arc =
195                three_quarters_arc.absolute_position(top_left);
196            let three_quarters_arc_frag_span =
197                FragmentSpan::new(self.clone(), three_quarters_arc.into());
198            accepted.push(three_quarters_arc_frag_span);
199            un_endorsed_span
200        } else if let Some((half_arc, un_endorsed_span)) =
201            circle_map::endorse_half_arc_span(&self)
202        {
203            let half_arc = half_arc.absolute_position(top_left);
204            let half_arc_frag_span =
205                FragmentSpan::new(self.clone(), half_arc.into());
206            accepted.push(half_arc_frag_span);
207            un_endorsed_span
208        } else if let Some((arc, un_endorsed_span)) =
209            circle_map::endorse_quarter_arc_span(&self)
210        {
211            let arc = arc.absolute_position(top_left);
212            let arc_frag_span = FragmentSpan::new(self.clone(), arc.into());
213            accepted.push(arc_frag_span);
214            un_endorsed_span
215        } else {
216            self
217        };
218        (accepted, un_endorsed_span)
219    }
220
221    /// create a span of the cells that is inside of the start and end bound cells
222    pub(crate) fn extract(&self, bound1: Cell, bound2: Cell) -> Self {
223        Span(
224            self.iter()
225                .map(|(cell, ch)| (*cell, *ch))
226                .filter(|(cell, _ch)| cell.is_bounded(bound1, bound2))
227                .collect(),
228        )
229    }
230
231    /// returns true if any cell on this span
232    /// is within the bounds of `bound1` and `bound2`
233    pub fn is_bounded(&self, bound1: Cell, bound2: Cell) -> bool {
234        self.iter()
235            .all(|(cell, ch)| cell.is_bounded(bound1, bound2))
236    }
237
238    pub fn hit_cell(&self, needle: Cell) -> bool {
239        self.iter().any(|(cell, ch)| *cell == needle)
240    }
241
242    /// merge as is without checking it it can
243    pub fn merge_no_check(&self, other: &Self) -> Self {
244        let mut cells = self.0.clone();
245        cells.extend(&other.0);
246        Span(cells)
247    }
248}
249
250impl Merge for Span {
251    fn merge(&self, other: &Self) -> Option<Self> {
252        if self.can_merge(other) {
253            Some(self.merge_no_check(other))
254        } else {
255            None
256        }
257    }
258}
259
260impl Bounds {
261    pub fn new(cell1: Cell, cell2: Cell) -> Self {
262        let (top_left, bottom_right) = Cell::rearrange_bound(cell1, cell2);
263        Self {
264            top_left,
265            bottom_right,
266        }
267    }
268
269    pub fn top_left(&self) -> Cell {
270        self.top_left
271    }
272
273    pub fn bottom_right(&self) -> Cell {
274        self.bottom_right
275    }
276
277    pub fn top_right(&self) -> Cell {
278        Cell::new(self.bottom_right.x, self.top_left.y)
279    }
280
281    pub fn bottom_left(&self) -> Cell {
282        Cell::new(self.top_left.x, self.bottom_right.y)
283    }
284}
285
286/// create a property buffer for all the cells of this span
287impl<'p> From<Span> for PropertyBuffer<'p> {
288    fn from(span: Span) -> Self {
289        let mut pb = PropertyBuffer::new();
290        for (cell, ch) in span.iter() {
291            if let Some(property) = Property::from_char(*ch) {
292                pb.as_mut().insert(*cell, property);
293            }
294        }
295        pb
296    }
297}
298
299/// Grouping cell by adjacents are not enough
300///
301/// grouping them together when they are actually connected
302/// is the most approprivate way of grouping
303/// Span just provides an optimization of the number
304/// of elements to be checked.
305/// Only elements on the same span are checked to see if they
306/// belong on the same group
307///
308impl From<Span> for Vec<Contacts> {
309    fn from(span: Span) -> Vec<Contacts> {
310        let fb = FragmentBuffer::from(span);
311        let merged_fragments: Vec<FragmentSpan> = fb.merge_fragment_spans();
312        let contacts: Vec<Contacts> = merged_fragments
313            .into_iter()
314            .map(|frag| Contacts::new(frag))
315            .collect();
316        Contacts::merge_recursive(contacts)
317    }
318}
319
320/// First we crate a property buffer based on the cell,char content of this span
321/// and then based on the property, we extract the accurate fragments
322///
323/// If a character has no property, try to see if has equivalent fragments from unicode_map
324/// otherwise add it to the fragment_buffer as a text fragment
325impl From<Span> for FragmentBuffer {
326    fn from(span: Span) -> FragmentBuffer {
327        let pb = PropertyBuffer::from(span.clone());
328        let mut fb = FragmentBuffer::from(pb.clone());
329        for (cell, ch) in span.iter() {
330            if pb.as_ref().get(cell).is_none() {
331                if let Some(fragments) = UNICODE_FRAGMENTS.get(ch) {
332                    fb.add_fragments_to_cell(*cell, *ch, fragments.clone());
333                } else {
334                    fb.add_fragment_to_cell(
335                        *cell,
336                        *ch,
337                        fragment::cell_text(*ch),
338                    );
339                }
340            }
341        }
342        fb
343    }
344}
345
346impl fmt::Display for Span {
347    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348        let mut buffer = StringBuffer::new();
349        if let Some((tl, _br)) = self.bounds() {
350            for (cell, ch) in self.iter() {
351                if *ch != '\0' && !ch.is_whitespace() {
352                    let local = tl.localize_cell(*cell);
353                    buffer.add_char(local.x, local.y, *ch);
354                }
355            }
356        }
357        write!(f, "{}", buffer.to_string())
358    }
359}
360
361#[cfg(test)]
362mod test_span;