diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/hitomezashi.rs | 75 | ||||
-rw-r--r-- | src/main.rs | 78 |
2 files changed, 153 insertions, 0 deletions
diff --git a/src/hitomezashi.rs b/src/hitomezashi.rs new file mode 100644 index 0000000..50d3d32 --- /dev/null +++ b/src/hitomezashi.rs @@ -0,0 +1,75 @@ +/* + 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 <https://www.gnu.org/licenses/>. +*/ + +use rand::Rng; + +const TRANSPARENT_SQUARE: char = ' '; +const OPAQUE_SQUARE: char = '█'; + +fn print_square(is_transparent: bool) { + print!("{}", if is_transparent { TRANSPARENT_SQUARE } else { OPAQUE_SQUARE }); +} + +pub fn hitomezashi(width: usize, height: usize, skew: Option<f32>) { + let skew: f64 = match skew { + Some(n) => { n.into() } + None => { 0.5 } + }; + + let mut rng = rand::thread_rng(); + + let init_bit: bool = rng.gen::<bool>(); + let mut row_bits: Vec<bool> = Vec::with_capacity(height - 1); + let mut col_bits: Vec<bool> = Vec::with_capacity(width - 1); + + for _ in 0..(height - 1) { + row_bits.push(rng.gen_bool(skew)); + } + + for _ in 0..(width - 1) { + col_bits.push(rng.gen_bool(skew)); + } + + // each new row of the pattern depends on the bits directly above it + let mut above_bits: Vec<bool> = Vec::with_capacity(width); + + above_bits.push(init_bit); + print_square(above_bits[0]); + + for col in 1..width { + /* each square in the first row is derived from the square to its left. the column bits + * represent whether there's a stitch between the two squares. if there's a stitch, the squares + * are different, otherwise they are the same. */ + above_bits.push(above_bits[col - 1] ^ col_bits[col - 1]); + print_square(above_bits[col]); + } + + println!(); + + // height-1 because the first row has already been printed + for row in 0..(height - 1) { + for col in 0..width { + /* each square in each successive row is derived from the square above it. the row bits + * represent whether there's a stitch between the two squares. if there's a stitch, the + * squares are different, otherwise they are the same. */ + above_bits[col] = above_bits[col] ^ (row_bits[row] ^ (col % 2 == 1)); + print_square(above_bits[col]); + } + println!(); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6a44530 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,78 @@ +/* + 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 <https://www.gnu.org/licenses/>. +*/ + +use clap::{Parser, value_parser}; +use colored::Colorize; +use std::process::exit; + +mod hitomezashi; + +#[derive(Parser)] +#[command(version, about, long_about = None)] // Read from `Cargo.toml` +struct Cli { + /// Pattern width + #[arg(value_parser = value_parser!(usize))] + width: usize, + + /// Pattern height + #[arg(value_parser = value_parser!(usize))] + height: usize, + + #[arg(short, long)] + #[arg(default_value_t = 0.5)] + #[arg(value_parser = value_parser!(f32))] + /// Set skew + skew: f32, +} + +/* CLI for hitomezashi */ +fn main() { + let cli = Cli::parse(); + + // validate bounds of width [1..] + if cli.width < 1 { + println!("{} invalid value '{}' for '{}': valid values are [1..]\n\nFor more information, try '{}'.", + "error:".red().bold(), + cli.width.to_string().yellow(), + "<WIDTH>".bold(), + "--help".bold()); + exit(2); + } + + // validate bounds of height [1..] + if cli.height < 1 { + println!("{} invalid value '{}' for '{}': valid values are [1..]\n\nFor more information, try '{}'.", + "error:".red().bold(), + cli.height.to_string().yellow(), + "<HEIGHT>".bold(), + "--help".bold()); + exit(2); + } + + // validate bounds of skew [0-1] + if cli.skew < 0.0 || cli.skew > 1.0 { + println!("{} invalid value '{}' for '{}': valid values are [0-1]\n\nFor more information, try '{}'.", + "error:".red().bold(), + cli.skew.to_string().yellow(), + "--skew <SKEW>".bold(), + "--help".bold()); + exit(2); + } + + hitomezashi::hitomezashi(cli.width, cli.height, Some(cli.skew)); +} |