From 4e042126bd9f250fc84a00fa34de90cc48d15ccf9715a72ef465669313331252 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Sun, 21 Apr 2024 00:00:00 +0000 Subject: Make hitomezashi_rs a proper library crate --- src/hitomezashi.rs | 102 ----------------------------------------------------- src/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 4 +-- 3 files changed, 104 insertions(+), 104 deletions(-) delete mode 100644 src/hitomezashi.rs create mode 100644 src/lib.rs (limited to 'src') diff --git a/src/hitomezashi.rs b/src/hitomezashi.rs deleted file mode 100644 index 8e2a773..0000000 --- a/src/hitomezashi.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - hitomezashi-rs Generates classical colored Hitomezashi stitch patterns - Copyright (C) 2024 Nicholas Johnson - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -use rand::distributions::{Bernoulli, Distribution}; - -const TRANSPARENT_SQUARE: char = ' '; -const OPAQUE_SQUARE: char = '█'; - -fn print_square(is_opaque: bool) { - print!( - "{}", - if is_opaque { - OPAQUE_SQUARE - } else { - TRANSPARENT_SQUARE - } - ); -} - -pub fn hitomezashi(width: usize, height: usize, skew: Option) { - // skew=0.5 generates the most random-looking patterns - let skew: f64 = skew.unwrap_or(0.5); - - assert!(width >= 1, "Width must be a positive number!"); - assert!(height >= 1, "Height must be a positive number!"); - assert!(skew >= 0.0 && skew <= 1.0, "Skew must be between zero and one inclusive!"); - - let mut rng = rand::thread_rng(); - let brn = Bernoulli::new(skew).unwrap(); - - // Each value represents whether to start that row with a stitch or no stitch - let mut row_bits: Vec = Vec::with_capacity(height - 1); - - for _ in 0..row_bits.capacity() { - row_bits.push(brn.sample(&mut rng)); - } - - // Each value represents whether to start that column with a stitch or no stitch - let mut col_bits: Vec = Vec::with_capacity(width - 1); - - for _ in 0..col_bits.capacity() { - col_bits.push(brn.sample(&mut rng)); - } - - // Precomputed values to facilitate alternating between stitch presence and stitch absence - let mut alt_bits: Vec = Vec::with_capacity(width); - - let mut alternator: bool = false; - for _ in 0..alt_bits.capacity() { - alt_bits.push(alternator); - alternator = !alternator; - } - - /* The first square is always opaque to prevent invisible 1xN or Nx1 patterns. Invisible - * patterns must be prevented so that users don't mistakenly conclude that the function does - * not work. */ - let first_square: bool = true; - - let mut cur_row: Vec = Vec::with_capacity(width); - - /* Base case - * - * The first row has no preceding row, so it's computing using the column stitches */ - cur_row.push(first_square); - for col in 0..(cur_row.capacity() - 1) { - print_square(cur_row[col]); - cur_row.push(cur_row[col] ^ col_bits[col]); - } - - print_square(cur_row[cur_row.capacity() - 1]); - println!(); // End of base case - - for row_bit in row_bits.iter() { - /* Recursive case - * - * Each row after the first row is computed based on the preceding row. */ - cur_row - .iter_mut() - .zip(alt_bits.iter()) - .for_each(|(x1, &x2)| { - *x1 ^= x2 ^ row_bit; - print_square(*x1); - }); - - println!(); // End of recursive case - } -} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9f8643e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,102 @@ +/* + hitomezashi-rs Generates classical colored Hitomezashi stitch patterns + Copyright (C) 2024 Nicholas Johnson + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +use rand::distributions::{Bernoulli, Distribution}; + +const TRANSPARENT_SQUARE: char = ' '; +const OPAQUE_SQUARE: char = '█'; + +fn print_square(is_opaque: bool) { + print!( + "{}", + if is_opaque { + OPAQUE_SQUARE + } else { + TRANSPARENT_SQUARE + } + ); +} + +pub fn generate(width: usize, height: usize, skew: Option) { + // skew=0.5 generates the most random-looking patterns + let skew: f64 = skew.unwrap_or(0.5); + + assert!(width >= 1, "Width must be a positive number!"); + assert!(height >= 1, "Height must be a positive number!"); + assert!(skew >= 0.0 && skew <= 1.0, "Skew must be between zero and one inclusive!"); + + let mut rng = rand::thread_rng(); + let brn = Bernoulli::new(skew).unwrap(); + + // Each value represents whether to start that row with a stitch or no stitch + let mut row_bits: Vec = Vec::with_capacity(height - 1); + + for _ in 0..row_bits.capacity() { + row_bits.push(brn.sample(&mut rng)); + } + + // Each value represents whether to start that column with a stitch or no stitch + let mut col_bits: Vec = Vec::with_capacity(width - 1); + + for _ in 0..col_bits.capacity() { + col_bits.push(brn.sample(&mut rng)); + } + + // Precomputed values to facilitate alternating between stitch presence and stitch absence + let mut alt_bits: Vec = Vec::with_capacity(width); + + let mut alternator: bool = false; + for _ in 0..alt_bits.capacity() { + alt_bits.push(alternator); + alternator = !alternator; + } + + /* The first square is always opaque to prevent invisible 1xN or Nx1 patterns. Invisible + * patterns must be prevented so that users don't mistakenly conclude that the function does + * not work. */ + let first_square: bool = true; + + let mut cur_row: Vec = Vec::with_capacity(width); + + /* Base case + * + * The first row has no preceding row, so it's computing using the column stitches */ + cur_row.push(first_square); + for col in 0..(cur_row.capacity() - 1) { + print_square(cur_row[col]); + cur_row.push(cur_row[col] ^ col_bits[col]); + } + + print_square(cur_row[cur_row.capacity() - 1]); + println!(); // End of base case + + for row_bit in row_bits.iter() { + /* Recursive case + * + * Each row after the first row is computed based on the preceding row. */ + cur_row + .iter_mut() + .zip(alt_bits.iter()) + .for_each(|(x1, &x2)| { + *x1 ^= x2 ^ row_bit; + print_square(*x1); + }); + + println!(); // End of recursive case + } +} diff --git a/src/main.rs b/src/main.rs index 0aba5f0..c936dc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use clap::Parser; use std::ops::RangeInclusive; -mod hitomezashi; +use hitomezashi_rs; #[derive(Parser)] #[command(version, about, long_about = None)] // Read from `Cargo.toml` @@ -43,7 +43,7 @@ struct Cli { fn main() { let cli = Cli::parse(); - hitomezashi::hitomezashi(cli.width, cli.height, Some(cli.skew)); + hitomezashi_rs::generate(cli.width, cli.height, Some(cli.skew)); } const DIMENSION_RANGE: RangeInclusive = 1..=std::usize::MAX; -- cgit v1.2.3