Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
783e3e1f30 | ||
|
|
0f01a16d60 |
+70
-27
@@ -72,15 +72,26 @@ fn kind_label(kind: FractalKind) -> &'static str {
|
|||||||
/// Multibrot.
|
/// Multibrot.
|
||||||
fn kind_formula(kind: FractalKind, power: u32) -> String {
|
fn kind_formula(kind: FractalKind, power: u32) -> String {
|
||||||
match kind {
|
match kind {
|
||||||
FractalKind::Mandelbrot => "z = z² + c".to_string(),
|
FractalKind::Mandelbrot => "z → z² + c".to_string(),
|
||||||
FractalKind::BurningShip => "z = (|Re(z)| + i|Im(z)|)² + c".to_string(),
|
FractalKind::BurningShip => "z → (|Re z| + i|Im z|)² + c".to_string(),
|
||||||
FractalKind::Tricorn => "z = conj(z)² + c".to_string(),
|
FractalKind::Tricorn => "z → conj(z)² + c".to_string(),
|
||||||
FractalKind::Multibrot => format!("z = z^{power} + c"),
|
FractalKind::Multibrot => format!("z → z^{power} + c"),
|
||||||
FractalKind::Celtic => "z = |Re(z²)| + i·Im(z²) + c".to_string(),
|
FractalKind::Celtic => "z → |Re(z²)| + i·Im(z²) + c".to_string(),
|
||||||
FractalKind::Perpendicular => "z = (x² − y²) − 2x|y|i + c".to_string(),
|
FractalKind::Perpendicular => "z → (x² − y²) − 2x|y|i + c".to_string(),
|
||||||
FractalKind::Buffalo => "z = |Re(z²)| − i|Im(z²)| + c".to_string(),
|
FractalKind::Buffalo => "z → |Re(z²)| − i|Im(z²)| + c".to_string(),
|
||||||
FractalKind::Phoenix => "z = z² + c + p·z_prev".to_string(),
|
FractalKind::Phoenix => "z → z² + c + p·z_prev".to_string(),
|
||||||
FractalKind::Lambda => "z = λ·z(1 − z)".to_string(),
|
FractalKind::Lambda => "z → λ·z(1 − z)".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shorten a long decimal string for compact display, keeping the leading
|
||||||
|
/// (most significant) digits and marking the cut with "…".
|
||||||
|
fn truncate_digits(s: &str, max_len: usize) -> String {
|
||||||
|
if s.chars().count() <= max_len {
|
||||||
|
s.to_string()
|
||||||
|
} else {
|
||||||
|
let head: String = s.chars().take(max_len).collect();
|
||||||
|
format!("{head}…")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1188,7 +1199,7 @@ impl FractalApp {
|
|||||||
.shadow(egui::Shadow::NONE)
|
.shadow(egui::Shadow::NONE)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if ui
|
if ui
|
||||||
.button("Fractal infos")
|
.button("ⓘ Info")
|
||||||
.on_hover_text("Show details about the current fractal")
|
.on_hover_text("Show details about the current fractal")
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
@@ -1215,17 +1226,13 @@ impl FractalApp {
|
|||||||
.strong()
|
.strong()
|
||||||
.heading(),
|
.heading(),
|
||||||
);
|
);
|
||||||
let mode_label = if self.buddhabrot {
|
let mode_label = match self.mode {
|
||||||
"Buddhabrot mode — orbit density (random c, z₀ = 0)"
|
|
||||||
} else {
|
|
||||||
match self.mode {
|
|
||||||
FractalMode::Mandelbrot => {
|
FractalMode::Mandelbrot => {
|
||||||
"Mandelbrot mode — parameter space (c varies per pixel, z₀ = 0)"
|
"Mandelbrot mode — parameter space (c varies per pixel, z₀ = 0)"
|
||||||
}
|
}
|
||||||
FractalMode::Julia => {
|
FractalMode::Julia => {
|
||||||
"Julia mode — dynamical plane for a fixed c (z₀ varies per pixel)"
|
"Julia mode — dynamical plane for a fixed c (z₀ varies per pixel)"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
ui.label(mode_label);
|
ui.label(mode_label);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@@ -1248,7 +1255,32 @@ impl FractalApp {
|
|||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.label(self.kind.description());
|
if self.buddhabrot {
|
||||||
|
ui.label("rendering: Buddhabrot (Monte-Carlo orbit density)");
|
||||||
|
ui.label(format!(
|
||||||
|
"sample caps: R {} / G {} / B {}",
|
||||||
|
self.buddha_r_cap, self.buddha_g_cap, self.buddha_b_cap
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
ui.label(format!(
|
||||||
|
"magnification: {}×",
|
||||||
|
format_magnification(self.view.magnification())
|
||||||
|
));
|
||||||
|
ui.label(format!(
|
||||||
|
"iterations: {}{}",
|
||||||
|
self.max_iterations,
|
||||||
|
if self.auto_iterations { " (auto)" } else { "" }
|
||||||
|
));
|
||||||
|
ui.label(format!("precision: {} bits", self.view.precision_bits()));
|
||||||
|
ui.label(format!(
|
||||||
|
"center re: {}",
|
||||||
|
truncate_digits(&self.center_re_edit, 24)
|
||||||
|
));
|
||||||
|
ui.label(format!(
|
||||||
|
"center im: {}",
|
||||||
|
truncate_digits(&self.center_im_edit, 24)
|
||||||
|
));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
self.info_open = open;
|
self.info_open = open;
|
||||||
}
|
}
|
||||||
@@ -1270,16 +1302,19 @@ impl FractalApp {
|
|||||||
ui.label(
|
ui.label(
|
||||||
"A deep-zoom fractal explorer. It renders the Mandelbrot set \
|
"A deep-zoom fractal explorer. It renders the Mandelbrot set \
|
||||||
and several related fractals (Burning Ship, Tricorn, \
|
and several related fractals (Burning Ship, Tricorn, \
|
||||||
Multibrot, Celtic, Perpendicular, Buffalo, Phoenix, Lambda).",
|
Multibrot, Celtic, Perpendicular, Buffalo, Phoenix, Lambda), \
|
||||||
|
and can zoom in far beyond what ordinary floating-point math \
|
||||||
|
allows by using perturbation theory: one very precise \
|
||||||
|
reference orbit is computed once, and every pixel on screen \
|
||||||
|
is then rendered cheaply as a small offset from that shared \
|
||||||
|
orbit.",
|
||||||
);
|
);
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
ui.label(
|
ui.label(
|
||||||
"Each fractals can be rendered in different modes: \n\
|
"Mandelbrot mode plots one point per pixel in parameter space \
|
||||||
• Mandelbrot mode fixes z₀=0 and then for each pixel, set c as it's position \
|
(does c escape?). Julia mode fixes c and instead varies the \
|
||||||
in the complex plane. \n\
|
starting point z₀ across the plane. Buddhabrot mode switches \
|
||||||
• Julia mode fixes c and instead varies the \
|
to a different, Monte-Carlo rendering of orbit density \
|
||||||
starting point z₀ across the plane. \n\
|
|
||||||
• Buddhabrot mode switches to a different, Monte-Carlo rendering of orbit density \
|
|
||||||
instead of the ordinary escape-time set.",
|
instead of the ordinary escape-time set.",
|
||||||
);
|
);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@@ -1331,11 +1366,20 @@ impl FractalApp {
|
|||||||
ui.label("Toggle antialiasing (2×2)");
|
ui.label("Toggle antialiasing (2×2)");
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
});
|
});
|
||||||
|
ui.label(
|
||||||
|
"Shortcuts are ignored while a text field (e.g. the center/zoom \
|
||||||
|
edit boxes) has focus. Esc / F11 toggle fullscreen via the \
|
||||||
|
browser or OS, as usual.",
|
||||||
|
);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.heading("Tips");
|
ui.heading("Tips");
|
||||||
ui.label(
|
ui.label(
|
||||||
"• \"Copy link\" (in the panel) encodes the exact view so it \
|
"• The controls panel (top-left \"Menu\" button) holds the \
|
||||||
|
fractal formula, coloring, animation, and export options.\n\
|
||||||
|
• The bottom-left \"Info\" button shows details about what's \
|
||||||
|
currently on screen.\n\
|
||||||
|
• \"Copy link\" (in the panel) encodes the exact view so it \
|
||||||
can be reopened later or sent to someone else.",
|
can be reopened later or sent to someone else.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1826,11 +1870,10 @@ impl FractalApp {
|
|||||||
ui.add(
|
ui.add(
|
||||||
egui::DragValue::new(&mut self.export_scale)
|
egui::DragValue::new(&mut self.export_scale)
|
||||||
.range(1.0..=16.0)
|
.range(1.0..=16.0)
|
||||||
.speed(0.25)
|
.speed(0.5),
|
||||||
.custom_formatter(|x, _| format!("x{:.1}", x)),
|
|
||||||
);
|
);
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"= {}×{}",
|
"→ {}×{}",
|
||||||
(self.last_size_px.x * self.export_scale) as u32,
|
(self.last_size_px.x * self.export_scale) as u32,
|
||||||
(self.last_size_px.y * self.export_scale) as u32,
|
(self.last_size_px.y * self.export_scale) as u32,
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -37,26 +37,6 @@ pub enum FractalKind {
|
|||||||
Lambda = 8,
|
Lambda = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FractalKind {
|
|
||||||
pub fn description(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
FractalKind::Mandelbrot => {
|
|
||||||
"The Mandelbrot set is the most famous fractal set, obtained with the simplest escape-time formula. This set represents all Julia fractals: each points of the Mandelbrot set is related to a specific Julia fractal."
|
|
||||||
}
|
|
||||||
FractalKind::BurningShip => {
|
|
||||||
"A variation of the famous Mandelbrot set, using absolute values on the real and imaginary part of each iterations."
|
|
||||||
}
|
|
||||||
FractalKind::Tricorn => "The Tricorn set is obtained using the same formula as the Mandelbrot set, taking the complex conjugate of the previous iteration.",
|
|
||||||
FractalKind::Multibrot => "Multibrot use the same formula as the Mandelbrot set, with a bigger exposant.",
|
|
||||||
FractalKind::Celtic => "",
|
|
||||||
FractalKind::Perpendicular => "",
|
|
||||||
FractalKind::Buffalo => "",
|
|
||||||
FractalKind::Phoenix => "",
|
|
||||||
FractalKind::Lambda => "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reference orbit escapes once |Z|^2 exceeds this. Kept larger than the pixel
|
/// Reference orbit escapes once |Z|^2 exceeds this. Kept larger than the pixel
|
||||||
/// bailout so pixels escaping alongside the reference can still reach their
|
/// bailout so pixels escaping alongside the reference can still reach their
|
||||||
/// bailout before the stored orbit runs out.
|
/// bailout before the stored orbit runs out.
|
||||||
|
|||||||
Reference in New Issue
Block a user