summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hitomezashi.rs75
-rw-r--r--src/main.rs78
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));
+}