encode origin unit in type

This commit is contained in:
Vinzenz Schroeter 2024-06-23 13:38:46 +02:00
parent 672b5e0581
commit e0647bacd6
10 changed files with 120 additions and 76 deletions

View file

@ -44,6 +44,6 @@ fn main() {
} }
connection connection
.send(Command::Cp437Data(Origin(0, 0), chars)) .send(Command::Cp437Data(Origin::new(0, 0), chars))
.expect("sending text failed"); .expect("sending text failed");
} }

View file

@ -21,7 +21,7 @@ fn main() {
connection connection
.send(BitmapLinearWin( .send(BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
pixels, pixels,
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
)) ))
@ -33,6 +33,6 @@ fn main() {
} }
connection connection
.send(Command::CharBrightness(Origin(0, 0), brightnesses)) .send(Command::CharBrightness(Origin::new(0, 0), brightnesses))
.expect("send failed"); .expect("send failed");
} }

View file

@ -25,7 +25,7 @@ fn main() {
loop { loop {
connection connection
.send(Command::BitmapLinearWin( .send(Command::BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
field.clone(), field.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
)) ))

View file

@ -25,7 +25,7 @@ fn main() {
} }
connection connection
.send(Command::BitmapLinearWin( .send(Command::BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
pixels.clone(), pixels.clone(),
CompressionCode::Lzma, CompressionCode::Lzma,
)) ))

View file

