use anyhow::{Context, Result}; use enkou_shaders::Coefficients2; use enkou_shaders::camera::Camera; use enkou_shaders::chaos_game::ChaosGame; use enkou_shaders::transform::Transform; use glam::{Affine2, Vec2, uvec2, UVec2}; use image::{GrayImage, Luma}; use rand::SeedableRng; use rand_xoshiro::Xoshiro256StarStar; use std::mem; use std::process::Command; use tempfile::{NamedTempFile}; const ITERATIONS: u32 = 50_000; const ITERATIONS_DISCARD: u32 = 20; const IMAGE_DIMENSION: UVec2 = uvec2(600, 600); pub fn main() -> Result<()> { let seed: u64 = 4; // chosen by fair dice roll let mut rng = Xoshiro256StarStar::seed_from_u64(seed); let transforms = [ // F_0: (x / 2, y / 2) Transform::new(Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.0)), // F_1: ((x + 1) / 2, y / 2) Transform::new(Affine2::from_coefficients(0.5, 0.0, 0.5, 0.0, 0.5, 0.0)), // F_2: (x / 2, (y + 1) / 2) Transform::new(Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.5)), ]; let weights = [1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]; let mut image = GrayImage::new(IMAGE_DIMENSION.x, IMAGE_DIMENSION.y); // The gasket is defined on the range [0, 1] for both X and Y let camera = Camera::new( IMAGE_DIMENSION, Vec2::ONE * 0.5, 0.0, Vec2::ZERO, IMAGE_DIMENSION.as_vec2(), ); let mut chaos_game = ChaosGame::new(&mut rng, &transforms, &weights); for i in 0..ITERATIONS { let next_point = chaos_game.next().unwrap(); if i < ITERATIONS_DISCARD { continue; } if let Some(next_point) = camera.transform_point_to_image(next_point) { image.put_pixel(next_point.x, next_point.y, Luma([255u8])) } } let temp = NamedTempFile::with_suffix(".png").context("Unable to create file for image")?; image.save(temp.path()).context("Unable to save image")?; let open_program = cfg_select! { unix => "xdg-open", _ => panic!("Unknown system"), }; Command::new(open_program) .arg(temp.path()) .spawn()? .wait()?; // In case the image viewer forks and gives control back prior to reading the file, // drop it and don't run the constructor mem::forget(temp); Ok(()) }