ascii_forge/renderer/
buffer.rs

1use crate::prelude::*;
2
3/**
4A screen buffer that can be rendered to, has a size
5
6This is the backbone of ascii-forge
7
8`Example`
9```rust
10# use ascii_forge::prelude::*;
11# fn main() {
12// A 30x30 buffer window
13let mut buffer = Buffer::new((30, 30));
14
15// Render Hello World to the top left of the buffer
16render!(buffer, (0, 0) => [ "Hello World!" ]);
17# }
18```
19
20*/
21#[derive(Debug)]
22pub struct Buffer {
23    size: Vec2,
24    cells: Vec<Cell>,
25}
26
27impl AsMut<Buffer> for Buffer {
28    fn as_mut(&mut self) -> &mut Buffer {
29        self
30    }
31}
32
33impl Buffer {
34    /// Creates a new buffer of empty cells with the given size.
35    pub fn new(size: impl Into<Vec2>) -> Self {
36        let size = size.into();
37        Self {
38            size,
39            cells: vec![Cell::default(); size.x as usize * size.y as usize],
40        }
41    }
42
43    /// Returns the current size of the buffer.
44    pub fn size(&self) -> Vec2 {
45        self.size
46    }
47
48    /// Sets a cell at the given location to the given cell
49    pub fn set<C: Into<Cell>>(&mut self, loc: impl Into<Vec2>, cell: C) {
50        let loc = loc.into();
51        let idx = self.index_of(loc);
52
53        // Ignore if cell is out of bounds
54        let Some(idx) = idx else {
55            return;
56        };
57
58        let cell = cell.into();
59
60        for i in 1..cell.width().saturating_sub(1) {
61            self.set(loc + vec2(i, 0), Cell::default());
62        }
63
64        self.cells[idx] = cell;
65    }
66
67    /// Sets all cells at the given location to the given cell
68    pub fn fill<C: Into<Cell>>(&mut self, cell: C) {
69        let cell = cell.into();
70        for i in 0..self.cells.len() {
71            self.cells[i] = cell.clone()
72        }
73    }
74
75    /// Returns a reverence to the cell at the given location.
76    pub fn get(&self, loc: impl Into<Vec2>) -> Option<&Cell> {
77        let idx = self.index_of(loc)?;
78        self.cells.get(idx)
79    }
80
81    /// Returns a mutable reference to the cell at the given location.
82    pub fn get_mut(&mut self, loc: impl Into<Vec2>) -> Option<&mut Cell> {
83        let idx = self.index_of(loc)?;
84        self.cells.get_mut(idx)
85    }
86
87    fn index_of(&self, loc: impl Into<Vec2>) -> Option<usize> {
88        let loc = loc.into();
89        let idx = loc.y as usize * self.size.x as usize + loc.x as usize;
90
91        if (idx as u16) >= self.size.x * self.size.y {
92            return None;
93        }
94
95        Some(idx.min((self.size.x as usize * self.size.y as usize) - 1))
96    }
97
98    /// Clears the buffer
99    pub fn clear(&mut self) {
100        *self = Self::new(self.size);
101    }
102
103    /// Returns the cells and locations that are different between the two buffers
104    pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(Vec2, &'a Cell)> {
105        assert!(self.size == other.size);
106
107        let mut res = vec![];
108        let mut skip = 0;
109
110        for y in 0..self.size.y {
111            for x in 0..self.size.x {
112                if skip > 0 {
113                    skip -= 1;
114                    continue;
115                }
116
117                let old = self.get((x, y));
118                let new = other.get((x, y));
119
120                if old != new {
121                    let new = new.expect("new should be in bounds");
122                    skip = new.width().saturating_sub(1) as usize;
123                    res.push((vec2(x, y), new))
124                }
125            }
126        }
127
128        res
129    }
130
131    /// Shrinks the buffer to the given size by dropping any cells that are only whitespace
132    pub fn shrink(&mut self) {
133        let mut max_whitespace_x = 0;
134        let mut max_whitespace_y = 0;
135        for x in (0..self.size.x).rev() {
136            for y in (0..self.size.y).rev() {
137                if !self
138                    .get((x, y))
139                    .expect("Cell should be in bounds")
140                    .is_empty()
141                {
142                    max_whitespace_x = x.max(max_whitespace_x);
143                    max_whitespace_y = y.max(max_whitespace_y);
144                }
145            }
146        }
147
148        self.resize(vec2(max_whitespace_x + 1, max_whitespace_y + 1));
149    }
150
151    /// Resizes the buffer while retaining elements that have already been rendered
152    pub fn resize(&mut self, new_size: impl Into<Vec2>) {
153        let new_size = new_size.into();
154        if self.size == new_size {
155            return;
156        }
157
158        let mut new_elements = vec![];
159
160        for y in 0..new_size.y {
161            for x in 0..new_size.x {
162                new_elements.push(self.get((x, y)).expect("Cell should be in bounds").clone());
163            }
164        }
165
166        self.size = new_size;
167        self.cells = new_elements;
168    }
169
170    /// Creates a Buffer from the given element with the minimum size it could have for that element.
171    /// Useful for if you want to store any set of render elements in a custom element.
172    pub fn sized_element<R: Render>(item: R) -> Self {
173        let mut buff = Buffer::new((100, 100));
174        render!(buff, vec2(0, 0) => [ item ]);
175        buff.shrink();
176        buff
177    }
178}
179
180impl Render for Buffer {
181    fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
182        for x in 0..self.size.x {
183            if x + loc.x >= buffer.size.x {
184                break;
185            }
186
187            for y in 0..self.size.y {
188                if y + loc.y >= buffer.size.y {
189                    break;
190                }
191
192                let dest = vec2(x + loc.x, y + loc.y);
193
194                buffer.set(
195                    dest,
196                    self.get(vec2(x, y))
197                        .expect("Cell should be in bounds")
198                        .clone(),
199                );
200            }
201        }
202        vec2(loc.x + buffer.size().x, loc.y + buffer.size().y)
203    }
204}