add WindowMut

This commit is contained in:
Vinzenz Schroeter 2025-07-05 13:46:36 +02:00
parent 04f8884a14
commit c9ad4e3357
4 changed files with 160 additions and 5 deletions

View file

@ -1,5 +1,6 @@
use crate::{
DataRef, DisplayBitVec, Grid, Payload, ValueGrid, PIXEL_HEIGHT, PIXEL_WIDTH,
DataRef, DisplayBitVec, Grid, Payload, ValueGrid, WindowMut, PIXEL_HEIGHT,
PIXEL_WIDTH,
};
use ::bitvec::{order::Msb0, prelude::BitSlice, slice::IterMut};
use inherent::inherent;
@ -262,6 +263,20 @@ impl TryFrom<&ValueGrid<bool>> for Bitmap {
}
}
impl<G: Grid<bool>> TryFrom<&WindowMut<'_, bool, G>> for Bitmap {
type Error = ();
fn try_from(value: &WindowMut<'_, bool, G>) -> Result<Self, Self::Error> {
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
for y in 0..value.height() {
for x in 0..value.width() {
result.set(x, y, value.get(x, y));
}
}
Ok(result)
}
}
impl From<&Bitmap> for ValueGrid<bool> {
/// Converts a [Bitmap] into a grid of [bool]s.
fn from(value: &Bitmap) -> Self {
@ -315,9 +330,7 @@ pub enum LoadBitmapError {
#[cfg(test)]
mod tests {
use crate::{
Bitmap, DataRef, DisplayBitVec, LoadBitmapError, ValueGrid,
};
use crate::{Bitmap, DataRef, DisplayBitVec, LoadBitmapError, ValueGrid};
#[test]
fn fill() {

128
src/containers/grid_view.rs Normal file
View file

@ -0,0 +1,128 @@
use crate::Grid;
use std::marker::PhantomData;
#[derive(Debug)]
pub struct WindowMut<'t, TElement: Copy, TGrid: Grid<TElement>> {
grid: &'t mut TGrid,
x: usize,
y: usize,
width: usize,
height: usize,
phantom: PhantomData<TElement>,
}
impl<'t, TElement: Copy, TGrid: Grid<TElement>> WindowMut<'t, TElement, TGrid> {
#[must_use]
pub fn new(
grid: &'t mut TGrid,
x: usize,
y: usize,
width: usize,
height: usize,
) -> Option<Self> {
if width == 0 || height == 0 {
return None;
}
if !grid.is_in_bounds(x + width - 1, y + height - 1) {
return None;
}
Some(Self {
grid,
x,
y,
width,
height,
phantom: PhantomData,
})
}
}
impl<TElement: Copy, TGrid: Grid<TElement>> Grid<TElement>
for WindowMut<'_, TElement, TGrid>
{
fn set(&mut self, x: usize, y: usize, value: TElement) {
self.grid.set(x + self.x, y + self.y, value)
}
fn get(&self, x: usize, y: usize) -> TElement {
self.grid.get(x + self.x, y + self.y)
}
fn fill(&mut self, value: TElement) {
for y in self.y..(self.y + self.height) {
for x in self.x..(self.x + self.width) {
self.grid.set(x, y, value);
}
}
}
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
}
#[cfg(test)]
mod tests {
use crate::containers::grid_view::WindowMut;
use crate::{Bitmap, CharGrid, DataRef, Grid};
#[test]
fn test_grid_view_bitmap() {
let mut bitmap = Bitmap::new(8, 4).unwrap();
// non-byte-aligned views work
let mut view = WindowMut::new(&mut bitmap, 3, 1, 4, 2).unwrap();
view.fill(true);
assert_eq!(bitmap.data_ref(), &[0, 30, 30, 0]);
// full size view works
_ = WindowMut::new(&mut bitmap, 0, 0, 8, 4).unwrap();
// zero size view does not work
assert!(WindowMut::new(&mut bitmap, 1, 2, 3, 0).is_none());
assert!(WindowMut::new(&mut bitmap, 1, 2, 0, 1).is_none());
// oob does not work
assert!(WindowMut::new(&mut bitmap, 30, 43, 3, 1).is_none());
assert!(WindowMut::new(&mut bitmap, 0, 0, 9, 1).is_none());
assert!(WindowMut::new(&mut bitmap, 0, 0, 1, 5).is_none());
}
#[test]
fn test_grid_view_char_grid() {
let mut grid = CharGrid::new(3, 4);
grid.fill(' ');
let mut view = WindowMut::new(&mut grid, 1, 1, 1, 2).unwrap();
view.fill('#');
assert_eq!(
grid.data_ref(),
&[' ', ' ', ' ', ' ', '#', ' ', ' ', '#', ' ', ' ', ' ', ' ']
);
// full size view works
_ = WindowMut::new(&mut grid, 0, 0, 3, 4).unwrap();
// zero size view does not work
assert!(WindowMut::new(&mut grid, 1, 2, 2, 0).is_none());
assert!(WindowMut::new(&mut grid, 1, 2, 0, 1).is_none());
}
#[test]
fn round_trip_bitmap() {
let mut bitmap = Bitmap::new(8, 4).unwrap();
let non_aligned = WindowMut::new(&mut bitmap, 3, 1, 4, 2).unwrap();
assert_eq!(Bitmap::try_from(&non_aligned), Err(()));
let aligned = WindowMut::new(&mut bitmap, 0, 1, 8, 2).unwrap();
assert!(matches!(Bitmap::try_from(&aligned), Ok(_)));
}
}

View file

@ -6,6 +6,7 @@ mod char_grid;
mod cp437_grid;
mod data_ref;
mod grid;
mod grid_view;
mod value_grid;
pub use bit_vec::{bitvec, DisplayBitVec};
@ -16,6 +17,7 @@ pub use char_grid::CharGrid;
pub use cp437_grid::Cp437Grid;
pub use data_ref::DataRef;
pub use grid::Grid;
pub use grid_view::WindowMut;
pub use value_grid::{
IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid,
};

View file

@ -1,4 +1,4 @@
use crate::{DataRef, Grid};
use crate::{DataRef, Grid, WindowMut};
use inherent::inherent;
use std::fmt::Debug;
use std::slice::{Iter, IterMut};
@ -441,6 +441,18 @@ impl<T: Value> Iterator for EnumerateGrid<'_, T> {
}
}
impl<E: Value> From<&WindowMut<'_, E, Self>> for ValueGrid<E> {
fn from(value: &WindowMut<'_, E, Self>) -> Self {
let mut result = Self::new(value.width(), value.height());
for y in 0..value.height() {
for x in 0..value.width() {
result.set(x, y, value.get(x, y));
}
}
result
}
}
#[cfg(test)]
mod tests {
use crate::{SetValueSeriesError, ValueGrid, *};