summaryrefslogtreecommitdiff
path: root/libhitomezashi
diff options
context:
space:
mode:
Diffstat (limited to 'libhitomezashi')
-rw-r--r--libhitomezashi/.gitignore2
-rw-r--r--libhitomezashi/Cargo.toml9
-rw-r--r--libhitomezashi/src/lib.rs63
-rw-r--r--libhitomezashi/src/pixel_buffer.rs93
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");
+ }
+}