diff --git a/src/generate.rs b/src/generate.rs index 537d91a..d20ee2e 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1,3 +1,4 @@ +use eframe::epaint::Vec2; use image::{Rgb, RgbImage}; use serde::{Deserialize, Serialize}; @@ -32,35 +33,54 @@ impl Default for RenderOptions { } } -pub fn render(q: &RenderOptions, color: (u8, u8, u8)) -> RgbImage { - let mut img = RgbImage::new(q.width, q.height); +pub fn view_point(q: &RenderOptions, image: RgbImage) -> RgbImage { + apply_fn(image, q, |x, y| { + let len = (Vec2::new(x as f32, y as f32) - Vec2::new(q.cx as f32, q.cy as f32)).length(); + if len < 0.04 { + Some(Rgb([0, 255, 255])) + } else if len < 0.05 { + Some(Rgb([255; 3])) + } else { + None + } + }) +} - let width: f64 = q.width.into(); - let height: f64 = q.height.into(); +pub fn render(q: &RenderOptions, color: (u8, u8, u8)) -> RgbImage { + let img = RgbImage::new(q.width, q.height); + apply_fn(img, q, |x, y| { + let i = julia(x, y, q.cx, q.cy, q.max_iterations); + if q.fill_style == FillStyle::Black && i == q.max_iterations { + None + } else { + let i = i.min(255) as u8; + Some(Rgb([ + i.saturating_mul(color.0), + i.saturating_mul(color.1), + i.saturating_mul(color.2), + ])) + } + }) +} + +fn apply_fn(mut image: RgbImage, q: &RenderOptions, op: F) -> RgbImage +where + F: Fn(f64, f64) -> Option>, +{ + let width = q.width as f64; + let height = q.height as f64; let ppu = width / q.unit_width; for y in 0..q.height { for x in 0..q.width { - let pixel = { - let x = (f64::from(x) - width / 2.0) / ppu; - let y = (f64::from(y) - height / 2.0) / ppu; - - let iter = julia(x, y, q.cx, q.cy, q.max_iterations); - if q.fill_style == FillStyle::Black && iter == q.max_iterations { - Rgb([0, 0, 0]) - } else { - let i = iter.min(255) as u8; - Rgb([ - i.saturating_mul(color.0), - i.saturating_mul(color.1), - i.saturating_mul(color.2), - ]) - } - }; - img.put_pixel(x, y, pixel); + let sx = (x as f64 - width / 2.0) / ppu; + let sy = (y as f64 - height / 2.0) / ppu; + if let Some(pixel) = op(sx, sy) { + image.put_pixel(x, y, pixel); + } } } - img + image } fn julia(mut x: f64, mut y: f64, cx: f64, cy: f64, max_iter: u32) -> u32 { diff --git a/src/main.rs b/src/main.rs index b01eba5..fbb91be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use eframe::{ epaint::{TextureHandle, Vec2}, Frame, NativeOptions, }; -use generate::{render, FillStyle, RenderOptions}; +use generate::{render, view_point, FillStyle, RenderOptions}; use image::EncodableLayout; use native_dialog::FileDialog; use serde::{Deserialize, Serialize}; @@ -50,6 +50,7 @@ struct JuliaGUI { export_path: PathBuf, #[serde(skip)] settings_changed: bool, + preview_point: bool, } impl Default for JuliaGUI { @@ -60,10 +61,11 @@ impl Default for JuliaGUI { render_options: RenderOptions::default(), preview_render_ms: 0.0, export_render_ms: None, - export_res_power: 8, + export_res_power: 3, export_iterations: 512, export_path: "".into(), settings_changed: true, + preview_point: true, } } } @@ -96,7 +98,10 @@ impl JuliaGUI { fn update_preview(&mut self) { let start_time = SystemTime::now(); - let frame = render(&self.render_options, self.color); + let mut frame = render(&self.render_options, self.color); + if self.preview_point { + frame = view_point(&self.render_options, frame); + } if let Some(preview) = &mut self.preview { preview.set( @@ -129,7 +134,7 @@ impl JuliaGUI { fn export_render_new_path(&mut self) { if let Ok(Some(path)) = FileDialog::new() - .set_filename(&self.export_path.to_string_lossy().to_string()) + .set_filename(&self.export_path.to_string_lossy()) .add_filter("PNG file", &["png"]) .show_save_single_file() { @@ -156,6 +161,7 @@ impl eframe::App for JuliaGUI { self.preview_render_ms )); + let set_point_vis = ui.checkbox(&mut self.preview_point, "View C point"); ui.label("CX:"); let set_cx = ui.add(Slider::new(&mut self.render_options.cx, -2.0..=2.0)); ui.label("CY:"); @@ -242,7 +248,7 @@ impl eframe::App for JuliaGUI { .to_string(), ); if let Some(ms) = self.export_render_ms { - ui.label(format!("(took {:.2}ms)", ms)); + ui.label(format!("(took {ms:.2}ms)")); } if set_cx.changed() @@ -250,6 +256,7 @@ impl eframe::App for JuliaGUI { || set_iter.changed() || set_red.changed() || set_green.changed() || set_blue.changed() + || set_point_vis.changed() { self.settings_changed = true; } @@ -263,6 +270,6 @@ impl eframe::App for JuliaGUI { } fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) { - self.save_settings() + self.save_settings(); } }