feat: lossless share links + auto iteration count
This commit is contained in:
+39
-5
@@ -124,6 +124,10 @@ pub struct FractalApp {
|
||||
power: u32,
|
||||
julia_c: (f64, f64),
|
||||
max_iterations: u32,
|
||||
/// When set, `max_iterations` tracks the zoom depth automatically (so deep
|
||||
/// zooms stay sharp without hand-tuning); the manual slider takes over when
|
||||
/// unset. Turned off when a preset or share link supplies an explicit count.
|
||||
auto_iterations: bool,
|
||||
color_scale: f32,
|
||||
color_offset: f32,
|
||||
palette: u32,
|
||||
@@ -222,6 +226,7 @@ impl FractalApp {
|
||||
power: 3,
|
||||
julia_c: (-0.8, 0.156),
|
||||
max_iterations: 512,
|
||||
auto_iterations: true,
|
||||
color_scale: 0.15,
|
||||
color_offset: 0.0,
|
||||
palette: 0,
|
||||
@@ -338,16 +343,28 @@ impl FractalApp {
|
||||
{
|
||||
self.mode = FractalMode::Mandelbrot;
|
||||
self.view = ViewState::with_center(cre, cim, half_height);
|
||||
// Presets carry a hand-tuned count; don't let the auto-scaler clobber it.
|
||||
self.auto_iterations = false;
|
||||
self.max_iterations = iterations.clamp(32, MAX_REF_POINTS as u32 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Iteration count scaled to the current zoom depth, used while
|
||||
/// `auto_iterations` is on. Grows roughly linearly with zoom decades so deep
|
||||
/// zooms keep enough iterations to stay sharp instead of banding.
|
||||
fn auto_iteration_count(&self) -> u32 {
|
||||
let decades = self.view.magnification().log10().max(0.0);
|
||||
let iters = 400.0 + 900.0 * decades;
|
||||
(iters.round() as u32).clamp(200, MAX_REF_POINTS as u32 - 1)
|
||||
}
|
||||
|
||||
/// Snapshot the current view as a shareable state.
|
||||
fn share_state(&self) -> ShareState {
|
||||
let sig_digits = sig_digits_for(self.view.precision_bits());
|
||||
ShareState {
|
||||
julia: matches!(self.mode, FractalMode::Julia),
|
||||
kind: self.kind,
|
||||
power: self.power,
|
||||
center_re: big_to_decimal_str(&self.view.center_re, sig_digits),
|
||||
center_im: big_to_decimal_str(&self.view.center_im, sig_digits),
|
||||
half_height: self.view.half_height,
|
||||
@@ -355,6 +372,7 @@ impl FractalApp {
|
||||
julia_c: self.julia_c,
|
||||
color_scale: self.color_scale,
|
||||
color_offset: self.color_offset,
|
||||
palette: self.palette,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,9 +384,14 @@ impl FractalApp {
|
||||
FractalMode::Mandelbrot
|
||||
};
|
||||
self.kind = s.kind;
|
||||
self.power = s.power.clamp(2, 8);
|
||||
self.julia_c = s.julia_c;
|
||||
self.color_scale = s.color_scale;
|
||||
self.color_offset = s.color_offset;
|
||||
self.palette = (s.palette as usize).min(PALETTE_NAMES.len() - 1) as u32;
|
||||
// The link carries an explicit iteration count; honor it rather than
|
||||
// letting the auto-scaler immediately overwrite it.
|
||||
self.auto_iterations = false;
|
||||
self.max_iterations = s.iterations.clamp(32, MAX_REF_POINTS as u32 - 1);
|
||||
let bits = precision_for(s.half_height);
|
||||
if let (Some(re), Some(im)) = (
|
||||
@@ -808,11 +831,17 @@ impl FractalApp {
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.max_iterations, 32..=100_000)
|
||||
.text("iterations")
|
||||
.logarithmic(true),
|
||||
);
|
||||
ui.checkbox(&mut self.auto_iterations, "Auto iterations")
|
||||
.on_hover_text("Scale the iteration count with zoom depth so deep zooms stay sharp.");
|
||||
if self.auto_iterations {
|
||||
ui.label(format!("iterations: {} (auto)", self.max_iterations));
|
||||
} else {
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.max_iterations, 32..=100_000)
|
||||
.text("iterations")
|
||||
.logarithmic(true),
|
||||
);
|
||||
}
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.color_scale, 0.01..=1.0)
|
||||
.text("color scale")
|
||||
@@ -1006,6 +1035,11 @@ impl FractalApp {
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
|
||||
// Keep the iteration count matched to the zoom depth while auto is on.
|
||||
if self.auto_iterations {
|
||||
self.max_iterations = self.auto_iteration_count();
|
||||
}
|
||||
|
||||
self.ensure_reference();
|
||||
|
||||
// Poll the worker roughly every 30 ms while a reference is computing,
|
||||
|
||||
Reference in New Issue
Block a user