save settings between sessions

This commit is contained in:
Crispy 2023-07-14 01:05:56 +02:00
parent 0068e6fe88
commit a2c1acd24d
5 changed files with 116 additions and 36 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
*.png *.png
!images/*.png !images/*.png
fractal_settings.json

25
Cargo.lock generated
View file

@ -1183,6 +1183,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "itoa"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
[[package]] [[package]]
name = "jni" name = "jni"
version = "0.21.1" version = "0.21.1"
@ -1230,6 +1236,8 @@ dependencies = [
"eframe", "eframe",
"image", "image",
"native-dialog", "native-dialog",
"serde",
"serde_json",
] ]
[[package]] [[package]]
@ -1866,6 +1874,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "ryu"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1920,6 +1934,17 @@ dependencies = [
"syn 2.0.25", "syn 2.0.25",
] ]
[[package]]
name = "serde_json"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.14" version = "0.1.14"

View file

@ -9,6 +9,8 @@ edition = "2021"
eframe = "0.22.0" eframe = "0.22.0"
image = { version = "0.24.6", default_features = false, features = ["png"] } image = { version = "0.24.6", default_features = false, features = ["png"] }
native-dialog = "0.6.4" native-dialog = "0.6.4"
serde = "1.0.171"
serde_json = "1.0.102"
# Enable high optimizations for dependencies # Enable high optimizations for dependencies
[profile.dev.package."*"] [profile.dev.package."*"]

View file

@ -1,6 +1,7 @@
use image::{Rgb, RgbImage}; use image::{Rgb, RgbImage};
use serde::{Deserialize, Serialize};
#[derive(Clone)] #[derive(Clone, Serialize, Deserialize)]
pub struct RenderOptions { pub struct RenderOptions {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,
@ -11,12 +12,26 @@ pub struct RenderOptions {
pub fill_style: FillStyle, pub fill_style: FillStyle,
} }
#[derive(PartialEq, Clone)] #[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum FillStyle { pub enum FillStyle {
Bright, Bright,
Black, Black,
} }
impl Default for RenderOptions {
fn default() -> Self {
Self {
width: 512,
height: 512,
unit_width: 4.0,
max_iterations: 128,
cx: -0.8,
cy: -0.27,
fill_style: FillStyle::Bright,
}
}
}
pub fn render(q: &RenderOptions, color: (u8, u8, u8)) -> RgbImage { pub fn render(q: &RenderOptions, color: (u8, u8, u8)) -> RgbImage {
let mut img = RgbImage::new(q.width, q.height); let mut img = RgbImage::new(q.width, q.height);

View file

@ -1,5 +1,10 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use std::{env, time::SystemTime}; use std::{
env,
fs::{self, File},
io::Write,
time::SystemTime,
};
use eframe::{ use eframe::{
egui::{self, DragValue, RichText, Slider, TextureOptions}, egui::{self, DragValue, RichText, Slider, TextureOptions},
@ -9,9 +14,12 @@ use eframe::{
use generate::{render, FillStyle, RenderOptions}; use generate::{render, FillStyle, RenderOptions};
use image::EncodableLayout; use image::EncodableLayout;
use native_dialog::FileDialog; use native_dialog::FileDialog;
use serde::{Deserialize, Serialize};
mod generate; mod generate;
const SETTINGS_FILE: &str = "fractal_settings.json";
fn main() { fn main() {
let native_options = NativeOptions { let native_options = NativeOptions {
initial_window_size: Some(Vec2::new(1280.0, 720.0)), initial_window_size: Some(Vec2::new(1280.0, 720.0)),
@ -26,58 +34,77 @@ fn main() {
.unwrap(); .unwrap();
} }
#[derive(Serialize, Deserialize)]
struct JuliaGUI { struct JuliaGUI {
color: (u8, u8, u8), color: (u8, u8, u8),
preview: TextureHandle, #[serde(skip)]
preview: Option<TextureHandle>,
render_options: RenderOptions, render_options: RenderOptions,
#[serde(skip)]
preview_render_ms: f64, preview_render_ms: f64,
#[serde(skip)]
export_render_ms: f64, export_render_ms: f64,
export_res_multiplier: u32, export_res_multiplier: u32,
export_iterations: u32, export_iterations: u32,
export_name: String, export_name: String,
#[serde(skip)]
settings_changed: bool, settings_changed: bool,
} }
impl Default for JuliaGUI {
fn default() -> Self {
Self {
color: (12, 5, 10),
preview: None,
render_options: RenderOptions::default(),
preview_render_ms: 0.0,
export_render_ms: f64::NAN,
export_res_multiplier: 8,
export_iterations: 512,
export_name: String::from("julia_fractal.png"),
settings_changed: true,
}
}
}
impl JuliaGUI { impl JuliaGUI {
fn new(cc: &eframe::CreationContext<'_>) -> Self { fn new(cc: &eframe::CreationContext<'_>) -> Self {
let mut n: Self = fs::read_to_string(SETTINGS_FILE)
.map(|s| serde_json::from_str(&s).ok())
.ok()
.flatten()
.unwrap_or_default();
let preview = cc.egui_ctx.load_texture( let preview = cc.egui_ctx.load_texture(
"preview_image", "preview_image",
egui::ColorImage::from_rgb([1, 1], &[0, 0, 0]), egui::ColorImage::from_rgb([1, 1], &[0, 0, 0]),
TextureOptions::default(), TextureOptions::default(),
); );
let preview_quality = RenderOptions {
width: 512,
height: 512,
unit_width: 4.0,
max_iterations: 128,
cx: -0.981,
cy: -0.277,
fill_style: FillStyle::Bright,
};
Self { n.preview = Some(preview);
color: (12, 5, 10), n.settings_changed = true;
preview, n
render_options: preview_quality, }
preview_render_ms: 0.0,
export_render_ms: f64::NAN, fn save_settings(&self) {
export_res_multiplier: 8, let settings = serde_json::to_string_pretty(&self).unwrap();
export_iterations: 512, let mut file = File::create(SETTINGS_FILE).unwrap();
export_name: String::from("julia_set.png"), file.write_all(settings.as_bytes()).unwrap();
settings_changed: true,
}
} }
fn update_preview(&mut self) { fn update_preview(&mut self) {
let start_time = SystemTime::now(); let start_time = SystemTime::now();
let preview = render(&self.render_options, self.color); let frame = render(&self.render_options, self.color);
self.preview.set(
egui::ColorImage::from_rgb( if let Some(preview) = &mut self.preview {
[preview.width() as usize, preview.height() as usize], preview.set(
preview.as_bytes(), egui::ColorImage::from_rgb(
), [frame.width() as usize, frame.height() as usize],
TextureOptions::default(), frame.as_bytes(),
); ),
TextureOptions::default(),
);
}
self.preview_render_ms = start_time.elapsed().unwrap().as_micros() as f64 / 1000.0; self.preview_render_ms = start_time.elapsed().unwrap().as_micros() as f64 / 1000.0;
} }
@ -94,6 +121,7 @@ impl JuliaGUI {
println!("Error exporting render: {err}"); println!("Error exporting render: {err}");
} }
self.export_render_ms = start_time.elapsed().unwrap().as_micros() as f64 / 1000.0; self.export_render_ms = start_time.elapsed().unwrap().as_micros() as f64 / 1000.0;
self.save_settings();
} }
} }
@ -101,6 +129,7 @@ impl eframe::App for JuliaGUI {
fn update(&mut self, ctx: &egui::Context, _frame: &mut Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut Frame) {
if self.settings_changed { if self.settings_changed {
self.update_preview(); self.update_preview();
self.save_settings();
self.settings_changed = false; self.settings_changed = false;
} }
@ -152,11 +181,15 @@ impl eframe::App for JuliaGUI {
ui.label("Preview resolution:"); ui.label("Preview resolution:");
ui.horizontal(|ui| { ui.horizontal(|ui| {
let set_width = ui.add( let set_width = ui.add(
DragValue::new(&mut self.render_options.width).clamp_range(128..=16384), DragValue::new(&mut self.render_options.width)
.clamp_range(128..=4096)
.suffix("px"),
); );
ui.label("x"); ui.label("x");
let set_height = ui.add( let set_height = ui.add(
DragValue::new(&mut self.render_options.height).clamp_range(128..=16384), DragValue::new(&mut self.render_options.height)
.clamp_range(128..=4096)
.suffix("px"),
); );
if set_width.changed() || set_height.changed() { if set_width.changed() || set_height.changed() {
self.settings_changed = true; self.settings_changed = true;
@ -207,9 +240,13 @@ impl eframe::App for JuliaGUI {
}); });
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.image(&self.preview, self.preview.size_vec2()); if let Some(texture) = &self.preview {
ui.image(texture, texture.size_vec2());
}
}); });
} }
fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {} fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {
self.save_settings()
}
} }