use anyhow::{Context, Result}; use enkou_shaders::Coefficients2; use enkou_shaders::camera::Camera; use enkou_shaders::camera::entry::main_camera; use enkou_shaders::chaos_game::entry::main_chaos_game; use enkou_shaders::transform::Transform; use enkou_shaders::variation::Variation; use glam::{Affine2, IVec2, UVec2, Vec2, uvec2}; use image::{GrayImage, Luma}; use std::mem; use std::process::Command; use tempfile::NamedTempFile; const ITERATIONS_DISCARD: u32 = 20; const ITERATIONS: u32 = 50_000; const IMAGE_DIMENSION: UVec2 = uvec2(600, 600); pub fn main() -> Result<()> { 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), uvec2(0, 1), ), // F_1: ((x + 1) / 2, y / 2) Transform::new( Affine2::from_coefficients(0.5, 0.0, 0.5, 0.0, 0.5, 0.0), uvec2(0, 1), ), // F_2: (x / 2, (y + 1) / 2) Transform::new( Affine2::from_coefficients(0.5, 0.0, 0.0, 0.0, 0.5, 0.5), uvec2(0, 1), ), ]; let weights = [1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]; let variations = [Variation::IDENTITY]; let mut output_points_ifs = Vec::new(); output_points_ifs.resize(ITERATIONS as usize, Vec2::ZERO); main_chaos_game( ITERATIONS_DISCARD, &[4u8], &transforms, &weights, &variations, &mut output_points_ifs, ); // 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 output_points_pixel = Vec::new(); output_points_pixel.resize(ITERATIONS as usize, IVec2::ZERO); main_camera(&camera, &output_points_ifs, &mut output_points_pixel); let mut image = GrayImage::new(IMAGE_DIMENSION.x, IMAGE_DIMENSION.y); let dimensions = image.dimensions(); output_points_pixel .iter() .skip_while(|p| { p.x < 0 || (p.x as u32) > dimensions.0 || p.y < 0 || (p.y as u32) > dimensions.1 }) .map(|p| (p.x as u32, p.y as u32)) .for_each(|(x, y)| image.put_pixel(x, 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 destructor mem::forget(temp); Ok(()) }