add ValueGrid::wrap and CharGrid::wrap_str, more examples

add examples
This commit is contained in:
Vinzenz Schroeter 2025-01-12 13:50:33 +01:00
parent 2a6005fff9
commit 04cda144ed
6 changed files with 160 additions and 26 deletions

View file

@ -40,20 +40,8 @@ fn main() {
.expect("sending clear failed"); .expect("sending clear failed");
} }
let text = cli let text = cli.text.join("\n");
.text let grid = CharGrid::wrap_str(TILE_WIDTH, &text);
.iter()
.flat_map(move |x| {
x.chars()
.collect::<Vec<_>>()
.chunks(TILE_WIDTH)
.map(|c| String::from_iter(c))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
.join("\n");
let grid = CharGrid::from(text);
connection connection
.send(Command::Utf8Data(Origin::ZERO, grid)) .send(Command::Utf8Data(Origin::ZERO, grid))
.expect("sending text failed"); .expect("sending text failed");

View file

@ -9,12 +9,13 @@ use ::bitvec::slice::IterMut;
/// ///
/// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels. /// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels.
/// This means that no conversion is necessary for sending the data to the display. /// This means that no conversion is necessary for sending the data to the display.
/// The downside is that the width has to be a multiple of 8.
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use servicepoint::Bitmap; /// use servicepoint::Bitmap;
/// let mut bitmap = Bitmap::new(4, 2); /// let mut bitmap = Bitmap::new(8, 2);
/// ///
/// ``` /// ```
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -192,12 +193,18 @@ impl From<Bitmap> for Vec<u8> {
} }
impl From<Bitmap> for BitVec { impl From<Bitmap> for BitVec {
/// Turns a [Bitmap] into the underlying [BitVec].
fn from(value: Bitmap) -> Self { fn from(value: Bitmap) -> Self {
value.bit_vec value.bit_vec
} }
} }
impl From<&ValueGrid<bool>> for Bitmap { impl From<&ValueGrid<bool>> for Bitmap {
/// Converts a grid of [bool]s into a [Bitmap].
///
/// # Panics
///
/// - when the width of `value` is not dividable by 8
fn from(value: &ValueGrid<bool>) -> Self { fn from(value: &ValueGrid<bool>) -> Self {
let mut result = Self::new(value.width(), value.height()); let mut result = Self::new(value.width(), value.height());
for (mut to, from) in result.iter_mut().zip(value.iter()) { for (mut to, from) in result.iter_mut().zip(value.iter()) {
@ -208,6 +215,7 @@ impl From<&ValueGrid<bool>> for Bitmap {
} }
impl From<&Bitmap> for ValueGrid<bool> { impl From<&Bitmap> for ValueGrid<bool> {
/// Converts a [Bitmap] into a grid of [bool]s.
fn from(value: &Bitmap) -> Self { fn from(value: &Bitmap) -> Self {
let mut result = Self::new(value.width(), value.height()); let mut result = Self::new(value.width(), value.height());
for (to, from) in result.iter_mut().zip(value.iter()) { for (to, from) in result.iter_mut().zip(value.iter()) {

View file

@ -3,9 +3,9 @@ use std::string::FromUtf8Error;
/// A grid containing UTF-8 characters. /// A grid containing UTF-8 characters.
/// ///
/// To send a CharGrid to the display, use [crate::Command::Utf8Data]. /// To send a CharGrid to the display, use [Command::Utf8Data](crate::Command::Utf8Data).
/// ///
/// Also see [crate::ValueGrid] for the non-specialized operations and examples. /// Also see [ValueGrid] for the non-specialized operations and examples.
/// ///
/// # Examples /// # Examples
/// ///
@ -20,9 +20,52 @@ use std::string::FromUtf8Error;
pub type CharGrid = ValueGrid<char>; pub type CharGrid = ValueGrid<char>;
impl CharGrid { impl CharGrid {
/// Loads a [CharGrid] with the specified width from the provided text, wrapping to as many rows as needed.
///
/// The passed rows are extended with '\0' if needed.
///
/// returns: [CharGrid] that contains a copy of the provided data.
///
/// # Examples
///
/// ```
/// # use servicepoint::CharGrid;
/// let grid = CharGrid::wrap_str(2, "abc\ndef");
/// ```
pub fn wrap_str(width: usize, text: &str) -> Self {
let lines = text
.split('\n')
.flat_map(move |x| {
x.chars()
.collect::<Vec<char>>()
.chunks(width)
.map(|c| {
let mut s = String::from_iter(c);
s.push_str(&"\0".repeat(width - s.chars().count()));
s
})
.collect::<Vec<String>>()
})
.collect::<Vec<String>>();
let height = lines.len();
let mut result = Self::new(width, height);
for (row, text_line) in lines.iter().enumerate() {
result.set_row_str(row, text_line).unwrap()
}
result
}
/// Copies a column from the grid as a String. /// Copies a column from the grid as a String.
/// ///
/// Returns [None] if x is out of bounds. /// 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"
/// ```
pub fn get_col_str(&self, x: usize) -> Option<String> { pub fn get_col_str(&self, x: usize) -> Option<String> {
Some(String::from_iter(self.get_col(x)?)) Some(String::from_iter(self.get_col(x)?))
} }
@ -30,6 +73,14 @@ impl CharGrid {
/// Copies a row from the grid as a String. /// Copies a row from the grid as a String.
/// ///
/// Returns [None] if y is out of bounds. /// 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"
/// ```
pub fn get_row_str(&self, y: usize) -> Option<String> { pub fn get_row_str(&self, y: usize) -> Option<String> {
Some(String::from_iter(self.get_row(y)?)) Some(String::from_iter(self.get_row(y)?))
} }
@ -37,6 +88,14 @@ impl CharGrid {
/// Overwrites a row in the grid with a str. /// 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. /// 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_row_str(0, "ef").unwrap();
/// ```
pub fn set_row_str( pub fn set_row_str(
&mut self, &mut self,
y: usize, y: usize,
@ -48,6 +107,14 @@ impl CharGrid {
/// Overwrites a column in the grid with a str. /// 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. /// 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();
/// ```
pub fn set_col_str( pub fn set_col_str(
&mut self, &mut self,
x: usize, x: usize,
@ -60,9 +127,12 @@ impl CharGrid {
/// ///
/// returns: [CharGrid] that contains the provided data, or [FromUtf8Error] if the data is invalid. /// returns: [CharGrid] that contains the provided data, or [FromUtf8Error] if the data is invalid.
/// ///
/// # Panics /// # Examples
/// ///
/// - when the dimensions and data size do not match exactly. /// ```
/// # use servicepoint::CharGrid;
/// let grid = CharGrid::load_utf8(2, 2, [97u8, 98, 99, 100].to_vec());
/// ```
pub fn load_utf8( pub fn load_utf8(
width: usize, width: usize,
height: usize, height: usize,
@ -117,6 +187,18 @@ impl From<CharGrid> for String {
} }
impl From<&CharGrid> for String { impl From<&CharGrid> for String {
/// Converts a [CharGrid] into a [String].
///
/// Rows are separated by '\n'.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::CharGrid;
/// let grid = CharGrid::from("ab\ncd");
/// let string = String::from(grid);
/// let grid = CharGrid::from(string);
/// ```
fn from(value: &CharGrid) -> Self { fn from(value: &CharGrid) -> Self {
value value
.iter_rows() .iter_rows()
@ -127,12 +209,26 @@ impl From<&CharGrid> for String {
} }
impl From<&CharGrid> for Vec<u8> { impl From<&CharGrid> for Vec<u8> {
/// Converts a [CharGrid] into a [`Vec<u8>`].
///
/// Rows are not separated.
///
/// # Examples
///
/// ```rust
/// # use servicepoint::{CharGrid, Grid};
/// let grid = CharGrid::from("ab\ncd");
/// let height = grid.height();
/// let width = grid.width();
/// let grid = CharGrid::load_utf8(width, height, grid.into());
/// ```
fn from(value: &CharGrid) -> Self { fn from(value: &CharGrid) -> Self {
String::from_iter(value.iter()).into_bytes() String::from_iter(value.iter()).into_bytes()
} }
} }
impl From<CharGrid> for Vec<u8> { impl From<CharGrid> for Vec<u8> {
/// See [`From<&CharGrid>::from`].
fn from(value: CharGrid) -> Self { fn from(value: CharGrid) -> Self {
Self::from(&value) Self::from(&value)
} }
@ -192,4 +288,11 @@ mod test {
let copy = CharGrid::from(str); let copy = CharGrid::from(str);
assert_eq!(grid, copy); assert_eq!(grid, copy);
} }
#[test]
fn wrap_str() {
let grid = CharGrid::wrap_str(2, "abc\ndef");
assert_eq!(4, grid.height());
assert_eq!("ab\nc\0\nde\nf\0", String::from(grid));
}
} }

View file

@ -90,6 +90,12 @@ mod feature_cp437 {
value.map(Cp437Converter::cp437_to_char) value.map(Cp437Converter::cp437_to_char)
} }
} }
impl From<Cp437Grid> for CharGrid {
fn from(value: Cp437Grid) -> Self {
Self::from(&value)
}
}
impl From<&CharGrid> for Cp437Grid { impl From<&CharGrid> for Cp437Grid {
fn from(value: &CharGrid) -> Self { fn from(value: &CharGrid) -> Self {
@ -99,7 +105,7 @@ mod feature_cp437 {
impl From<CharGrid> for Cp437Grid { impl From<CharGrid> for Cp437Grid {
fn from(value: CharGrid) -> Self { fn from(value: CharGrid) -> Self {
Cp437Grid::from(&value) Self::from(&value)
} }
} }
} }
@ -150,8 +156,8 @@ mod tests_feature_cp437 {
#[test] #[test]
fn round_trip_cp437() { fn round_trip_cp437() {
let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']); let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']);
let cp437 = Cp437Grid::from(&utf8); let cp437 = Cp437Grid::from(utf8.clone());
let actual = CharGrid::from(&cp437); let actual = CharGrid::from(cp437);
assert_eq!(actual, utf8); assert_eq!(actual, utf8);
} }
} }

View file

@ -79,6 +79,27 @@ impl<T: Value> ValueGrid<T> {
} }
} }
/// Loads a [ValueGrid] with the specified width from the provided data, wrapping to as many rows as needed.
///
/// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
///
/// # Examples
///
/// ```
/// # use servicepoint::ValueGrid;
/// let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap();
/// ```
pub fn wrap(
width: usize,
data: &[T],
) -> Result<Self, TryLoadValueGridError> {
let len = data.len();
if len % width != 0 {
return Err(TryLoadValueGridError::InvalidDimensions);
}
Ok(Self::load(width, len / width, data))
}
/// Loads a [ValueGrid] with the specified dimensions from the provided data. /// Loads a [ValueGrid] with the specified dimensions from the provided data.
/// ///
/// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError]. /// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
@ -277,7 +298,7 @@ impl<T: Value> ValueGrid<T> {
} }
/// Errors that can occur when loading a grid /// Errors that can occur when loading a grid
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error, PartialEq)]
pub enum TryLoadValueGridError { pub enum TryLoadValueGridError {
#[error("The provided dimensions do not match with the data size")] #[error("The provided dimensions do not match with the data size")]
/// The provided dimensions do not match with the data size /// The provided dimensions do not match with the data size
@ -537,4 +558,13 @@ mod tests {
}) })
); );
} }
#[test]
fn wrap() {
let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(grid.height(), 3);
let grid = ValueGrid::wrap(4, &[0, 1, 2, 3, 4, 5]);
assert_eq!(grid.err(), Some(TryLoadValueGridError::InvalidDimensions));
}
} }

View file

@ -2,7 +2,7 @@
#include "servicepoint.h" #include "servicepoint.h"
int main(void) { int main(void) {
SPConnection *connection = sp_connection_open("172.23.42.29:2342"); SPConnection *connection = sp_connection_open("localhost:2342");
if (connection == NULL) if (connection == NULL)
return 1; return 1;
@ -10,9 +10,8 @@ int main(void) {
sp_bitmap_fill(pixels, true); sp_bitmap_fill(pixels, true);
SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED); SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED);
while (sp_connection_send_command(connection, sp_command_clone(command))); sp_connection_send_command(connection, command);
sp_command_free(command);
sp_connection_free(connection); sp_connection_free(connection);
return 0; return 0;
} }