Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ae461aef2 | ||
|
|
6b66d205ec |
@@ -61,11 +61,15 @@ pixel is a handful of `f32` complex multiplies.
|
|||||||
- `src/view.rs` — `ViewState`; center is arbitrary-precision `FBig` (`Big`
|
- `src/view.rs` — `ViewState`; center is arbitrary-precision `FBig` (`Big`
|
||||||
type alias), pixel scale stays `f64` (still in-range at 10³⁰×). Precision
|
type alias), pixel scale stays `f64` (still in-range at 10³⁰×). Precision
|
||||||
(bits) scales with zoom depth (`precision_for`).
|
(bits) scales with zoom depth (`precision_for`).
|
||||||
- `src/fractal/reference.rs` — `FractalKind` enum (Mandelbrot, Burning Ship,
|
- `src/fractal/kind.rs` — the `FractalKind` enum (Mandelbrot, Burning Ship,
|
||||||
Tricorn, Multibrot, Celtic, Perpendicular, Buffalo, Phoenix, Lambda) and
|
Tricorn, Multibrot, Celtic, Perpendicular, Buffalo, Phoenix, Lambda,
|
||||||
`compute_reference`/`compute_set_reference`: iterate the chosen formula at
|
Complex Multibrot) plus everything that only needs to switch on it:
|
||||||
high precision on the CPU, emitting `Z_n` as `f32` pairs — that's the
|
`label`/`description`/`formula` (UI text), `share_tag`/`from_share_tag`
|
||||||
reference orbit the GPU perturbs from.
|
(share-link encoding), `default_set_view` (per-kind starting view), and the
|
||||||
|
`ALL` array used to enumerate every kind.
|
||||||
|
- `src/fractal/reference.rs` — `compute_reference`/`compute_set_reference`:
|
||||||
|
iterate the chosen formula at high precision on the CPU, emitting `Z_n` as
|
||||||
|
`f32` pairs — that's the reference orbit the GPU perturbs from.
|
||||||
- `src/shaders/*.wgsl` — none of these are standalone WGSL modules; WGSL has
|
- `src/shaders/*.wgsl` — none of these are standalone WGSL modules; WGSL has
|
||||||
no `#include`, so each is compiled by concatenating plain-text fragments
|
no `#include`, so each is compiled by concatenating plain-text fragments
|
||||||
with `concat!`/`include_str!` at the `create_shader_module` call site (see
|
with `concat!`/`include_str!` at the `create_shader_module` call site (see
|
||||||
@@ -106,29 +110,31 @@ pixel is a handful of `f32` complex multiplies.
|
|||||||
`should_request`/`ensure_reference` (decide when the reference is stale and
|
`should_request`/`ensure_reference` (decide when the reference is stale and
|
||||||
dispatch/collect it), `make_uniforms` (assemble the per-frame `Uniforms`),
|
dispatch/collect it), `make_uniforms` (assemble the per-frame `Uniforms`),
|
||||||
`tick_animations` (drives the "morph c/p/λ" and auto-zoom animations),
|
`tick_animations` (drives the "morph c/p/λ" and auto-zoom animations),
|
||||||
`default_view_for` (per-kind starting view). `KINDS`, `JULIA_PRESETS`, and
|
`default_view_for` (wraps `FractalKind::default_set_view`, adding the
|
||||||
`SET_PRESETS` are sized as `[T; FractalKind::<last variant> as usize + 1]` —
|
kind-independent Julia case). `JULIA_PRESETS` and `SET_PRESETS` are sized as
|
||||||
adding a new `FractalKind` means bumping all three (and adding an empty
|
`[T; FractalKind::<last variant> as usize + 1]` — adding a new `FractalKind`
|
||||||
`&[]` slot to the two preset arrays if the kind has none).
|
means bumping both (and adding an empty `&[]` slot to each if the kind has
|
||||||
|
none), plus adding it to `FractalKind::ALL` in `kind.rs`.
|
||||||
- `src/fractal/share.rs` — `ShareState`: encodes the full view (mode, kind,
|
- `src/fractal/share.rs` — `ShareState`: encodes the full view (mode, kind,
|
||||||
full-precision decimal center, zoom, iterations, per-kind constants,
|
full-precision decimal center, zoom, iterations, per-kind constants,
|
||||||
coloring) as a `#`-fragment URL for bookmarking/sharing deep-zoom locations.
|
coloring) as a `#`-fragment URL for bookmarking/sharing deep-zoom locations.
|
||||||
|
|
||||||
### Adding a new `FractalKind`
|
### Adding a new `FractalKind`
|
||||||
|
|
||||||
Touches, in order: `reference.rs` (enum variant + CPU iteration formula, and a
|
Touches, in order: `kind.rs` (enum variant + `ALL` slot + `label`/
|
||||||
test comparing against a naive `f64` iteration), `common.wgsl` (matching
|
`description`/`formula`/`share_tag`/`from_share_tag`/`default_set_view`
|
||||||
`KIND_*` const), `mandelbrot.wgsl` (matching `advance_delta`/`fprime` arms),
|
arms), `reference.rs` (CPU iteration formula arm, and a test comparing
|
||||||
`buddhabrot.wgsl` (matching arm in `advance()`, if the kind makes sense as a
|
against a naive `f64` iteration), `common.wgsl` (matching `KIND_*` const),
|
||||||
Buddhabrot), `renderer.rs`
|
`mandelbrot.wgsl` (matching `advance_delta`/`fprime` arms), `buddhabrot.wgsl`
|
||||||
`Uniforms` (only if the kind needs a new per-kind constant, e.g. Phoenix's
|
(matching arm in `advance()`, if the kind makes sense as a Buddhabrot),
|
||||||
`phoenix_p`), `share.rs` (encode/decode string tag), `app.rs` (`KINDS` label,
|
`renderer.rs` `Uniforms` (only if the kind needs a new per-kind constant,
|
||||||
`JULIA_PRESETS`/`SET_PRESETS` slot, `default_view_for` entry, and optionally a
|
e.g. Phoenix's `phoenix_p`), `app.rs` (`JULIA_PRESETS`/`SET_PRESETS` slot,
|
||||||
UI control for its constant + an animation toggle, following the
|
and optionally a UI control for its constant + an animation toggle,
|
||||||
Phoenix/Lambda pattern). If `c` doesn't enter the formula additively (e.g. a
|
following the Phoenix/Lambda pattern). If `c` doesn't enter the formula
|
||||||
rational map with `c` in a denominator), the `advance_delta`/`step_add` split
|
additively (e.g. a rational map with `c` in a denominator), the
|
||||||
doesn't work — that needs its own step function plus extra per-step reference
|
`advance_delta`/`step_add` split doesn't work — that needs its own step
|
||||||
data uploaded in a second GPU buffer alongside the orbit.
|
function plus extra per-step reference data uploaded in a second GPU buffer
|
||||||
|
alongside the orbit.
|
||||||
|
|
||||||
### Buddhabrot is a separate pipeline
|
### Buddhabrot is a separate pipeline
|
||||||
|
|
||||||
|
|||||||
+6
-64
@@ -46,49 +46,6 @@ pub enum FractalMode {
|
|||||||
Buddhabrot,
|
Buddhabrot,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Selectable fractal formulas, with UI labels.
|
|
||||||
const KINDS: &[(FractalKind, &str)] = &[
|
|
||||||
(FractalKind::Mandelbrot, "Mandelbrot"),
|
|
||||||
(FractalKind::BurningShip, "Burning Ship"),
|
|
||||||
(FractalKind::Tricorn, "Tricorn"),
|
|
||||||
(FractalKind::Multibrot, "Multibrot"),
|
|
||||||
(FractalKind::Celtic, "Celtic"),
|
|
||||||
(FractalKind::Perpendicular, "Perpendicular"),
|
|
||||||
(FractalKind::Buffalo, "Buffalo"),
|
|
||||||
(FractalKind::Phoenix, "Phoenix"),
|
|
||||||
(FractalKind::Lambda, "Lambda"),
|
|
||||||
(FractalKind::ComplexMultibrot, "Complex Multibrot"),
|
|
||||||
];
|
|
||||||
|
|
||||||
/// UI label for a fractal kind.
|
|
||||||
fn kind_label(kind: FractalKind) -> &'static str {
|
|
||||||
KINDS
|
|
||||||
.iter()
|
|
||||||
.find(|(k, _)| *k == kind)
|
|
||||||
.map(|(_, name)| *name)
|
|
||||||
.unwrap_or("Mandelbrot")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The iteration formula for a kind, in human-readable notation (mirrors the
|
|
||||||
/// doc comments on `FractalKind`'s variants). `power` is only used by
|
|
||||||
/// Multibrot; `complex_power` only by Complex Multibrot.
|
|
||||||
fn kind_formula(kind: FractalKind, power: u32, complex_power: (f64, f64)) -> String {
|
|
||||||
match kind {
|
|
||||||
FractalKind::Mandelbrot => "z = z² + c".to_string(),
|
|
||||||
FractalKind::BurningShip => "z = (|Re(z)| + i|Im(z)|)² + c".to_string(),
|
|
||||||
FractalKind::Tricorn => "z = conj(z)² + c".to_string(),
|
|
||||||
FractalKind::Multibrot => format!("z = z^{power} + c"),
|
|
||||||
FractalKind::Celtic => "z = |Re(z²)| + i·Im(z²) + 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::Phoenix => "z = z² + c + p·z_prev".to_string(),
|
|
||||||
FractalKind::Lambda => "z = λ·z(1 − z)".to_string(),
|
|
||||||
FractalKind::ComplexMultibrot => {
|
|
||||||
format!("z = z^({:.3}{:+.3}i) + c", complex_power.0, complex_power.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type JuliaPreset = (&'static str, f64, f64, u32, Option<(f64, f64)>);
|
type JuliaPreset = (&'static str, f64, f64, u32, Option<(f64, f64)>);
|
||||||
|
|
||||||
/// Nice-looking Julia constants offered as presets.
|
/// Nice-looking Julia constants offered as presets.
|
||||||
@@ -697,18 +654,7 @@ impl FractalApp {
|
|||||||
if mode == FractalMode::Julia {
|
if mode == FractalMode::Julia {
|
||||||
return ViewState::with_center(big_from_f64(0.0, 53), big_from_f64(0.0, 53), 1.5);
|
return ViewState::with_center(big_from_f64(0.0, 53), big_from_f64(0.0, 53), 1.5);
|
||||||
}
|
}
|
||||||
let (cr, ci, hh) = match kind {
|
let (cr, ci, hh) = kind.default_set_view();
|
||||||
FractalKind::Mandelbrot => (-0.5, 0.0, 1.25),
|
|
||||||
FractalKind::BurningShip => (-0.5, -0.5, 1.3),
|
|
||||||
FractalKind::Tricorn => (-0.25, 0.0, 1.6),
|
|
||||||
FractalKind::Multibrot => (0.0, 0.0, 1.5),
|
|
||||||
FractalKind::Celtic => (-0.5, 0.0, 1.6),
|
|
||||||
FractalKind::Perpendicular => (-0.5, 0.0, 1.5),
|
|
||||||
FractalKind::Buffalo => (-0.5, -0.5, 1.5),
|
|
||||||
FractalKind::Phoenix => (0.0, 0.0, 1.6),
|
|
||||||
FractalKind::Lambda => (0.0, 0.0, 1.6),
|
|
||||||
FractalKind::ComplexMultibrot => (0.0, 0.0, 1.5),
|
|
||||||
};
|
|
||||||
ViewState::with_center(big_from_f64(cr, 53), big_from_f64(ci, 53), hh)
|
ViewState::with_center(big_from_f64(cr, 53), big_from_f64(ci, 53), hh)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1249,11 +1195,7 @@ impl FractalApp {
|
|||||||
.resizable(false)
|
.resizable(false)
|
||||||
.anchor(egui::Align2::LEFT_BOTTOM, egui::vec2(8.0, -44.0))
|
.anchor(egui::Align2::LEFT_BOTTOM, egui::vec2(8.0, -44.0))
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.label(
|
ui.label(egui::RichText::new(self.kind.label()).strong().heading());
|
||||||
egui::RichText::new(kind_label(self.kind))
|
|
||||||
.strong()
|
|
||||||
.heading(),
|
|
||||||
);
|
|
||||||
let mode_label = match self.mode {
|
let mode_label = 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)"
|
||||||
@@ -1268,7 +1210,7 @@ impl FractalApp {
|
|||||||
|
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"formula: {}",
|
"formula: {}",
|
||||||
kind_formula(self.kind, self.power, self.complex_power)
|
self.kind.formula(self.power, self.complex_power)
|
||||||
));
|
));
|
||||||
if self.mode == FractalMode::Julia {
|
if self.mode == FractalMode::Julia {
|
||||||
ui.label(format!("c = {:.6} {:+.6}i", self.julia_c.0, self.julia_c.1));
|
ui.label(format!("c = {:.6} {:+.6}i", self.julia_c.0, self.julia_c.1));
|
||||||
@@ -1517,10 +1459,10 @@ impl FractalApp {
|
|||||||
// since interesting regions differ between fractals.
|
// since interesting regions differ between fractals.
|
||||||
let prev_kind = self.kind;
|
let prev_kind = self.kind;
|
||||||
egui::ComboBox::from_label("fractal")
|
egui::ComboBox::from_label("fractal")
|
||||||
.selected_text(kind_label(self.kind))
|
.selected_text(self.kind.label())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
for &(kind, name) in KINDS {
|
for kind in FractalKind::ALL {
|
||||||
ui.selectable_value(&mut self.kind, kind, name);
|
ui.selectable_value(&mut self.kind, kind, kind.label());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if self.kind == FractalKind::Multibrot {
|
if self.kind == FractalKind::Multibrot {
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
//! `FractalKind`: the enum selecting which iteration formula is in use, plus
|
||||||
|
//! everything that only needs to switch on it (UI label/description/formula
|
||||||
|
//! text, share-link tag, default parameter-plane view). The CPU/GPU orbit
|
||||||
|
//! math itself lives in `reference.rs` (CPU reference orbit) and
|
||||||
|
//! `shaders/mandelbrot.wgsl` (GPU perturbation delta) since both must also
|
||||||
|
//! stay in sync with `common.wgsl`'s `KIND_*` constants.
|
||||||
|
|
||||||
|
/// The iteration formula. Must be kept in sync with `advance_delta` and the
|
||||||
|
/// `KIND_*` constants in the shader.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub enum FractalKind {
|
||||||
|
/// `z -> z^2 + c`.
|
||||||
|
Mandelbrot = 0,
|
||||||
|
/// `z -> (|Re z| + i|Im z|)^2 + c`.
|
||||||
|
BurningShip = 1,
|
||||||
|
/// `z -> conj(z)^2 + c` (the Mandelbar).
|
||||||
|
Tricorn = 2,
|
||||||
|
/// `z -> z^power + c` (power >= 2).
|
||||||
|
Multibrot = 3,
|
||||||
|
/// `z -> |Re(z^2)| + i·Im(z^2) + c` (abs on the real output of the square).
|
||||||
|
Celtic = 4,
|
||||||
|
/// `z -> (x^2 - y^2) - 2·x·|y|·i + c` (abs on the imaginary input).
|
||||||
|
Perpendicular = 5,
|
||||||
|
/// `z -> |Re(z^2)| - |Im(z^2)|·i + c` (abs on both outputs).
|
||||||
|
Buffalo = 6,
|
||||||
|
/// `z -> z^2 + c + p·z_{n-1}` (two-term recurrence; `p` is `phoenix_p`).
|
||||||
|
Phoenix = 7,
|
||||||
|
/// `z -> lambda·z(1 - z)` (logistic map).
|
||||||
|
Lambda = 8,
|
||||||
|
/// `z -> z^power + c`, where `power` is a complex constant (the
|
||||||
|
/// `complex_power` argument), via the principal branch `z^p = exp(p·ln z)`.
|
||||||
|
ComplexMultibrot = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FractalKind {
|
||||||
|
/// Every kind, in declaration/discriminant order. Sized arrays keyed by
|
||||||
|
/// `kind as usize` (`JULIA_PRESETS`, `SET_PRESETS`) must have one slot per
|
||||||
|
/// entry here.
|
||||||
|
pub const ALL: [FractalKind; 10] = [
|
||||||
|
FractalKind::Mandelbrot,
|
||||||
|
FractalKind::BurningShip,
|
||||||
|
FractalKind::Tricorn,
|
||||||
|
FractalKind::Multibrot,
|
||||||
|
FractalKind::Celtic,
|
||||||
|
FractalKind::Perpendicular,
|
||||||
|
FractalKind::Buffalo,
|
||||||
|
FractalKind::Phoenix,
|
||||||
|
FractalKind::Lambda,
|
||||||
|
FractalKind::ComplexMultibrot,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn description(&self) -> &'static 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 => "",
|
||||||
|
FractalKind::ComplexMultibrot => {
|
||||||
|
"Like Multibrot, but the exponent itself is a complex number instead of a plain integer, via z^p = exp(p·ln z)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UI label for this kind (combo box / info panel heading).
|
||||||
|
pub fn label(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FractalKind::Mandelbrot => "Mandelbrot",
|
||||||
|
FractalKind::BurningShip => "Burning Ship",
|
||||||
|
FractalKind::Tricorn => "Tricorn",
|
||||||
|
FractalKind::Multibrot => "Multibrot",
|
||||||
|
FractalKind::Celtic => "Celtic",
|
||||||
|
FractalKind::Perpendicular => "Perpendicular",
|
||||||
|
FractalKind::Buffalo => "Buffalo",
|
||||||
|
FractalKind::Phoenix => "Phoenix",
|
||||||
|
FractalKind::Lambda => "Lambda",
|
||||||
|
FractalKind::ComplexMultibrot => "Complex Multibrot",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The iteration formula in human-readable notation (mirrors the doc
|
||||||
|
/// comments on the variants above). `power` is only used by Multibrot;
|
||||||
|
/// `complex_power` only by Complex Multibrot.
|
||||||
|
pub fn formula(&self, power: u32, complex_power: (f64, f64)) -> String {
|
||||||
|
match self {
|
||||||
|
FractalKind::Mandelbrot => "z = z² + c".to_string(),
|
||||||
|
FractalKind::BurningShip => "z = (|Re(z)| + i|Im(z)|)² + c".to_string(),
|
||||||
|
FractalKind::Tricorn => "z = conj(z)² + c".to_string(),
|
||||||
|
FractalKind::Multibrot => format!("z = z^{power} + c"),
|
||||||
|
FractalKind::Celtic => "z = |Re(z²)| + i·Im(z²) + 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::Phoenix => "z = z² + c + p·z_prev".to_string(),
|
||||||
|
FractalKind::Lambda => "z = λ·z(1 − z)".to_string(),
|
||||||
|
FractalKind::ComplexMultibrot => {
|
||||||
|
format!("z = z^({:.3}{:+.3}i) + c", complex_power.0, complex_power.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Short tag used to identify this kind in a share-link fragment.
|
||||||
|
pub fn share_tag(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
FractalKind::Mandelbrot => "mandel",
|
||||||
|
FractalKind::BurningShip => "burning",
|
||||||
|
FractalKind::Tricorn => "tricorn",
|
||||||
|
FractalKind::Multibrot => "multi",
|
||||||
|
FractalKind::Celtic => "celtic",
|
||||||
|
FractalKind::Perpendicular => "perp",
|
||||||
|
FractalKind::Buffalo => "buffalo",
|
||||||
|
FractalKind::Phoenix => "phoenix",
|
||||||
|
FractalKind::Lambda => "lambda",
|
||||||
|
FractalKind::ComplexMultibrot => "cmulti",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inverse of `share_tag`; unknown tags fall back to `None` so the caller
|
||||||
|
/// can decide the default (matches historical share-link behavior).
|
||||||
|
pub fn from_share_tag(tag: &str) -> Option<FractalKind> {
|
||||||
|
Some(match tag {
|
||||||
|
"mandel" => FractalKind::Mandelbrot,
|
||||||
|
"burning" => FractalKind::BurningShip,
|
||||||
|
"tricorn" => FractalKind::Tricorn,
|
||||||
|
"multi" => FractalKind::Multibrot,
|
||||||
|
"celtic" => FractalKind::Celtic,
|
||||||
|
"perp" => FractalKind::Perpendicular,
|
||||||
|
"buffalo" => FractalKind::Buffalo,
|
||||||
|
"phoenix" => FractalKind::Phoenix,
|
||||||
|
"lambda" => FractalKind::Lambda,
|
||||||
|
"cmulti" => FractalKind::ComplexMultibrot,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default parameter-plane (Mandelbrot-mode) view for this kind, as
|
||||||
|
/// `(center_re, center_im, half_height)`. The Julia (dynamical) plane
|
||||||
|
/// doesn't vary by kind, so it isn't covered here.
|
||||||
|
pub fn default_set_view(&self) -> (f64, f64, f64) {
|
||||||
|
match self {
|
||||||
|
FractalKind::Mandelbrot => (-0.5, 0.0, 1.25),
|
||||||
|
FractalKind::BurningShip => (-0.5, -0.5, 1.3),
|
||||||
|
FractalKind::Tricorn => (-0.25, 0.0, 1.6),
|
||||||
|
FractalKind::Multibrot => (0.0, 0.0, 1.5),
|
||||||
|
FractalKind::Celtic => (-0.5, 0.0, 1.6),
|
||||||
|
FractalKind::Perpendicular => (-0.5, 0.0, 1.5),
|
||||||
|
FractalKind::Buffalo => (-0.5, -0.5, 1.5),
|
||||||
|
FractalKind::Phoenix => (0.0, 0.0, 1.6),
|
||||||
|
FractalKind::Lambda => (0.0, 0.0, 1.6),
|
||||||
|
FractalKind::ComplexMultibrot => (0.0, 0.0, 1.5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -2,12 +2,14 @@
|
|||||||
//! egui paint callback.
|
//! egui paint callback.
|
||||||
|
|
||||||
pub mod buddhabrot;
|
pub mod buddhabrot;
|
||||||
|
pub mod kind;
|
||||||
pub mod reference;
|
pub mod reference;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod share;
|
pub mod share;
|
||||||
|
|
||||||
pub use buddhabrot::{BuddhabrotCallback, BuddhabrotRenderer, BuddhabrotUniforms};
|
pub use buddhabrot::{BuddhabrotCallback, BuddhabrotRenderer, BuddhabrotUniforms};
|
||||||
pub use reference::{FractalKind, compute_reference, compute_set_reference};
|
pub use kind::FractalKind;
|
||||||
|
pub use reference::{compute_reference, compute_set_reference};
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub use renderer::encode_png_with_progress;
|
pub use renderer::encode_png_with_progress;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|||||||
@@ -10,57 +10,9 @@
|
|||||||
//! * Mandelbrot-set: `z0 = 0`, `c = view center` (the c-plane point per pixel).
|
//! * Mandelbrot-set: `z0 = 0`, `c = view center` (the c-plane point per pixel).
|
||||||
//! * Julia-set: `z0 = view center`, `c = fractal constant` (fixed per view).
|
//! * Julia-set: `z0 = view center`, `c = fractal constant` (fixed per view).
|
||||||
|
|
||||||
|
use super::kind::FractalKind;
|
||||||
use crate::view::{Big, big_from_f64};
|
use crate::view::{Big, big_from_f64};
|
||||||
|
|
||||||
/// The iteration formula. Must be kept in sync with `advance_delta` and the
|
|
||||||
/// `KIND_*` constants in the shader.
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
||||||
pub enum FractalKind {
|
|
||||||
/// `z -> z^2 + c`.
|
|
||||||
Mandelbrot = 0,
|
|
||||||
/// `z -> (|Re z| + i|Im z|)^2 + c`.
|
|
||||||
BurningShip = 1,
|
|
||||||
/// `z -> conj(z)^2 + c` (the Mandelbar).
|
|
||||||
Tricorn = 2,
|
|
||||||
/// `z -> z^power + c` (power >= 2).
|
|
||||||
Multibrot = 3,
|
|
||||||
/// `z -> |Re(z^2)| + i·Im(z^2) + c` (abs on the real output of the square).
|
|
||||||
Celtic = 4,
|
|
||||||
/// `z -> (x^2 - y^2) - 2·x·|y|·i + c` (abs on the imaginary input).
|
|
||||||
Perpendicular = 5,
|
|
||||||
/// `z -> |Re(z^2)| - |Im(z^2)|·i + c` (abs on both outputs).
|
|
||||||
Buffalo = 6,
|
|
||||||
/// `z -> z^2 + c + p·z_{n-1}` (two-term recurrence; `p` is `phoenix_p`).
|
|
||||||
Phoenix = 7,
|
|
||||||
/// `z -> lambda·z(1 - z)` (logistic map).
|
|
||||||
Lambda = 8,
|
|
||||||
/// `z -> z^power + c`, where `power` is a complex constant (the
|
|
||||||
/// `complex_power` argument), via the principal branch `z^p = exp(p·ln z)`.
|
|
||||||
ComplexMultibrot = 9,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 => "",
|
|
||||||
FractalKind::ComplexMultibrot => "Like Multibrot, but the exponent itself is a complex number instead of a plain integer, via z^p = exp(p·ln z).",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
||||||
|
|||||||
+2
-28
@@ -39,21 +39,7 @@ impl ShareState {
|
|||||||
pub fn encode(&self) -> String {
|
pub fn encode(&self) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s.push_str(if self.julia { "m=j" } else { "m=m" });
|
s.push_str(if self.julia { "m=j" } else { "m=m" });
|
||||||
s.push_str(&format!(
|
s.push_str(&format!("&f={}", self.kind.share_tag()));
|
||||||
"&f={}",
|
|
||||||
match self.kind {
|
|
||||||
FractalKind::Mandelbrot => "mandel",
|
|
||||||
FractalKind::BurningShip => "burning",
|
|
||||||
FractalKind::Multibrot => "multi",
|
|
||||||
FractalKind::Tricorn => "tricorn",
|
|
||||||
FractalKind::Celtic => "celtic",
|
|
||||||
FractalKind::Perpendicular => "perp",
|
|
||||||
FractalKind::Buffalo => "buffalo",
|
|
||||||
FractalKind::Phoenix => "phoenix",
|
|
||||||
FractalKind::Lambda => "lambda",
|
|
||||||
FractalKind::ComplexMultibrot => "cmulti",
|
|
||||||
}
|
|
||||||
));
|
|
||||||
s.push_str(&format!("&pw={}", self.power));
|
s.push_str(&format!("&pw={}", self.power));
|
||||||
s.push_str(&format!(
|
s.push_str(&format!(
|
||||||
"&re={}&im={}&hh={}&it={}",
|
"&re={}&im={}&hh={}&it={}",
|
||||||
@@ -86,19 +72,7 @@ impl ShareState {
|
|||||||
julia: map.get("m").map(|m| *m == "j").unwrap_or(false),
|
julia: map.get("m").map(|m| *m == "j").unwrap_or(false),
|
||||||
kind: map
|
kind: map
|
||||||
.get("f")
|
.get("f")
|
||||||
.map(|f| match *f {
|
.and_then(|f| FractalKind::from_share_tag(f))
|
||||||
"mandel" => FractalKind::Mandelbrot,
|
|
||||||
"multi" => FractalKind::Multibrot,
|
|
||||||
"burning" => FractalKind::BurningShip,
|
|
||||||
"tricorn" => FractalKind::Tricorn,
|
|
||||||
"celtic" => FractalKind::Celtic,
|
|
||||||
"perp" => FractalKind::Perpendicular,
|
|
||||||
"buffalo" => FractalKind::Buffalo,
|
|
||||||
"phoenix" => FractalKind::Phoenix,
|
|
||||||
"lambda" => FractalKind::Lambda,
|
|
||||||
"cmulti" => FractalKind::ComplexMultibrot,
|
|
||||||
_ => FractalKind::Mandelbrot,
|
|
||||||
})
|
|
||||||
.unwrap_or(FractalKind::Mandelbrot),
|
.unwrap_or(FractalKind::Mandelbrot),
|
||||||
power: map.get("pw").and_then(|s| s.parse().ok()).unwrap_or(2),
|
power: map.get("pw").and_then(|s| s.parse().ok()).unwrap_or(2),
|
||||||
center_re: (*map.get("re")?).to_string(),
|
center_re: (*map.get("re")?).to_string(),
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ pub fn run(cli: Cli) -> Result<(), String> {
|
|||||||
app.compute_reference_blocking();
|
app.compute_reference_blocking();
|
||||||
|
|
||||||
let (device, queue) = pollster::block_on(request_device())?;
|
let (device, queue) = pollster::block_on(request_device())?;
|
||||||
let format = wgpu::TextureFormat::Rgba8UnormSrgb;
|
let format = wgpu::TextureFormat::Bgra8Unorm;
|
||||||
let renderer = FractalRenderer::new(&device, format);
|
let renderer = FractalRenderer::new(&device, format);
|
||||||
let (pipeline, bind_group_layout, format) = renderer.export_handles();
|
let (pipeline, bind_group_layout, format) = renderer.export_handles();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user