aboutsummaryrefslogtreecommitdiff
path: root/src/hitomezashi.rs
blob: a14296ad0143dd7ab745d683f66e5c370742b7f847972592629c9c683d1c00e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
    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::distributions::{Bernoulli, Distribution};

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<f64>) {
    let skew: f64 = skew.unwrap_or(0.5);

    let mut rng = rand::thread_rng();
    let brn = Bernoulli::new(skew).unwrap();

    // the first square (upper left corner) is always opaque
    let init_bit: bool = false;

    let mut row_bits: Vec<bool> = Vec::with_capacity(height - 1);

    for _ in 0..(height - 1) {
        row_bits.push(brn.sample(&mut rng));
    }

    let mut col_bits: Vec<bool> = Vec::with_capacity(width - 1);

    for _ in 0..(width - 1) {
        col_bits.push(brn.sample(&mut rng));
    }

    let mut alt_bits: Vec<bool> = Vec::with_capacity(width);

    for col in 0..width {
        alt_bits.push(col % 2 == 1);
    }

    // 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);

    for col in 0..(width - 1) {
        /* 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. */
        print_square(above_bits[col]);
        above_bits.push(above_bits[col] ^ col_bits[col]);
    }

    print_square(above_bits[width - 1]);
    println!();

    // height-1 because the first row has already been printed
    for row_bit in row_bits.iter().take(height - 1) {
        /* 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
            .iter_mut()
            .zip(alt_bits.iter())
            .for_each(|(x1, &x2)| {
                *x1 ^= x2 ^ row_bit;
                print_square(*x1);
            });

        println!();
    }
}