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/lib.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/lib.rs (limited to 'src/lib.rs') 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 + } +} -- cgit v1.2.3