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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
use crate::prelude::*;
/**
A screen buffer that can be rendered to, has a size
This is the backbone of ascii-forge
`Example`
```rust, no_run
use ascii_forge::prelude::*;
// A 30x30 buffer window
let mut buffer = Buffer::new(30, 30);
// Render Hello World to the top left of the buffer
render!(
buffer, {
(0, 0) => "Hello World!"
}
);
```
*/
#[derive(Debug)]
pub struct Buffer {
size: Vec2,
cells: Vec<Cell>,
}
impl AsMut<Buffer> for Buffer {
fn as_mut(&mut self) -> &mut Buffer {
self
}
}
impl Buffer {
/// Creates a new buffer of empty cells with the given size.
pub fn new(size: impl Into<Vec2>) -> Self {
let size = size.into();
Self {
size,
cells: vec![Cell::default(); size.x as usize * size.y as usize],
}
}
/// Returns the current size of the buffer.
pub fn size(&self) -> Vec2 {
self.size
}
/// Sets a cell at the given location to the given cell
pub fn set<C: Into<Cell>>(&mut self, loc: impl Into<Vec2>, cell: C) {
let idx = self.index_of(loc);
self.cells[idx] = cell.into();
}
/// Sets all cells at the given location to the given cell
pub fn fill<C: Into<Cell>>(&mut self, cell: C) {
let cell = cell.into();
for i in 0..self.cells.len() {
self.cells[i] = cell.clone()
}
}
/// Returns a reverence to the cell at the given location.
pub fn get(&self, loc: impl Into<Vec2>) -> &Cell {
let idx = self.index_of(loc);
&self.cells[idx]
}
/// Returns a mutable reference to the cell at the given location.
pub fn get_mut(&mut self, loc: impl Into<Vec2>) -> &mut Cell {
let idx = self.index_of(loc);
&mut self.cells[idx]
}
fn index_of(&self, loc: impl Into<Vec2>) -> usize {
let loc = loc.into();
let idx = loc.y as usize * self.size.x as usize + loc.x as usize;
debug_assert!((idx as u16) < self.size.x * self.size.y);
idx.min((self.size.x as usize * self.size.y as usize) - 1)
}
/// Clears the buffer
pub fn clear(&mut self) {
*self = Self::new(self.size);
}
/// Returns the cells and locations that are different between the two buffers
pub fn diff<'a>(&self, other: &'a Buffer) -> Vec<(Vec2, &'a Cell)> {
assert!(self.size == other.size);
let mut res = vec![];
for x in 0..self.size.x {
for y in 0..self.size.y {
if self.get((x, y)) != other.get((x, y)) {
res.push((vec2(x, y), other.get((x, y))))
}
}
}
res
}
/// Shrinks the buffer to the given size by dropping any cells that are only whitespace
pub fn shrink(&mut self) {
let mut max_whitespace_x = 0;
let mut max_whitespace_y = 0;
for x in (0..self.size.x).rev() {
for y in (0..self.size.y).rev() {
if !self.get((x, y)).is_empty() {
max_whitespace_x = x.max(max_whitespace_x);
max_whitespace_y = y.max(max_whitespace_y);
}
}
}
self.resize(vec2(max_whitespace_x + 1, max_whitespace_y + 1));
}
/// Resizes the buffer while retaining elements that have already been rendered
pub fn resize(&mut self, new_size: impl Into<Vec2>) {
let new_size = new_size.into();
if self.size == new_size {
return;
}
let mut new_elements = vec![];
for y in 0..new_size.y {
for x in 0..new_size.x {
new_elements.push(self.get((x, y)).clone());
}
}
self.size = new_size;
self.cells = new_elements;
}
/// Creates a Buffer from the given element with the minimum size it could have for that element.
/// Useful for if you want to store any set of render elements in a custom element.
pub fn sized_element<R: Render>(item: R) -> Self {
let mut buff = Buffer::new((100, 100));
render!(buff, vec2(0, 0) => [ item ]);
buff.shrink();
buff
}
}
impl Render for Buffer {
fn render(&self, loc: Vec2, buffer: &mut Buffer) -> Vec2 {
for x in 0..self.size.x {
if x + loc.x >= buffer.size.x {
break;
}
for y in 0..self.size.y {
if y + loc.y >= buffer.size.y {
break;
}
let dest = vec2(x + loc.x, y + loc.y);
buffer.set(dest, self.get(vec2(x, y)).clone());
}
}
vec2(loc.x + buffer.size().x, loc.y + buffer.size().y)
}
}