use bevy::prelude::*; use bevy::render::texture::{Extent3d, TextureDimension, TextureFormat}; use mandelbrot::{Bounds, Mandelbrot, TEXTURE_HEIGHT, TEXTURE_WIDTH}; use std::time::Instant; fn main() { // Mandelbrot::escape_time_image(); App::build() .add_plugins(DefaultPlugins) .add_startup_system(startup.system()) .insert_resource(TextureOptions::default()) .add_system(mandelbrot_render_system.system()) .add_system(transform_mandelbrot_system.system()) .run(); } struct TextureOptions { zoom: f64, horizontal: f64, vertical: f64, step: f64, iteration_limit: u32, } impl Default for TextureOptions { fn default() -> Self { Self { zoom: 1.0, horizontal: 0.0, vertical: 0.0, step: 0.01, iteration_limit: 64, } } } fn transform_mandelbrot_system(input: Res>, mut scale: ResMut) { // Zoom: E to Zoom In, Q to Zoom Out // Horizontal Movement: D to move right, A to move left // Vertical Movement: W to move up, S to move Down // R to increase the steps taken by zoom, horizontal movement, and vertical movement // T to increase the Iteration Limit, G to decrease the iteration limit let zoom = input.pressed(KeyCode::Q) as i8 - input.pressed(KeyCode::E) as i8; let horizontal = input.pressed(KeyCode::D) as i8 - input.pressed(KeyCode::A) as i8; let vertical = input.pressed(KeyCode::W) as i8 - input.pressed(KeyCode::S) as i8; let step_change = input.pressed(KeyCode::R) as i8 - input.pressed(KeyCode::F) as i8; let limit_change = input.just_pressed(KeyCode::T) as i8 - input.just_pressed(KeyCode::G) as i8; scale.step += (scale.step / 10.0) * step_change as f64; scale.vertical += scale.step * vertical as f64; scale.horizontal += scale.step * horizontal as f64; scale.zoom += scale.step * zoom as f64; if limit_change == 1 { scale.iteration_limit *= 2; if scale.iteration_limit > 512 { scale.iteration_limit = 512; } } else if limit_change == -1 { scale.iteration_limit /= 2; if scale.iteration_limit < 32 { scale.iteration_limit = 32; } } if scale.zoom < 0.0 { // We can't go below 0 scale.zoom -= scale.step * zoom as f64; // slow down our step function scale.step /= 10.0; } } fn mandelbrot_render_system( materials: Res>, mut textures: ResMut>, scale: Res, mut query: Query<(&mut Mandelbrot, &Handle)>, ) { for (mut fractal, handle) in query.iter_mut() { if let Some(material) = materials.get(handle) { if let Some(texture_handle) = &material.texture { if let Some(texture) = textures.get_mut(texture_handle) { let z = scale.zoom; let h = scale.horizontal; let v = scale.vertical; let limit = scale.iteration_limit; let x = ((-2.5 * z) + h, (1.0 * z) + h); let y = ((-1.0 * z) - v, (1.0 * z) - v); let bounds = Bounds::new(x, y); let start = Instant::now(); texture .data .copy_from_slice(fractal.scaled_image(bounds, limit)); let _diff = Instant::now() - start; } } } } } fn startup( mut commands: Commands, mut textures: ResMut>, mut materials: ResMut>, ) { let mut fractal = Mandelbrot::default(); let texture_size = Extent3d::new(TEXTURE_WIDTH as u32, TEXTURE_HEIGHT as u32, 1); let texture_data = fractal.image().to_vec(); let texture_handle = textures.add(Texture::new( texture_size, TextureDimension::D2, texture_data, TextureFormat::Rgba8UnormSrgb, )); commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands .spawn_bundle(SpriteBundle { material: materials.add(texture_handle.into()), ..Default::default() }) .insert(fractal); }