fix: add c parameter again for lambda fractal in julia mode
This commit is contained in:
+1
-1
@@ -2142,7 +2142,7 @@ impl FractalApp {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.mode == FractalMode::Julia && self.kind != FractalKind::Lambda {
|
if self.mode == FractalMode::Julia {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("c =");
|
ui.label("c =");
|
||||||
ui.add(
|
ui.add(
|
||||||
|
|||||||
+2
-2
@@ -26,7 +26,7 @@ pub enum FractalKind {
|
|||||||
Buffalo = 6,
|
Buffalo = 6,
|
||||||
/// `z -> z^2 + c + p·z_{n-1}` (two-term recurrence; `p` is `phoenix_p`).
|
/// `z -> z^2 + c + p·z_{n-1}` (two-term recurrence; `p` is `phoenix_p`).
|
||||||
Phoenix = 7,
|
Phoenix = 7,
|
||||||
/// `z -> lambda·z(1 - z)` (logistic map).
|
/// `z -> lambda·z(1 - z) + c` (logistic map).
|
||||||
Lambda = 8,
|
Lambda = 8,
|
||||||
/// `z -> z^power + c`, where `power` is a complex constant (the
|
/// `z -> z^power + c`, where `power` is a complex constant (the
|
||||||
/// `complex_power` argument), via the principal branch `z^p = exp(p·ln z)`.
|
/// `complex_power` argument), via the principal branch `z^p = exp(p·ln z)`.
|
||||||
@@ -104,7 +104,7 @@ impl FractalKind {
|
|||||||
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) + c".to_string(),
|
||||||
FractalKind::ComplexMultibrot => {
|
FractalKind::ComplexMultibrot => {
|
||||||
format!("z = z^({:.3}{:+.3}i) + c", complex_power.0, complex_power.1)
|
format!("z = z^({:.3}{:+.3}i) + c", complex_power.0, complex_power.1)
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-56
@@ -59,41 +59,6 @@ pub fn compute_reference(
|
|||||||
lambda_l: (f64, f64),
|
lambda_l: (f64, f64),
|
||||||
complex_power: (f64, f64),
|
complex_power: (f64, f64),
|
||||||
morph: Option<(FractalKind, f64)>,
|
morph: Option<(FractalKind, f64)>,
|
||||||
) -> Vec<[f32; 2]> {
|
|
||||||
compute_reference_inner(
|
|
||||||
z0_re,
|
|
||||||
z0_im,
|
|
||||||
c_re,
|
|
||||||
c_im,
|
|
||||||
max_iter,
|
|
||||||
precision,
|
|
||||||
kind,
|
|
||||||
power,
|
|
||||||
phoenix_p,
|
|
||||||
lambda_l,
|
|
||||||
complex_power,
|
|
||||||
morph,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [`compute_reference`] plus `set_plane` (see [`StepConsts::set_plane`]):
|
|
||||||
/// picks the `f64` fast path or the `FBig` path by precision.
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn compute_reference_inner(
|
|
||||||
z0_re: &Big,
|
|
||||||
z0_im: &Big,
|
|
||||||
c_re: &Big,
|
|
||||||
c_im: &Big,
|
|
||||||
max_iter: u32,
|
|
||||||
precision: usize,
|
|
||||||
kind: FractalKind,
|
|
||||||
power: u32,
|
|
||||||
phoenix_p: (f64, f64),
|
|
||||||
lambda_l: (f64, f64),
|
|
||||||
complex_power: (f64, f64),
|
|
||||||
morph: Option<(FractalKind, f64)>,
|
|
||||||
set_plane: bool,
|
|
||||||
) -> Vec<[f32; 2]> {
|
) -> Vec<[f32; 2]> {
|
||||||
// A zero-weight morph is just the plain kind; skip the second formula.
|
// A zero-weight morph is just the plain kind; skip the second formula.
|
||||||
let morph = morph.filter(|&(_, w)| w != 0.0);
|
let morph = morph.filter(|&(_, w)| w != 0.0);
|
||||||
@@ -104,7 +69,6 @@ fn compute_reference_inner(
|
|||||||
l: lambda_l,
|
l: lambda_l,
|
||||||
cpow: complex_power,
|
cpow: complex_power,
|
||||||
power,
|
power,
|
||||||
set_plane,
|
|
||||||
};
|
};
|
||||||
return compute_reference_f64(
|
return compute_reference_f64(
|
||||||
(z0_re.to_f64().value(), z0_im.to_f64().value()),
|
(z0_re.to_f64().value(), z0_im.to_f64().value()),
|
||||||
@@ -125,7 +89,6 @@ fn compute_reference_inner(
|
|||||||
cpow_im: big_from_f64(complex_power.1, precision),
|
cpow_im: big_from_f64(complex_power.1, precision),
|
||||||
power,
|
power,
|
||||||
precision,
|
precision,
|
||||||
set_plane,
|
|
||||||
};
|
};
|
||||||
compute_reference_big(z0_re, z0_im, max_iter, kind, &k, morph)
|
compute_reference_big(z0_re, z0_im, max_iter, kind, &k, morph)
|
||||||
}
|
}
|
||||||
@@ -137,7 +100,6 @@ struct StepConstsF64 {
|
|||||||
l: (f64, f64),
|
l: (f64, f64),
|
||||||
cpow: (f64, f64),
|
cpow: (f64, f64),
|
||||||
power: u32,
|
power: u32,
|
||||||
set_plane: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`compute_reference`]'s fast path for shallow views (see
|
/// [`compute_reference`]'s fast path for shallow views (see
|
||||||
@@ -204,16 +166,11 @@ fn step_f64(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
FractalKind::Lambda => {
|
FractalKind::Lambda => {
|
||||||
// λ·z(1 - z) (+ c on the parameter plane).
|
// λ·z(1 - z) + c.
|
||||||
let (lr, li) = k.l;
|
let (lr, li) = k.l;
|
||||||
let (re2, im2) = (1.0 - zr, -zi);
|
let (re2, im2) = (1.0 - zr, -zi);
|
||||||
let (lzr, lzi) = (lr * zr - li * zi, lr * zi + li * zr);
|
let (lzr, lzi) = (lr * zr - li * zi, lr * zi + li * zr);
|
||||||
let (re, im) = (lzr * re2 - lzi * im2, re2 * lzi + lzr * im2);
|
(lzr * re2 - lzi * im2 + cr, re2 * lzi + lzr * im2 + ci)
|
||||||
if k.set_plane {
|
|
||||||
(re + cr, im + ci)
|
|
||||||
} else {
|
|
||||||
(re, im)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FractalKind::ComplexMultibrot => {
|
FractalKind::ComplexMultibrot => {
|
||||||
let (pr, pi) = complex_pow_complex_f64(zr, zi, k.cpow.0, k.cpow.1);
|
let (pr, pi) = complex_pow_complex_f64(zr, zi, k.cpow.0, k.cpow.1);
|
||||||
@@ -250,9 +207,6 @@ struct StepConsts {
|
|||||||
cpow_im: Big,
|
cpow_im: Big,
|
||||||
power: u32,
|
power: u32,
|
||||||
precision: usize,
|
precision: usize,
|
||||||
/// Parameter plane: the GPU adds `dc` every step for every kind, so the
|
|
||||||
/// Lambda map (which has no `c` of its own) is `λ·z(1 - z) + c` there.
|
|
||||||
set_plane: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`compute_reference`] at arbitrary precision (`FBig`), for deep views.
|
/// [`compute_reference`] at arbitrary precision (`FBig`), for deep views.
|
||||||
@@ -369,18 +323,14 @@ fn step(
|
|||||||
(re2 + cr + pzr, im2 + ci + pzi)
|
(re2 + cr + pzr, im2 + ci + pzi)
|
||||||
}
|
}
|
||||||
FractalKind::Lambda => {
|
FractalKind::Lambda => {
|
||||||
// λ·z(1 - z): logistic map (+ c on the parameter plane).
|
// λ·z(1 - z) + c: logistic map plus the usual additive `c`.
|
||||||
let re2 = 1 - zr;
|
let re2 = 1 - zr;
|
||||||
let im2 = -zi;
|
let im2 = -zi;
|
||||||
let lzr = &k.lr * zr - &k.li * zi;
|
let lzr = &k.lr * zr - &k.li * zi;
|
||||||
let lzi = &k.lr * zi + &k.li * zr;
|
let lzi = &k.lr * zi + &k.li * zr;
|
||||||
let re = &lzr * &re2 - &lzi * &im2;
|
let re = &lzr * &re2 - &lzi * &im2;
|
||||||
let im = re2 * lzi + lzr * im2;
|
let im = re2 * lzi + lzr * im2;
|
||||||
if k.set_plane {
|
(re + cr, im + ci)
|
||||||
(re + cr, im + ci)
|
|
||||||
} else {
|
|
||||||
(re, im)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FractalKind::ComplexMultibrot => {
|
FractalKind::ComplexMultibrot => {
|
||||||
let (pr, pi) = complex_pow_complex(zr, zi, &k.cpow_re, &k.cpow_im, k.precision);
|
let (pr, pi) = complex_pow_complex(zr, zi, &k.cpow_re, &k.cpow_im, k.precision);
|
||||||
@@ -455,7 +405,7 @@ pub fn compute_set_reference(
|
|||||||
morph: Option<(FractalKind, f64)>,
|
morph: Option<(FractalKind, f64)>,
|
||||||
) -> Vec<[f32; 2]> {
|
) -> Vec<[f32; 2]> {
|
||||||
let zero = big_zero(precision);
|
let zero = big_zero(precision);
|
||||||
compute_reference_inner(
|
compute_reference(
|
||||||
&zero,
|
&zero,
|
||||||
&zero,
|
&zero,
|
||||||
center_re,
|
center_re,
|
||||||
@@ -468,7 +418,6 @@ pub fn compute_set_reference(
|
|||||||
lambda_l,
|
lambda_l,
|
||||||
complex_power,
|
complex_power,
|
||||||
morph,
|
morph,
|
||||||
true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,6 +629,38 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lambda Julia orbit adds the Julia `c`: `z -> λ·z(1 - z) + c`.
|
||||||
|
#[test]
|
||||||
|
fn lambda_julia_reference_matches_naive_f64() {
|
||||||
|
let (lr, li) = (-0.5_f64, 0.2_f64);
|
||||||
|
let (cr, ci) = (0.1_f64, -0.3_f64);
|
||||||
|
let points = compute_reference(
|
||||||
|
&Big::try_from(0.2_f64).unwrap(),
|
||||||
|
&Big::try_from(0.1_f64).unwrap(),
|
||||||
|
&Big::try_from(cr).unwrap(),
|
||||||
|
&Big::try_from(ci).unwrap(),
|
||||||
|
60,
|
||||||
|
200,
|
||||||
|
FractalKind::Lambda,
|
||||||
|
2,
|
||||||
|
(0.0, 0.0),
|
||||||
|
(lr, li),
|
||||||
|
(0.0, 0.0),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (mut zr, mut zi) = (0.2_f64, 0.1_f64);
|
||||||
|
for point in &points {
|
||||||
|
let tol = 1e-4 * (1.0 + zr.abs().max(zi.abs()));
|
||||||
|
assert!((point[0] as f64 - zr).abs() < tol, "{point:?} vs {zr}");
|
||||||
|
assert!((point[1] as f64 - zi).abs() < tol, "{point:?} vs {zi}");
|
||||||
|
let (lzr, lzi) = (lr * zr - li * zi, lr * zi + li * zr);
|
||||||
|
let (ar, ai) = (1.0 - zr, -zi);
|
||||||
|
zr = lzr * ar - lzi * ai + cr;
|
||||||
|
zi = lzr * ai + lzi * ar + ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Celtic reference matches a naive f64 iteration: real = |x^2 - y^2| + cr.
|
/// Celtic reference matches a naive f64 iteration: real = |x^2 - y^2| + cr.
|
||||||
#[test]
|
#[test]
|
||||||
fn celtic_reference_matches_naive_f64() {
|
fn celtic_reference_matches_naive_f64() {
|
||||||
|
|||||||
Reference in New Issue
Block a user