@ -1,26 +1,12 @@
use bitvec::prelude::BitVec; use bitvec::prelude::BitVec;
use crate::command_code::CommandCode;
use crate::compression::{into_compressed, into_decompressed};
use crate::{ use crate::{
Brightness, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, command_code::CommandCode,
SpBitVec, TILE_SIZE, compression::{into_compressed, into_decompressed},
Brightness, ByteGrid, CompressionCode, Grid, Header, Origin, Packet,
PixelGrid, Pixels, SpBitVec, Tiles, TILE_SIZE,
}; };
/// An origin marks the top left position of a window sent to the display.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Origin(pub usize, pub usize);
impl std::ops::Add<Origin> for Origin {
type Output = Origin;
fn add(self, rhs: Origin) -> Self::Output {
let Origin(x1, y1) = self;
let Origin(x2, y2) = rhs;
Origin(x1 + x2, y1 + y2)
}
}
/// Type alias for documenting the meaning of the u16 in enum values /// Type alias for documenting the meaning of the u16 in enum values
pub type Offset = usize; pub type Offset = usize;
@ -32,24 +18,20 @@ pub enum Command {
/// Show text on the screen. /// Show text on the screen.
/// ///
/// The origin is in tiles.
///
/// <div class="warning"> /// <div class="warning">
/// The library does not currently convert between UTF-8 and CP-437. /// The library does not currently convert between UTF-8 and CP-437.
/// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now. /// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
/// </div> /// </div>
Cp437Data(Origin, ByteGrid), Cp437Data(Origin<Tiles>, ByteGrid),
/// Sets a window of pixels to the specified values /// Sets a window of pixels to the specified values
BitmapLinearWin(Origin, PixelGrid, CompressionCode), BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode),
/// Set the brightness of all tiles to the same value. /// Set the brightness of all tiles to the same value.
Brightness(Brightness), Brightness(Brightness),
/// Set the brightness of individual tiles in a rectangular area of the display. /// Set the brightness of individual tiles in a rectangular area of the display.
/// CharBrightness(Origin<Tiles>, ByteGrid),
/// The origin is in tiles.
CharBrightness(Origin, ByteGrid),
/// Set pixel data starting at the pixel offset on screen. /// Set pixel data starting at the pixel offset on screen.
/// ///
@ -116,11 +98,11 @@ impl From<Command> for Packet {
Command::BitmapLegacy => { Command::BitmapLegacy => {
Command::command_code_only(CommandCode::BitmapLegacy) Command::command_code_only(CommandCode::BitmapLegacy)
} }
Command::CharBrightness(Origin(x, y), grid) => Packet( Command::CharBrightness(origin, grid) => Packet(
Header( Header(
CommandCode::CharBrightness.into(), CommandCode::CharBrightness.into(),
x as u16, origin.x as u16,
y as u16, origin.y as u16,
grid.width() as u16, grid.width() as u16,
grid.height() as u16, grid.height() as u16,
), ),
@ -171,11 +153,11 @@ impl From<Command> for Packet {
bits.into(), bits.into(),
) )
} }
Command::Cp437Data(Origin(x, y), grid) => Packet( Command::Cp437Data(origin, grid) => Packet(
Header( Header(
CommandCode::Cp437Data.into(), CommandCode::Cp437Data.into(),
x as u16, origin.x as u16,
y as u16, origin.y as u16,
grid.width() as u16, grid.width() as u16,
grid.height() as u16, grid.height() as u16,
), ),
@ -187,15 +169,14 @@ impl From<Command> for Packet {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn bitmap_win_into_packet( fn bitmap_win_into_packet(
origin: Origin, origin: Origin<Pixels>,
pixels: PixelGrid, pixels: PixelGrid,
compression: CompressionCode, compression: CompressionCode,
) -> Packet { ) -> Packet {
let Origin(pixel_x, pixel_y) = origin; debug_assert_eq!(origin.x % 8, 0);
debug_assert_eq!(pixel_x % 8, 0);
debug_assert_eq!(pixels.width() % 8, 0); debug_assert_eq!(pixels.width() % 8, 0);
let tile_x = (pixel_x / TILE_SIZE) as u16; let tile_x = (origin.x / TILE_SIZE) as u16;
let tile_w = (pixels.width() / TILE_SIZE) as u16; let tile_w = (pixels.width() / TILE_SIZE) as u16;
let pixel_h = pixels.height() as u16; let pixel_h = pixels.height() as u16;
let payload = into_compressed(compression, pixels.into()); let payload = into_compressed(compression, pixels.into());
@ -214,7 +195,7 @@ fn bitmap_win_into_packet(
}; };
Packet( Packet(
Header(command.into(), tile_x, pixel_y as u16, tile_w, pixel_h), Header(command.into(), tile_x, origin.y as u16, tile_w, pixel_h),
payload, payload,
) )
} }
@ -289,14 +270,14 @@ impl TryFrom<Packet> for Command {
CommandCode::Cp437Data => { CommandCode::Cp437Data => {
let Packet(_, payload) = packet; let Packet(_, payload) = packet;
Ok(Command::Cp437Data( Ok(Command::Cp437Data(
Origin(a as usize, b as usize), Origin::new(a as usize, b as usize),
ByteGrid::load(c as usize, d as usize, &payload), ByteGrid::load(c as usize, d as usize, &payload),
)) ))
} }
CommandCode::CharBrightness => { CommandCode::CharBrightness => {
let Packet(_, payload) = packet; let Packet(_, payload) = packet;
Ok(Command::CharBrightness( Ok(Command::CharBrightness(
Origin(a as usize, b as usize), Origin::new(a as usize, b as usize),
ByteGrid::load(c as usize, d as usize, &payload), ByteGrid::load(c as usize, d as usize, &payload),
)) ))
} }
@ -362,7 +343,7 @@ impl Command {
}; };
Ok(Command::BitmapLinearWin( Ok(Command::BitmapLinearWin(
Origin(tiles_x as usize * TILE_SIZE, pixels_y as usize), Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize),
PixelGrid::load( PixelGrid::load(
tile_w as usize * TILE_SIZE, tile_w as usize * TILE_SIZE,
pixel_h as usize, pixel_h as usize,
@ -441,13 +422,10 @@ impl Command {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bitvec::prelude::BitVec;
use crate::command::TryFromPacketError;
use crate::command_code::CommandCode;
use crate::{ use crate::{
Brightness, ByteGrid, Command, CompressionCode, Header, Origin, Packet, bitvec::prelude::BitVec, command::TryFromPacketError,
PixelGrid, command_code::CommandCode, origin::Pixels, Brightness, ByteGrid,
Command, CompressionCode, Header, Origin, Packet, PixelGrid,
}; };
fn round_trip(original: Command) { fn round_trip(original: Command) {
@ -501,12 +479,15 @@ mod tests {
#[test] #[test]
fn round_trip_char_brightness() { fn round_trip_char_brightness() {
round_trip(Command::CharBrightness(Origin(5, 2), ByteGrid::new(7, 5))); round_trip(Command::CharBrightness(
Origin::new(5, 2),
ByteGrid::new(7, 5),
));
} }
#[test] #[test]
fn round_trip_cp437_data() { fn round_trip_cp437_data() {
round_trip(Command::Cp437Data(Origin(5, 2), ByteGrid::new(7, 5))); round_trip(Command::Cp437Data(Origin::new(5, 2), ByteGrid::new(7, 5)));
} }
#[test] #[test]
@ -533,7 +514,7 @@ mod tests {
compression, compression,
)); ));
round_trip(Command::BitmapLinearWin( round_trip(Command::BitmapLinearWin(
Origin(0, 0), Origin::new(0, 0),
PixelGrid::max_sized(), PixelGrid::max_sized(),
compression, compression,
)); ));
@ -619,7 +600,7 @@ mod tests {
fn error_decompression_failed_win() { fn error_decompression_failed_win() {
for compression in all_compressions().to_owned() { for compression in all_compressions().to_owned() {
let p: Packet = Command::BitmapLinearWin( let p: Packet = Command::BitmapLinearWin(
Origin(16, 8), Origin::new(16, 8),
PixelGrid::new(8, 8), PixelGrid::new(8, 8),
compression, compression,
) )
@ -743,6 +724,9 @@ mod tests {
#[test] #[test]
fn origin_add() { fn origin_add() {
assert_eq!(Origin(4, 2), Origin(1, 0) + Origin(3, 2)); assert_eq!(
Origin::<Pixels>::new(4, 2),
Origin::new(1, 0) + Origin::new(3, 2)
);
} }
} }

View file

@ -46,20 +46,27 @@ impl Connection {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// use servicepoint::{Command, CompressionCode, Grid, PixelGrid}; /// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
/// let connection = servicepoint::Connection::open("172.23.42.29:2342") /// # let connection = servicepoint::Connection::open("172.23.42.29:2342")
/// .expect("connection failed"); /// # .expect("connection failed");
/// ///
/// // turn off all pixels /// // turn off all pixels on display
/// connection.send(Command::Clear) /// connection.send(Command::Clear)
/// .expect("send failed"); /// .expect("send failed");
/// ///
/// // turn on all pixels /// // turn on all pixels in a grid
/// let mut pixels = PixelGrid::max_sized(); /// let mut pixels = PixelGrid::max_sized();
/// pixels.fill(true); /// pixels.fill(true);
/// ///
/// // send pixels to display /// // create command to send pixels
/// connection.send(Command::BitmapLinearWin(servicepoint::Origin(0, 0), pixels, CompressionCode::Uncompressed)) /// let command = Command::BitmapLinearWin(
/// servicepoint::Origin::new(0, 0),
/// pixels,
/// CompressionCode::Uncompressed
/// );
///
/// // send command to display
/// connection.send(command)
/// .expect("send failed"); /// .expect("send failed");
/// ``` /// ```
pub fn send( pub fn send(

View file

@ -7,11 +7,12 @@ use bitvec::prelude::{BitVec, Msb0};
pub use crate::brightness::Brightness; pub use crate::brightness::Brightness;
pub use crate::byte_grid::ByteGrid; pub use crate::byte_grid::ByteGrid;
pub use crate::command::{Command, Offset, Origin}; pub use crate::command::{Command, Offset};
pub use crate::compression_code::CompressionCode; pub use crate::compression_code::CompressionCode;
pub use crate::connection::Connection; pub use crate::connection::Connection;
pub use crate::data_ref::DataRef; pub use crate::data_ref::DataRef;
pub use crate::grid::Grid; pub use crate::grid::Grid;
pub use crate::origin::{Origin, Pixels, Tiles};
pub use crate::packet::{Header, Packet, Payload}; pub use crate::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid; pub use crate::pixel_grid::PixelGrid;
@ -26,6 +27,7 @@ mod compression_code;
mod connection; mod connection;
mod data_ref; mod data_ref;
mod grid; mod grid;
mod origin;
mod packet; mod packet;
mod pixel_grid; mod pixel_grid;

View file

@ -0,0 +1,48 @@
use std::marker::PhantomData;
/// An origin marks the top left position of a window sent to the display.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Origin<Unit: DisplayUnit> {
/// position in the width direction
pub x: usize,
/// position in the height direction
pub y: usize,
phantom_data: PhantomData<Unit>,
}
impl<Unit: DisplayUnit> Origin<Unit> {
/// Create a new `Origin` instance for the provided position.
pub fn new(x: usize, y: usize) -> Self {
Self {
x,
y,
phantom_data: PhantomData::default(),
}
}
}
impl<T: DisplayUnit> std::ops::Add<Origin<T>> for Origin<T> {
type Output = Origin<T>;
fn add(self, rhs: Origin<T>) -> Self::Output {
Origin {
x: self.x + rhs.x,
y: self.y + rhs.y,
phantom_data: PhantomData::default(),
}
}
}
pub trait DisplayUnit {}
/// Marks something to be measured in number of pixels.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Pixels();
/// Marks something to be measured in number of iles.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Tiles();
impl DisplayUnit for Pixels {}
impl DisplayUnit for Tiles {}

View file

@ -105,9 +105,9 @@ pub unsafe extern "C" fn sp_command_fade_out() -> *mut Command {
/// - the returned `Command` instance is freed in some way, either by using a consuming function or /// - the returned `Command` instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_command_dealloc`. /// by explicitly calling `sp_command_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_brightness( pub unsafe extern "C" fn sp_command_brightness(brightness: u8) -> *mut Command {
brightness: Brightness, let brightness =
) -> *mut Command { Brightness::try_from(brightness).expect("invalid brightness");
Box::into_raw(Box::new(Command::Brightness(brightness))) Box::into_raw(Box::new(Command::Brightness(brightness)))
} }
@ -129,7 +129,10 @@ pub unsafe extern "C" fn sp_command_char_brightness(
byte_grid: *mut ByteGrid, byte_grid: *mut ByteGrid,
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid); let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::CharBrightness(Origin(x, y), byte_grid))) Box::into_raw(Box::new(Command::CharBrightness(
Origin::new(x, y),
byte_grid,
)))
} }
/// Allocates a new `Command::BitmapLinear` instance. /// Allocates a new `Command::BitmapLinear` instance.
@ -254,7 +257,7 @@ pub unsafe extern "C" fn sp_command_cp437_data(
byte_grid: *mut ByteGrid, byte_grid: *mut ByteGrid,
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(byte_grid); let byte_grid = *Box::from_raw(byte_grid);
Box::into_raw(Box::new(Command::Cp437Data(Origin(x, y), byte_grid))) Box::into_raw(Box::new(Command::Cp437Data(Origin::new(x, y), byte_grid)))
} }
/// Allocates a new `Command::BitmapLinearWin` instance. /// Allocates a new `Command::BitmapLinearWin` instance.
@ -278,7 +281,7 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win(
) -> *mut Command { ) -> *mut Command {
let byte_grid = *Box::from_raw(pixel_grid); let byte_grid = *Box::from_raw(pixel_grid);
Box::into_raw(Box::new(Command::BitmapLinearWin( Box::into_raw(Box::new(Command::BitmapLinearWin(
Origin(x, y), Origin::new(x, y),
byte_grid, byte_grid,
compression_code, compression_code,
))) )))

View file

@ -121,7 +121,7 @@ namespace ServicePoint.BindGen
[DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_fade_out", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_fade_out(); public static extern Command* sp_command_fade_out();
/// <summary>Allocates a new `Command::Brightness` instance. # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary> /// <summary>Allocates a new `Command::Brightness` instance for setting the brightness of all tiles to the same value. # Panics - When the provided brightness value is out of range (0-11). # Safety The caller has to make sure that: - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
[DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] [DllImport(__DllName, EntryPoint = "sp_command_brightness", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Command* sp_command_brightness(byte brightness); public static extern Command* sp_command_brightness(byte brightness);
@ -262,17 +262,17 @@ namespace ServicePoint.BindGen
public enum Command public enum Command
{ {
Clear, Clear,
HardReset, Cp437Data,
FadeOut, BitmapLinearWin,
CharBrightness,
Brightness, Brightness,
BitmapLegacy, CharBrightness,
BitmapLinear, BitmapLinear,
BitmapLinearAnd, BitmapLinearAnd,
BitmapLinearOr, BitmapLinearOr,
BitmapLinearXor, BitmapLinearXor,
Cp437Data, HardReset,
BitmapLinearWin, FadeOut,
BitmapLegacy,
} }
public enum CompressionCode : ushort public enum CompressionCode : ushort