diff options
Diffstat (limited to 'libhitomezashi')
-rw-r--r-- | libhitomezashi/.gitignore | 2 | ||||
-rw-r--r-- | libhitomezashi/Cargo.toml | 9 | ||||
-rw-r--r-- | libhitomezashi/src/lib.rs | 63 | ||||
-rw-r--r-- | libhitomezashi/src/pixel_buffer.rs | 93 |
4 files changed, 167 insertions, 0 deletions
diff --git a/libhitomezashi/.gitignore b/libhitomezashi/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/libhitomezashi/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/libhitomezashi/Cargo.toml b/libhitomezashi/Cargo.toml new file mode 100644 index 0000000..48e2935 --- /dev/null +++ b/libhitomezashi/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "libhitomezashi" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitvec = "1.0.1" diff --git a/libhitomezashi/src/lib.rs b/libhitomezashi/src/lib.rs new file mode 100644 index 0000000..b07057a --- /dev/null +++ b/libhitomezashi/src/lib.rs @@ -0,0 +1,63 @@ +mod pixel_buffer; +pub use bitvec::vec::BitVec; +use pixel_buffer::PixelBuffer; + +pub fn get_hitomezashi_pattern( + x_pattern: BitVec, + y_pattern: BitVec, + gap: usize, + line_thickness: usize, +) -> PixelBuffer { + let mut buf = PixelBuffer::new(x_pattern.len() * gap, y_pattern.len() * gap); + // Draw y pattern (horizontal) lines + for i in 0..y_pattern.len() { + for j in 0..x_pattern.len() { + if (j % 2 != 0) == y_pattern[i] { + let x = j * gap; + let y = i * gap; + let width = gap; + let height = line_thickness; + buf.fill_rect(x, y, width, height); + } + } + } + // Draw x pattern (vertical) lines + for i in 0..x_pattern.len() { + for j in 0..y_pattern.len() { + if (j % 2 != 0) == x_pattern[i] { + let x = i * gap; + let y = j * gap; + let width = line_thickness; + let height = gap; + buf.fill_rect(x, y, width, height) + } + } + } + + // Join up the lines to avoid leaving holes at the intersections + // The best way to understand how this works is to try commenting it out + for x in (gap..buf.width).step_by(gap) { + for y in (gap..buf.height).step_by(gap) { + let width = line_thickness; + let height = line_thickness; + buf.fill_rect(x, y, width, height); + } + } + buf +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + let mut x_pattern = BitVec::new(); + x_pattern.resize(40, false); + x_pattern.set(1, true); + let mut y_pattern = BitVec::new(); + y_pattern.resize(40, false); + y_pattern.set(1, true); + println!("{}", get_hitomezashi_pattern(x_pattern, y_pattern, 3, 1)); + } +} diff --git a/libhitomezashi/src/pixel_buffer.rs b/libhitomezashi/src/pixel_buffer.rs new file mode 100644 index 0000000..b151906 --- /dev/null +++ b/libhitomezashi/src/pixel_buffer.rs @@ -0,0 +1,93 @@ +use bitvec::prelude::*; +use std::fmt; +use std::ops::{Index, IndexMut}; + +/// 2D buffer of monochrome (white/black i.e. true/false) pixels +pub struct PixelBuffer { + buffer: BitVec, + pub width: usize, + pub height: usize, +} + +impl PixelBuffer { + pub fn new(width: usize, height: usize) -> Self { + let mut buffer = BitVec::with_capacity(width * height); + buffer.resize(width * height, false); + PixelBuffer { + buffer, + width, + height, + } + } + pub fn fill_rect(&mut self, x: usize, y: usize, width: usize, height: usize) { + for i in x..(x + width) { + for j in y..(y + height) { + self[i].set(j, true); + } + } + } +} + +impl Index<usize> for PixelBuffer { + type Output = BitSlice; + + fn index(&self, column: usize) -> &Self::Output { + &self.buffer[(column * self.height)..((column + 1) * self.height)] + } +} + +impl IndexMut<usize> for PixelBuffer { + fn index_mut(&mut self, column: usize) -> &mut Self::Output { + &mut self.buffer[(column * self.height)..((column + 1) * self.height)] + } +} + +impl fmt::Display for PixelBuffer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut result = String::new(); + for y in 0..self.height { + for x in 0..self.width { + result.push(if self[x][y] { '1' } else { '0' }) + } + result.push('\n') + } + write!(f, "{}", result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn access_default() { + let pixel_buffer = PixelBuffer::new(2, 3); + assert!(pixel_buffer[0][0] == false); + } + #[test] + fn mutate() { + let mut pixel_buffer = PixelBuffer::new(2, 3); + pixel_buffer[1].set(2, true); + assert!(pixel_buffer[1][2] == true); + } + #[test] + #[should_panic] + fn access_out_of_bounds() { + let pixel_buffer = PixelBuffer::new(10, 20); + assert!(pixel_buffer[10][0] == false); + } + #[test] + fn print() { + let mut pixel_buffer = PixelBuffer::new(3, 3); + pixel_buffer[1].set(1, true); + let result = format!("{}", pixel_buffer); + assert!(result == "000\n010\n000\n"); + } + #[test] + fn rect() { + let mut pixel_buffer = PixelBuffer::new(5, 3); + pixel_buffer.fill_rect(1, 0, 2, 2); + let result = format!("{}", pixel_buffer); + assert!(result == "01100\n01100\n00000\n"); + } +} |