use std::f64::consts::PI;
use crate::{
ColorRGB, Complex, FractalConfig, FractalDescriptor, FractalPreset, MAX_ITERATIONS,
MIN_ITERATIONS, Monitor, NEON_PALETTES, ProceduralEffect, RandomExt, color_distance_estimator,
get_random_integer, julia_escape, optimize_fractal_viewport, rand_f64,
};
pub struct JuliaGenerator {
pub preset: FractalPreset,
pub config: FractalConfig,
}
impl Default for JuliaGenerator {
fn default() -> Self {
Self {
preset: FractalPreset {
center: Complex::new(-0.7, 0.27015),
fractal_name: "Classic denderite",
effect_name: ProceduralEffect::JuliaSet,
},
config: FractalConfig {
scan_iterations: get_random_integer(MIN_ITERATIONS, MAX_ITERATIONS),
color_palette: NEON_PALETTES[5],
zoom: 3.0,
rotation: Complex::one(),
},
}
}
}
impl FractalDescriptor for JuliaGenerator {
#[inline(always)]
fn config(&self) -> &FractalConfig {
&self.config
}
#[inline(always)]
fn center(&self) -> Complex {
self.preset.center
}
#[inline(always)]
fn is_julia(&self) -> bool {
true
}
#[inline(always)]
fn render_pixel(&self, z_init: Complex, scale: f64, _max_radius: f64) -> (ColorRGB, f64, f64) {
let (i, z, dz) = julia_escape(z_init, self.preset.center, self.config.scan_iterations);
color_distance_estimator(
i,
self.config.scan_iterations,
z,
dz,
scale,
self.config.color_palette,
)
}
fn info_text(&self) -> String {
format!(
"fractal [{}]\n\
f(z) = z^2 + c, where c = {:8.5} {:+7.5}i (iter = {:4}, zoom = {:.2}), color: {}",
self.preset.fractal_name,
self.preset.center.re,
self.preset.center.im,
self.config.scan_iterations,
self.config.zoom,
self.config.color_palette
)
}
}
impl JuliaGenerator {
pub fn random(monitor: &Monitor) -> Self {
let width = monitor.resolution.width as u32;
let height = monitor.resolution.height as u32;
let presets = [
FractalPreset {
center: Complex::new(-0.4, 0.6),
fractal_name: "Classic cloud swirls",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.8, 0.156),
fractal_name: "Detailed spirals",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.7269, 0.1889),
fractal_name: "Lace structures",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.75, 0.11),
fractal_name: "Feathery branches",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.1, 0.651),
fractal_name: "Cosmic dust style",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(0.355, 0.355),
fractal_name: "Spiral galaxy arms",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.4, -0.59),
fractal_name: "Swirling vortexes",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.54, 0.54),
fractal_name: "Ornamental lace borders",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.835, -0.2321),
fractal_name: "Lightning rods",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.77269, 0.12428),
fractal_name: "Coral reefs",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.51251, 0.5213),
fractal_name: "Fine lace filaments",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.55, 0.55),
fractal_name: "Intricate leaf outlines",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.624, 0.435),
fractal_name: "Crystalline snowflake patterns",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.12, 0.85),
fractal_name: "Flowing plasma plumes",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.391, -0.587),
fractal_name: "Swirling storm clouds",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.73, 0.21),
fractal_name: "Feathery dendritic lace",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.81, 0.2),
fractal_name: "Spiral galaxy filaments",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.68, 0.34),
fractal_name: "Delicate coral spirals",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.76, 0.08),
fractal_name: "Lightning tree branches",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(0.285, 0.01),
fractal_name: "Cosmic galaxy vortex swirls",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.8, 0.17),
fractal_name: "Spidery lace denderites",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.7269, -0.1889),
fractal_name: "Conjugate lace structures",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.835, 0.2321),
fractal_name: "Conjugate lightning rods",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.75, 0.05),
fractal_name: "Dense branching coral reef",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.70176, 0.3842),
fractal_name: "Conjugate dragon-like curves",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.8, 0.16),
fractal_name: "Deep sea coral spirals",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.722, 0.246),
fractal_name: "Dendritic pine branch variation",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.11, 0.655),
fractal_name: "Triple helix rotational cosmic swirls",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.52519, 0.5215),
fractal_name: "Intertwined Gothic Cathedral window arches",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(0.28, 0.008),
fractal_name: "Centrifugal pinwheel galaxy vortex generator",
effect_name: ProceduralEffect::JuliaSet,
},
FractalPreset {
center: Complex::new(-0.83, -0.232),
fractal_name: "Sharp crystalline glacial ice column needles",
effect_name: ProceduralEffect::JuliaSet,
},
];
let selected_preset = presets.choose().copied().unwrap_or(presets[0]);
let color_palette = NEON_PALETTES.choose().copied().unwrap_or(NEON_PALETTES[0]);
let radians = rand_f64() * 2.0 * PI;
let mut julia = Self {
preset: selected_preset,
config: FractalConfig {
scan_iterations: get_random_integer(MIN_ITERATIONS, MAX_ITERATIONS),
color_palette,
zoom: 3.0,
rotation: Complex::from_polar(1.0, radians),
},
};
julia.optimize_fit(width, height);
julia
}
pub fn optimize_fit(&mut self, width: u32, height: u32) {
let c_abs = self.preset.center.abs();
let r_bound = (1.0 + (1.0 + 4.0 * c_abs).sqrt()) / 2.0;
let search_limit = r_bound * 1.2;
let steps = 128;
let scan_iterations = self.config.scan_iterations;
let center = self.preset.center;
let (best_zoom, best_rotation) = optimize_fractal_viewport(
width,
height,
search_limit,
steps,
self.config.rotation,
|z| {
let (i, _, _) = julia_escape(z, center, scan_iterations);
i > 3 && i < scan_iterations
},
);
let padding_factor = 1.10;
if best_zoom < f64::MAX {
self.config.zoom = best_zoom * padding_factor;
self.config.rotation = best_rotation;
} else {
self.config.zoom = 2.0 * r_bound * padding_factor;
}
}
}
#[cfg(test)]
mod tests_julia {
use super::*;
#[test]
fn test_julia_generation_random() {
let monitor = Monitor::default();
let julia = JuliaGenerator::random(&monitor);
assert!(julia.config.zoom > 0.0);
assert_eq!(julia.preset.effect_name, ProceduralEffect::JuliaSet);
}
}