Windows, light removals, move more functions to traits #15
8 changed files with 303 additions and 289 deletions
|
|
@ -285,43 +285,23 @@ impl From<Bitmap> for DisplayBitVec {
|
||||||
impl TryFrom<&ValueGrid<bool>> for Bitmap {
|
impl TryFrom<&ValueGrid<bool>> for Bitmap {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
/// Converts a grid of [bool]s into a [Bitmap].
|
|
||||||
///
|
|
||||||
/// Returns Err if the width of `value` is not dividable by 8
|
|
||||||
fn try_from(value: &ValueGrid<bool>) -> Result<Self, Self::Error> {
|
fn try_from(value: &ValueGrid<bool>) -> Result<Self, Self::Error> {
|
||||||
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
|
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
|
||||||
for (mut to, from) in result.iter_mut().zip(value.iter()) {
|
result.deref_assign(value);
|
||||||
*to = *from;
|
|
||||||
}
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G: Grid<bool>> TryFrom<&Window<'_, bool, G>> for Bitmap {
|
impl<T: Grid<bool>> TryFrom<&Window<'_, bool, T>> for Bitmap {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: &Window<'_, bool, G>) -> Result<Self, Self::Error> {
|
fn try_from(value: &Window<bool, T>) -> Result<Self, Self::Error> {
|
||||||
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
|
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
|
||||||
for y in 0..value.height() {
|
result.deref_assign(value);
|
||||||
for x in 0..value.width() {
|
|
||||||
result.set(x, y, value.get(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Bitmap> for ValueGrid<bool> {
|
|
||||||
/// Converts a [Bitmap] into a grid of [bool]s.
|
|
||||||
fn from(value: &Bitmap) -> Self {
|
|
||||||
let mut result = Self::new(value.width(), value.height());
|
|
||||||
for (to, from) in result.iter_mut().zip(value.iter()) {
|
|
||||||
*to = *from;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Bitmap> for Payload {
|
impl From<&Bitmap> for Payload {
|
||||||
fn from(value: &Bitmap) -> Self {
|
fn from(value: &Bitmap) -> Self {
|
||||||
value.bit_vec.as_raw_slice().into()
|
value.bit_vec.as_raw_slice().into()
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ mod tests {
|
||||||
let mut grid = BrightnessGrid::new(2, 2);
|
let mut grid = BrightnessGrid::new(2, 2);
|
||||||
grid.set(1, 0, Brightness::MIN);
|
grid.set(1, 0, Brightness::MIN);
|
||||||
grid.set(0, 1, Brightness::MAX);
|
grid.set(0, 1, Brightness::MAX);
|
||||||
let actual = ValueGrid::from(&grid);
|
let actual: ValueGrid<u8> = ValueGrid::from(&grid);
|
||||||
assert_eq!(actual.data_ref(), &[11, 0, 11, 11]);
|
assert_eq!(actual.data_ref(), &[11, 0, 11, 11]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::{
|
use crate::{CharGridMutExt, TryLoadValueGridError, ValueGrid};
|
||||||
Grid, GridMut, SetValueSeriesError, TryLoadValueGridError, ValueGrid,
|
|
||||||
};
|
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
/// A grid containing UTF-8 characters.
|
/// A grid containing UTF-8 characters.
|
||||||
|
|
@ -60,183 +58,6 @@ impl CharGrid {
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a [`CharGrid`] with the specified dimensions from the provided UTF-8 bytes.
|
|
||||||
///
|
|
||||||
/// returns: [`CharGrid`] that contains the provided data, or [`FromUtf8Error`] if the data is invalid.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use servicepoint::CharGrid;
|
|
||||||
/// let grid = CharGrid::load_utf8(2, 2, [97u8, 98, 99, 100].to_vec());
|
|
||||||
/// ```
|
|
||||||
pub fn load_utf8(
|
|
||||||
width: usize,
|
|
||||||
height: usize,
|
|
||||||
bytes: Vec<u8>,
|
|
||||||
) -> Result<CharGrid, LoadUtf8Error> {
|
|
||||||
let s: Vec<char> = String::from_utf8(bytes)?.chars().collect();
|
|
||||||
CharGrid::load(width, height, &s).ok_or(LoadUtf8Error::TryLoadError(
|
|
||||||
TryLoadValueGridError::InvalidDimensions,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CharGridExt: Grid<char> {
|
|
||||||
/// Copies a column from the grid as a String.
|
|
||||||
///
|
|
||||||
/// Returns [None] if x is out of bounds.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use servicepoint::CharGrid;
|
|
||||||
/// let grid = CharGrid::from("ab\ncd");
|
|
||||||
/// let col = grid.get_col_str(0).unwrap(); // "ac"
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
fn get_col_str(&self, x: usize) -> Option<String> {
|
|
||||||
Some((0..self.height()).map(|y| self.get(x, y)).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies a row from the grid as a String.
|
|
||||||
///
|
|
||||||
/// Returns [None] if y is out of bounds.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use servicepoint::CharGrid;
|
|
||||||
/// let grid = CharGrid::from("ab\ncd");
|
|
||||||
/// let row = grid.get_row_str(0).unwrap(); // "ab"
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
fn get_row_str(&self, y: usize) -> Option<String> {
|
|
||||||
Some((0..self.width()).map(|x| self.get(x, y)).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inherent::inherent]
|
|
||||||
impl CharGridExt for CharGrid {
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_col_str(&self, x: usize) -> Option<String> {
|
|
||||||
Some(String::from_iter(self.get_col(x)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_row_str(&self, y: usize) -> Option<String> {
|
|
||||||
Some(String::from_iter(self.get_row(y)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CharGridMutExt: GridMut<char> {
|
|
||||||
/// Overwrites a row in the grid with a str.
|
|
||||||
///
|
|
||||||
/// Returns [`SetValueSeriesError`] if y is out of bounds or `row` is not of the correct size.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use servicepoint::{CharGrid, CharGridMutExt};
|
|
||||||
/// let mut grid = CharGrid::from("ab\ncd");
|
|
||||||
/// grid.set_row_str(0, "ef").unwrap();
|
|
||||||
/// ```
|
|
||||||
fn set_row_str(
|
|
||||||
&mut self,
|
|
||||||
y: usize,
|
|
||||||
value: &str,
|
|
||||||
) -> Result<(), SetValueSeriesError> {
|
|
||||||
let width = self.width();
|
|
||||||
|
|
||||||
let len = value.len();
|
|
||||||
if len > width {
|
|
||||||
return Err(SetValueSeriesError::InvalidLength {
|
|
||||||
actual: len,
|
|
||||||
expected: width,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let height = self.height();
|
|
||||||
if y >= height {
|
|
||||||
return Err(SetValueSeriesError::OutOfBounds {
|
|
||||||
index: y,
|
|
||||||
size: height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let chars = value.chars().take(width);
|
|
||||||
for (x, c) in chars.enumerate() {
|
|
||||||
self.set(x, y, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overwrites a column in the grid with a str.
|
|
||||||
///
|
|
||||||
/// Returns [`SetValueSeriesError`] if y is out of bounds or `row` is not of the correct size.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use servicepoint::CharGrid;
|
|
||||||
/// let mut grid = CharGrid::from("ab\ncd");
|
|
||||||
/// grid.set_col_str(0, "ef").unwrap();
|
|
||||||
/// ```
|
|
||||||
fn set_col_str(
|
|
||||||
&mut self,
|
|
||||||
x: usize,
|
|
||||||
value: &str,
|
|
||||||
) -> Result<(), SetValueSeriesError> {
|
|
||||||
let height = self.height();
|
|
||||||
|
|
||||||
let len = value.len();
|
|
||||||
if len > height {
|
|
||||||
return Err(SetValueSeriesError::InvalidLength {
|
|
||||||
actual: len,
|
|
||||||
expected: height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = self.width();
|
|
||||||
if x >= width {
|
|
||||||
return Err(SetValueSeriesError::OutOfBounds {
|
|
||||||
index: x,
|
|
||||||
size: width,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let chars = value.chars().take(height);
|
|
||||||
for (y, c) in chars.enumerate() {
|
|
||||||
self.set(x, y, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inherent::inherent]
|
|
||||||
impl CharGridMutExt for CharGrid {
|
|
||||||
pub fn set_col_str(
|
|
||||||
&mut self,
|
|
||||||
x: usize,
|
|
||||||
value: &str,
|
|
||||||
) -> Result<(), SetValueSeriesError>;
|
|
||||||
|
|
||||||
pub fn set_row_str(
|
|
||||||
&mut self,
|
|
||||||
y: usize,
|
|
||||||
value: &str,
|
|
||||||
) -> Result<(), SetValueSeriesError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum LoadUtf8Error {
|
|
||||||
#[error(transparent)]
|
|
||||||
FromUtf8Error(#[from] FromUtf8Error),
|
|
||||||
#[error(transparent)]
|
|
||||||
TryLoadError(#[from] TryLoadValueGridError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for CharGrid {
|
impl From<&str> for CharGrid {
|
||||||
|
|
@ -308,7 +129,7 @@ impl From<&CharGrid> for Vec<u8> {
|
||||||
/// let grid = CharGrid::from("ab\ncd");
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
/// let height = grid.height();
|
/// let height = grid.height();
|
||||||
/// let width = grid.width();
|
/// let width = grid.width();
|
||||||
/// let grid = CharGrid::load_utf8(width, height, grid.into());
|
/// let bytes = Vec::<u8>::from(grid);
|
||||||
/// ```
|
/// ```
|
||||||
fn from(value: &CharGrid) -> Self {
|
fn from(value: &CharGrid) -> Self {
|
||||||
value.iter().collect::<String>().into_bytes()
|
value.iter().collect::<String>().into_bytes()
|
||||||
|
|
@ -325,6 +146,8 @@ impl From<CharGrid> for Vec<u8> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::{CharGridExt, SetValueSeriesError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn col_str() {
|
fn col_str() {
|
||||||
let mut grid = CharGrid::new(2, 3);
|
let mut grid = CharGrid::new(2, 3);
|
||||||
|
|
@ -364,8 +187,15 @@ mod test {
|
||||||
fn round_trip_bytes() {
|
fn round_trip_bytes() {
|
||||||
let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0");
|
let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0");
|
||||||
let bytes: Vec<u8> = grid.clone().into();
|
let bytes: Vec<u8> = grid.clone().into();
|
||||||
let copy =
|
let copy = CharGrid::load(
|
||||||
CharGrid::load_utf8(grid.width(), grid.height(), bytes).unwrap();
|
grid.width(),
|
||||||
|
grid.height(),
|
||||||
|
&String::from_utf8(bytes)
|
||||||
|
.unwrap()
|
||||||
|
.chars()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(grid, copy);
|
assert_eq!(grid, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
145
src/containers/char_grid_ext.rs
Normal file
145
src/containers/char_grid_ext.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
use crate::{Grid, GridMut, SetValueSeriesError};
|
||||||
|
|
||||||
|
/// Extension methods for any [`Grid<char>`]
|
||||||
|
pub trait CharGridExt {
|
||||||
|
/// Copies a column from the grid as a String.
|
||||||
|
///
|
||||||
|
/// Returns [None] if x is out of bounds.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::{CharGrid, CharGridExt};
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let col = grid.get_col_str(0).unwrap(); // "ac"
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
fn get_col_str(&self, x: usize) -> Option<String>;
|
||||||
|
|
||||||
|
/// Copies a row from the grid as a String.
|
||||||
|
///
|
||||||
|
/// Returns [None] if y is out of bounds.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::{CharGrid, CharGridExt};
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let row = grid.get_row_str(0).unwrap(); // "ab"
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
fn get_row_str(&self, y: usize) -> Option<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extension methods for any [`GridMut<char>`].
|
||||||
|
pub trait CharGridMutExt {
|
||||||
|
/// Overwrites a row in the grid with a str.
|
||||||
|
///
|
||||||
|
/// Returns [`SetValueSeriesError`] if y is out of bounds or `row` is not of the correct size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::{CharGrid, CharGridMutExt};
|
||||||
|
/// let mut grid = CharGrid::from("ab\ncd");
|
||||||
|
/// grid.set_row_str(0, "ef").unwrap();
|
||||||
|
/// ```
|
||||||
|
fn set_row_str(
|
||||||
|
&mut self,
|
||||||
|
y: usize,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<(), SetValueSeriesError>;
|
||||||
|
|
||||||
|
/// Overwrites a column in the grid with a str.
|
||||||
|
///
|
||||||
|
/// Returns [`SetValueSeriesError`] if y is out of bounds or `row` is not of the correct size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::{CharGrid, CharGridMutExt};
|
||||||
|
/// let mut grid = CharGrid::from("ab\ncd");
|
||||||
|
/// grid.set_col_str(0, "ef").unwrap();
|
||||||
|
/// ```
|
||||||
|
fn set_col_str(
|
||||||
|
&mut self,
|
||||||
|
x: usize,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<(), SetValueSeriesError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: Grid<char>> CharGridExt for G {
|
||||||
|
#[must_use]
|
||||||
|
fn get_col_str(&self, x: usize) -> Option<String> {
|
||||||
|
Some(String::from_iter(self.get_col(x)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn get_row_str(&self, y: usize) -> Option<String> {
|
||||||
|
Some(String::from_iter(self.get_row(y)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<G: GridMut<char>> CharGridMutExt for G {
|
||||||
|
fn set_row_str(
|
||||||
|
&mut self,
|
||||||
|
y: usize,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<(), SetValueSeriesError> {
|
||||||
|
let width = self.width();
|
||||||
|
|
||||||
|
let len = value.len();
|
||||||
|
if len > width {
|
||||||
|
return Err(SetValueSeriesError::InvalidLength {
|
||||||
|
actual: len,
|
||||||
|
expected: width,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = self.height();
|
||||||
|
if y >= height {
|
||||||
|
return Err(SetValueSeriesError::OutOfBounds {
|
||||||
|
index: y,
|
||||||
|
size: height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let chars = value.chars().take(width);
|
||||||
|
for (x, c) in chars.enumerate() {
|
||||||
|
self.set(x, y, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_col_str(
|
||||||
|
&mut self,
|
||||||
|
x: usize,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<(), SetValueSeriesError> {
|
||||||
|
let height = self.height();
|
||||||
|
|
||||||
|
let len = value.len();
|
||||||
|
if len > height {
|
||||||
|
return Err(SetValueSeriesError::InvalidLength {
|
||||||
|
actual: len,
|
||||||
|
expected: height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = self.width();
|
||||||
|
if x >= width {
|
||||||
|
return Err(SetValueSeriesError::OutOfBounds {
|
||||||
|
index: x,
|
||||||
|
size: width,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let chars = value.chars().take(height);
|
||||||
|
for (y, c) in chars.enumerate() {
|
||||||
|
self.set(x, y, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,6 +53,40 @@ pub trait Grid<T> {
|
||||||
let height = self.height();
|
let height = self.height();
|
||||||
assert!(y < height, "cannot access index [{x}, {y}] because y is outside of bounds [0..{height})");
|
assert!(y < height, "cannot access index [{x}, {y}] because y is outside of bounds [0..{height})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies a row from the grid.
|
||||||
|
///
|
||||||
|
/// Returns [None] if y is out of bounds.
|
||||||
|
#[must_use]
|
||||||
|
fn get_row(&self, y: usize) -> Option<Vec<T>> {
|
||||||
|
if y >= self.height() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = self.width();
|
||||||
|
let mut row = Vec::with_capacity(width);
|
||||||
|
for x in 0..width {
|
||||||
|
row.push(self.get(x, y));
|
||||||
|
}
|
||||||
|
Some(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies a column from the grid.
|
||||||
|
///
|
||||||
|
/// Returns [None] if x is out of bounds.
|
||||||
|
#[must_use]
|
||||||
|
fn get_col(&self, x: usize) -> Option<Vec<T>> {
|
||||||
|
if x >= self.width() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let height = self.height();
|
||||||
|
let mut col = Vec::with_capacity(height);
|
||||||
|
for y in 0..height {
|
||||||
|
col.push(self.get(x, y));
|
||||||
|
}
|
||||||
|
Some(col)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A two-dimensional mutable grid of `T`
|
/// A two-dimensional mutable grid of `T`
|
||||||
|
|
@ -87,4 +121,33 @@ pub trait GridMut<T>: Grid<T> {
|
||||||
|
|
||||||
/// Sets all cells in the grid to the specified value
|
/// Sets all cells in the grid to the specified value
|
||||||
fn fill(&mut self, value: T);
|
fn fill(&mut self, value: T);
|
||||||
|
|
||||||
|
/// Fills the grid with the values from the provided grid.
|
||||||
|
///
|
||||||
|
/// The grids have to match in size exactly.
|
||||||
|
///
|
||||||
|
/// For 1D slices the equivalent would be `*slice = other_slice`.
|
||||||
|
fn deref_assign<O: Grid<T>>(&mut self, other: &O) {
|
||||||
|
let width = self.width();
|
||||||
|
let height = self.height();
|
||||||
|
assert_eq!(
|
||||||
|
width,
|
||||||
|
other.width(),
|
||||||
|
"Cannot assign grid of width {} to a window of width {}",
|
||||||
|
other.width(),
|
||||||
|
self.width()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
height,
|
||||||
|
other.height(),
|
||||||
|
"Cannot assign grid of height {} to a height of width {}",
|
||||||
|
other.height(),
|
||||||
|
self.height()
|
||||||
|
);
|
||||||
|
for y in 0..height {
|
||||||
|
for x in 0..width {
|
||||||
|
self.set(x, y, other.get(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ mod bitmap;
|
||||||
mod brightness_grid;
|
mod brightness_grid;
|
||||||
mod byte_grid;
|
mod byte_grid;
|
||||||
mod char_grid;
|
mod char_grid;
|
||||||
|
mod char_grid_ext;
|
||||||
mod cp437_grid;
|
mod cp437_grid;
|
||||||
mod data_ref;
|
mod data_ref;
|
||||||
mod grid;
|
mod grid;
|
||||||
|
|
@ -13,13 +14,13 @@ pub use bit_vec::{bitvec, DisplayBitVec};
|
||||||
pub use bitmap::{Bitmap, LoadBitmapError};
|
pub use bitmap::{Bitmap, LoadBitmapError};
|
||||||
pub use brightness_grid::BrightnessGrid;
|
pub use brightness_grid::BrightnessGrid;
|
||||||
pub use byte_grid::ByteGrid;
|
pub use byte_grid::ByteGrid;
|
||||||
pub use char_grid::{CharGrid, CharGridExt, CharGridMutExt, LoadUtf8Error};
|
pub use char_grid::CharGrid;
|
||||||
|
pub use char_grid_ext::{CharGridExt, CharGridMutExt};
|
||||||
pub use cp437_grid::{Cp437Grid, InvalidCharError};
|
pub use cp437_grid::{Cp437Grid, InvalidCharError};
|
||||||
pub use data_ref::DataRef;
|
pub use data_ref::DataRef;
|
||||||
pub use grid::{Grid, GridMut};
|
pub use grid::{Grid, GridMut};
|
||||||
pub use value_grid::{
|
pub use value_grid::{
|
||||||
EnumerateGrid, IterGridRows, SetValueSeriesError, TryLoadValueGridError,
|
SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid,
|
||||||
Value, ValueGrid,
|
|
||||||
};
|
};
|
||||||
pub use window::{Window, WindowMut};
|
pub use window::{Window, WindowMut};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ impl<T: Value> ValueGrid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all rows in [`ValueGrid`] top to bottom.
|
/// Iterate over all rows in [`ValueGrid`] top to bottom.
|
||||||
pub fn iter_rows(&self) -> IterGridRows<T> {
|
pub fn iter_rows(&self) -> impl Iterator<Item = Iter<T>> + use<'_, T> {
|
||||||
IterGridRows { grid: self, row: 0 }
|
IterGridRows { grid: self, row: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,28 +224,6 @@ impl<T: Value> ValueGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies a row from the grid.
|
|
||||||
///
|
|
||||||
/// Returns [None] if y is out of bounds.
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_row(&self, y: usize) -> Option<Vec<T>> {
|
|
||||||
self.data
|
|
||||||
.chunks_exact(self.width())
|
|
||||||
.nth(y)
|
|
||||||
.map(<[T]>::to_vec)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies a column from the grid.
|
|
||||||
///
|
|
||||||
/// Returns [None] if x is out of bounds.
|
|
||||||
#[must_use]
|
|
||||||
pub fn get_col(&self, x: usize) -> Option<Vec<T>> {
|
|
||||||
self.data
|
|
||||||
.chunks_exact(self.width())
|
|
||||||
.map(|row| row.get(x).copied())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overwrites a column in the grid.
|
/// Overwrites a column in the grid.
|
||||||
///
|
///
|
||||||
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
|
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
|
||||||
|
|
@ -432,9 +410,19 @@ impl<T: Value> From<&ValueGrid<T>> for Vec<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Value, G: Grid<T>> From<&G> for ValueGrid<T> {
|
||||||
|
fn from(grid: &G) -> Self {
|
||||||
|
let width = grid.width();
|
||||||
|
let height = grid.height();
|
||||||
|
let mut result = Self::new(width, height);
|
||||||
|
result.deref_assign(grid);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An iterator iver the rows in a [`ValueGrid`]
|
/// An iterator iver the rows in a [`ValueGrid`]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct IterGridRows<'t, T: Value> {
|
struct IterGridRows<'t, T: Value> {
|
||||||
grid: &'t ValueGrid<T>,
|
grid: &'t ValueGrid<T>,
|
||||||
row: usize,
|
row: usize,
|
||||||
}
|
}
|
||||||
|
|
@ -455,7 +443,7 @@ impl<'t, T: Value> Iterator for IterGridRows<'t, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EnumerateGrid<'t, T: Value> {
|
struct EnumerateGrid<'t, T: Value> {
|
||||||
grid: &'t ValueGrid<T>,
|
grid: &'t ValueGrid<T>,
|
||||||
row: usize,
|
row: usize,
|
||||||
column: usize,
|
column: usize,
|
||||||
|
|
@ -480,18 +468,6 @@ impl<T: Value> Iterator for EnumerateGrid<'_, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Value> From<&Window<'_, E, Self>> for ValueGrid<E> {
|
|
||||||
fn from(value: &Window<'_, 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{SetValueSeriesError, ValueGrid, *};
|
use crate::{SetValueSeriesError, ValueGrid, *};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
containers::{
|
containers::{absolute_bounds_to_abs_range, relative_bounds_to_abs_range},
|
||||||
absolute_bounds_to_abs_range,
|
|
||||||
char_grid::{CharGridExt, CharGridMutExt},
|
|
||||||
relative_bounds_to_abs_range,
|
|
||||||
},
|
|
||||||
Grid, GridMut,
|
Grid, GridMut,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -62,7 +58,7 @@ macro_rules! define_window {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn split_horizontal(
|
pub fn split_horizontal(
|
||||||
self,
|
&'t self,
|
||||||
left_width: usize,
|
left_width: usize,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
Window<'t, TElement, TGrid>,
|
Window<'t, TElement, TGrid>,
|
||||||
|
|
@ -75,14 +71,17 @@ macro_rules! define_window {
|
||||||
self.xs.start..middle_abs,
|
self.xs.start..middle_abs,
|
||||||
self.ys.clone(),
|
self.ys.clone(),
|
||||||
)?;
|
)?;
|
||||||
let right =
|
let right = Window::new(
|
||||||
Window::new(self.grid, middle_abs..self.xs.end, self.ys)?;
|
self.grid,
|
||||||
|
middle_abs..self.xs.end,
|
||||||
|
self.ys.clone(),
|
||||||
|
)?;
|
||||||
Some((left, right))
|
Some((left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn split_vertical(
|
pub fn split_vertical(
|
||||||
self,
|
&'t self,
|
||||||
top_height: usize,
|
top_height: usize,
|
||||||
) -> Option<(
|
) -> Option<(
|
||||||
Window<'t, TElement, TGrid>,
|
Window<'t, TElement, TGrid>,
|
||||||
|
|
@ -95,8 +94,11 @@ macro_rules! define_window {
|
||||||
self.xs.clone(),
|
self.xs.clone(),
|
||||||
self.ys.start..middle_abs,
|
self.ys.start..middle_abs,
|
||||||
)?;
|
)?;
|
||||||
let bottom =
|
let bottom = Window::new(
|
||||||
Window::new(self.grid, self.xs, middle_abs..self.ys.end)?;
|
self.grid,
|
||||||
|
self.xs.clone(),
|
||||||
|
middle_abs..self.ys.end,
|
||||||
|
)?;
|
||||||
Some((top, bottom))
|
Some((top, bottom))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,9 +125,6 @@ macro_rules! define_window {
|
||||||
self.ys.len()
|
self.ys.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inherent::inherent]
|
|
||||||
impl<TGrid: Grid<char>> CharGridExt for $name<'_, char, TGrid> {}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,9 +150,6 @@ impl<TElement: Copy, TGrid: GridMut<TElement>> GridMut<TElement>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inherent::inherent]
|
|
||||||
impl<TGrid: GridMut<char>> CharGridMutExt for WindowMut<'_, char, TGrid> {}
|
|
||||||
|
|
||||||
impl<TElement: Copy, TGrid: GridMut<TElement>> WindowMut<'_, TElement, TGrid> {
|
impl<TElement: Copy, TGrid: GridMut<TElement>> WindowMut<'_, TElement, TGrid> {
|
||||||
/// Creates a mutable window into the grid.
|
/// Creates a mutable window into the grid.
|
||||||
///
|
///
|
||||||
|
|
@ -168,40 +164,47 @@ impl<TElement: Copy, TGrid: GridMut<TElement>> WindowMut<'_, TElement, TGrid> {
|
||||||
WindowMut::new(self.grid, xs, ys)
|
WindowMut::new(self.grid, xs, ys)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deref_assign<O: Grid<TElement>>(&mut self, other: &O) {
|
|
||||||
let width = self.width();
|
|
||||||
let height = self.height();
|
|
||||||
assert_eq!(width, other.width(), "Cannot assign grid of width {} to a window of width {}", other.width(), self.width());
|
|
||||||
assert_eq!(height, other.height(), "Cannot assign grid of height {} to a height of width {}", other.height(), self.height());
|
|
||||||
for y in 0..height {
|
|
||||||
for x in 0..width {
|
|
||||||
self.set(x, y, other.get(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn split_horizontal_mut(
|
pub fn split_horizontal_mut<'t>(
|
||||||
self,
|
&'t mut self,
|
||||||
left_width: usize,
|
left_width: usize,
|
||||||
) -> Option<(Self, Self)> {
|
) -> Option<(
|
||||||
|
WindowMut<'t, TElement, TGrid>,
|
||||||
|
WindowMut<'t, TElement, TGrid>,
|
||||||
|
)> {
|
||||||
assert!(left_width <= self.width());
|
assert!(left_width <= self.width());
|
||||||
let (grid1, grid2) = unsafe { Self::duplicate_mutable_ref(self.grid) };
|
let (grid1, grid2): (&'t mut TGrid, &'t mut TGrid) =
|
||||||
|
unsafe { Self::duplicate_mutable_ref(self.grid) };
|
||||||
let middle_abs = self.xs.start + left_width;
|
let middle_abs = self.xs.start + left_width;
|
||||||
let left =
|
let left =
|
||||||
WindowMut::new(grid1, self.xs.start..middle_abs, self.ys.clone())?;
|
WindowMut::new(grid1, self.xs.start..middle_abs, self.ys.clone())?;
|
||||||
let right = WindowMut::new(grid2, middle_abs..self.xs.end, self.ys)?;
|
let right =
|
||||||
|
WindowMut::new(grid2, middle_abs..self.xs.end, self.ys.clone())?;
|
||||||
Some((left, right))
|
Some((left, right))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn split_vertical_mut(self, top_height: usize) -> Option<(Self, Self)> {
|
pub fn split_vertical_mut<'t>(
|
||||||
|
&'t mut self,
|
||||||
|
top_height: usize,
|
||||||
|
) -> Option<(
|
||||||
|
WindowMut<'t, TElement, TGrid>,
|
||||||
|
WindowMut<'t, TElement, TGrid>,
|
||||||
|
)> {
|
||||||
assert!(top_height <= self.height());
|
assert!(top_height <= self.height());
|
||||||
let (grid1, grid2) = unsafe { Self::duplicate_mutable_ref(self.grid) };
|
let (grid1, grid2): (&'t mut TGrid, &'t mut TGrid) =
|
||||||
|
unsafe { Self::duplicate_mutable_ref(self.grid) };
|
||||||
let middle_abs = self.ys.start + top_height;
|
let middle_abs = self.ys.start + top_height;
|
||||||
let top =
|
let top = WindowMut::<'t>::new(
|
||||||
WindowMut::new(grid1, self.xs.clone(), self.ys.start..middle_abs)?;
|
grid1,
|
||||||
let bottom = WindowMut::new(grid2, self.xs, middle_abs..self.ys.end)?;
|
self.xs.clone(),
|
||||||
|
self.ys.start..middle_abs,
|
||||||
|
)?;
|
||||||
|
let bottom = WindowMut::<'t>::new(
|
||||||
|
grid2,
|
||||||
|
self.xs.clone(),
|
||||||
|
middle_abs..self.ys.end,
|
||||||
|
)?;
|
||||||
Some((top, bottom))
|
Some((top, bottom))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +364,23 @@ mod tests {
|
||||||
let mut grid = ByteGrid::new(5, 5);
|
let mut grid = ByteGrid::new(5, 5);
|
||||||
grid.fill(1);
|
grid.fill(1);
|
||||||
|
|
||||||
assert_eq!((0..5).len(), 5);
|
let mut win = grid.window_mut(.., ..).unwrap();
|
||||||
//let mut w1 = grid
|
let (mut top, mut bottom) = win.split_vertical_mut(2).unwrap();
|
||||||
|
let (mut left, mut right) = bottom.split_horizontal_mut(2).unwrap();
|
||||||
|
|
||||||
|
top.fill(2);
|
||||||
|
left.fill(3);
|
||||||
|
right.fill(4);
|
||||||
|
|
||||||
|
let grid2 = ByteGrid::from(&win.window(1..4, 1..4).unwrap());
|
||||||
|
|
||||||
|
assert_eq!(grid2.data_ref(), &[2, 2, 2, 3, 4, 4, 3, 4, 4,]);
|
||||||
|
assert_eq!(
|
||||||
|
grid.data_ref(),
|
||||||
|
&[
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 4, 3, 3, 4, 4, 4, 3,
|
||||||
|
3, 4, 4, 4,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue