wfc_image 0.12.1

Create patterns for wfc using image files
Documentation
use animation_helper::WindowPixels;
use coord_2d::Coord;
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use std::num::NonZeroU32;
use std::thread;
use std::time::Duration;
use wfc::wrap::*;
use wfc::*;
use wfc_image::{ImagePatterns, Size};

struct Forbid {
    wrapped_top_left_corner_id: Option<PatternId>,
    bottom_left_corner_id: Option<PatternId>,
    pattern_size: u32,
}

impl ForbidPattern for Forbid {
    fn forbid<W: Wrap, R: Rng>(&mut self, fi: &mut ForbidInterface<W>, rng: &mut R) {
        let output_size = fi.wave_size();
        if let Some(bottom_left_corner_id) = self.bottom_left_corner_id {
            for i in 0..(output_size.width() as i32) {
                let coord = Coord::new(i, output_size.height() as i32 - 1);
                fi.forbid_all_patterns_except(coord, bottom_left_corner_id, rng)
                    .unwrap();
            }
        }
        if let Some(wrapped_top_left_corner_id) = self.wrapped_top_left_corner_id {
            for i in 0..(output_size.width() as i32) {
                let coord = Coord::new(
                    i,
                    output_size.height() as i32 - self.pattern_size as i32 + 1,
                );
                fi.forbid_all_patterns_except(coord, wrapped_top_left_corner_id, rng)
                    .unwrap();
            }
        }
    }
}

fn main() {
    let (
        seed_opt,
        input_path,
        forever,
        anchor_top,
        anchor_bottom,
        width,
        height,
        delay,
        pattern_size,
        all_orientations,
    ) = meap::all! {
        opt_opt("INT", 's').name("seed").desc("rng seed"),
        opt_req::<String, _>("PATH", 'i').name("input").desc("input path"),
        flag('f').name("forever").desc("repeat forever"),
        flag('t').name("anchor-top").desc("anchor top"),
        flag('b').name("anchor-bottom").desc("anchor bottom"),
        opt_opt::<u32, _>("INT", 'x').name("width").desc("width").with_default(48),
        opt_opt::<u32, _>("INT", 'y').name("height").desc("height").with_default(48),
        opt_opt::<u64, _>("MS", 'd').name("delay").desc("delay between steps"),
        opt_opt::<u32, _>("INT", 'p').name("pattern-size").desc("size of patterns in pixels").with_default(3),
        flag('a').name("all-orientations").desc("all orientations"),
    }
    .with_help_default()
    .parse_env_or_exit();
    let seed = seed_opt.unwrap_or_else(|| rand::thread_rng().gen());
    if (anchor_top || anchor_bottom) && all_orientations {
        eprintln!("Can't anchor with all orientations");
        ::std::process::exit(1);
    }
    println!("seed: {}", seed);
    let orientation: &[Orientation] = if all_orientations {
        &orientation::ALL
    } else {
        &[Orientation::Original]
    };
    let image = image::open(input_path).unwrap();
    let grid_size = Size::new(width, height);
    let pixel_size = Size::new(8, 8);
    let mut window_pixels = WindowPixels::new(grid_size, pixel_size);
    let mut image_patterns = ImagePatterns::new(
        &image,
        NonZeroU32::new(pattern_size).expect("pattern size may not be zero"),
        orientation,
    );
    let input_size = image_patterns.grid().size();
    let id_grid = image_patterns.id_grid_original_orientation();
    let bottom_left_corner_id = if anchor_bottom {
        let coord = Coord::new(0, input_size.y() as i32 - 1);
        let pattern_id = *id_grid.get_checked(coord);
        image_patterns.pattern_mut(pattern_id).clear_count();
        Some(pattern_id)
    } else {
        None
    };
    let wrapped_top_left_corner_id = if anchor_top {
        let coord = Coord::new(0, input_size.y() as i32 - pattern_size as i32 + 1);
        let pattern_id = *id_grid.get_checked(coord);
        image_patterns.pattern_mut(pattern_id).clear_count();
        Some(pattern_id)
    } else {
        None
    };
    let mut rng = XorShiftRng::seed_from_u64(seed);
    let global_stats = image_patterns.global_stats();
    let mut wave = Wave::new(grid_size);
    let mut context = Context::new();
    let delay = delay.map(Duration::from_millis);
    'generate: loop {
        let forbid = Forbid {
            bottom_left_corner_id,
            wrapped_top_left_corner_id,
            pattern_size,
        };
        let mut run = RunBorrow::new_forbid(
            &mut context,
            &mut wave,
            &global_stats,
            forbid,
            &mut rng,
        );
        'inner: loop {
            window_pixels.draw(run.wave_cell_ref_iter(), &image_patterns);
            if let Some(delay) = delay {
                thread::sleep(delay);
            }
            match run.step(&mut rng) {
                Ok(observe) => match observe {
                    Observe::Complete => {
                        if forever {
                            continue 'generate;
                        } else {
                            break 'generate;
                        }
                    }
                    Observe::Incomplete => (),
                },
                Err(PropagateError::Contradiction) => break 'inner,
            }
        }
    }
}