wallswitch 0.60.8

randomly selects wallpapers for multiple monitors
Documentation
//! Newton-Raphson Basin fractal generator overlay.
//!
//! This module implements the Relaxed Newton-Raphson fractal for solving complex polynomial
//! equations of the form z^p - 1 = 0. By introducing a complex relaxation parameter (lambda),
//! the standard basins of attraction warp into intricate, symmetric, mandala-like
//! structures featuring nested circular bands, spiral arms, and crystalline structures.

use crate::effects::{
    MAX_ITERATIONS, MIN_ITERATIONS, RelaxedEscape, calculate_circular_fade,
    color_relaxed_newton_fractal, find_optimal_framing, render_fractal_parallel,
};
use crate::{Complex, ImageEffect, NEON_PALETTES, NeonColor, get_random_integer};
use image::RgbImage;

/// Valid operational range for randomized zoom viewport allocation.
const ZOOM_RANGE: [f64; 2] = [1.5, 3.8];

/// Structural parameters for polynomial evaluation and deformation.
///
/// Holds the power p of the equation z^p - 1 = 0, the relaxation parameter lambda,
/// and a user-friendly preset name.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NewtonPreset {
    /// Power of the polynomial.
    pub power: u32,
    /// Complex relaxation scale parameter.
    pub lambda: Complex,
    /// Human-readable name of the specific layout.
    pub name: &'static str,
}

/// A procedural generator for rendering Newton-Raphson Basin fractals onto backgrounds.
pub struct NewtonGenerator {
    /// Active parameters governing formula geometry.
    pub preset: NewtonPreset,
    /// Maximum scan operations limits.
    pub scan_iterations: u32,
    /// Neon visual coloring profiles.
    pub color_palette: NeonColor,
    /// Viewport translation zoom index.
    pub zoom: f64,
    /// Cosine of active viewport rotation angle.
    pub cos_angle: f64,
    /// Sine of active viewport rotation angle.
    pub sin_angle: f64,
}

impl Default for NewtonGenerator {
    fn default() -> Self {
        Self {
            preset: NewtonPreset {
                power: 3,
                lambda: Complex::new(1.0, 0.3),
                name: "Gothic Rose Mandala",
            },
            scan_iterations: get_random_integer::<_, u32>(MIN_ITERATIONS / 10, MAX_ITERATIONS / 10)
                .max(60),
            color_palette: NEON_PALETTES[0],
            zoom: 2.0,
            cos_angle: 1.0,
            sin_angle: 0.0,
        }
    }
}

impl ImageEffect for NewtonGenerator {
    /// Applies the Newton-Raphson Basin fractal overlay directly onto an in-memory image buffer.
    fn apply(&self, rgb_img: &mut RgbImage) {
        let scan_iterations = self.scan_iterations;
        let power = self.preset.power;
        let lambda = self.preset.lambda;
        let color_palette = self.color_palette;

        let (width, height) = rgb_img.dimensions();
        let w_f = width as f64;
        let h_f = height as f64;
        let aspect_ratio = w_f.max(h_f) / w_f.min(h_f);

        // Dynamically scale the edge fade radius to cover 98% of the maximum
        // viewport extent in complex coordinates.
        let max_radius = 0.98 * 0.5 * self.zoom * aspect_ratio;

        render_fractal_parallel(
            rgb_img,
            self.zoom,
            self.cos_angle,
            self.sin_angle,
            Complex::new(0.0, 0.0),
            true,
            move |z_init, _scale| {
                let (i, diff_norm, z_final) =
                    compute_newton_escape(z_init, power, lambda, scan_iterations);

                // Circular edge fade using the dynamically calculated maximum radius.
                // Core remains constant up to 40% of this radius, fading to 0.0 at the boundary.
                let edge_fade = calculate_circular_fade(z_init, max_radius as f32, 0.40);

                let escape_state = RelaxedEscape {
                    iterations: i,
                    max_iterations: scan_iterations,
                    diff_norm,
                    z_final,
                };

                color_relaxed_newton_fractal(
                    &escape_state,
                    color_palette,
                    edge_fade,
                    (1e-6_f64).ln() as f32,
                    false,
                )
            },
        );
    }

    /// Returns a formatted string containing specific diagnostic details of the active effect.
    fn info(&self) -> String {
        format!(
            "fractal [{}]\n\
            f(z) = z^{} - 1 = 0, where lambda = {:5.2} {} {:4.2}i (iter = {:4}, zoom = {:.2}), color: {}",
            self.preset.name,
            self.preset.power,
            self.preset.lambda.re,
            if self.preset.lambda.im >= 0.0 {
                "+"
            } else {
                "-"
            },
            self.preset.lambda.im.abs(),
            self.scan_iterations,
            self.zoom,
            self.color_palette
        )
    }
}

impl NewtonGenerator {
    /// Generates a randomized Newton Basin configuration fitted to the aspect ratio.
    pub fn random(monitor: &crate::Monitor) -> Self {
        let width = monitor.resolution.width as u32;
        let height = monitor.resolution.height as u32;

        let presets = [
            // --- Classical Geometric Patterns ---
            NewtonPreset {
                power: 3,
                lambda: Complex::new(1.0, 0.3),
                name: "Gothic Rose Mandala",
            },
            NewtonPreset {
                power: 5,
                lambda: Complex::new(0.9, 0.1),
                name: "Imperial Star Compass",
            },
            NewtonPreset {
                power: 4,
                lambda: Complex::new(1.0, 0.0),
                name: "Stained Glass Kaleidoscope",
            },
            NewtonPreset {
                power: 6,
                lambda: Complex::new(0.85, 0.2),
                name: "Cosmic Snowflake Grid",
            },
            NewtonPreset {
                power: 3,
                lambda: Complex::new(1.35, 0.0),
                name: "Spiked Crown of Thorns",
            },
            NewtonPreset {
                power: 8,
                lambda: Complex::new(0.7, 0.4),
                name: "Quantum Energy Shells",
            },
            NewtonPreset {
                power: 5,
                lambda: Complex::new(1.1, 0.25),
                name: "Solar Flare Compass",
            },
            NewtonPreset {
                power: 3,
                lambda: Complex::new(0.8, 0.5),
                name: "Celtic Knotwork Ribbon",
            },
            NewtonPreset {
                power: 4,
                lambda: Complex::new(0.6, 0.6),
                name: "Nautilus Spiral Chamber",
            },
            NewtonPreset {
                power: 7,
                lambda: Complex::new(1.0, 0.05),
                name: "Hyper-Dimensional Matrix",
            },
            // --- Twisted & High-Deformation Wave Mandalas ---
            NewtonPreset {
                power: 6,
                lambda: Complex::new(1.15, 0.15),
                name: "Aetheric Frost Flower",
            },
            NewtonPreset {
                power: 8,
                lambda: Complex::new(0.90, 0.30),
                name: "Celestial Gearwork",
            },
            NewtonPreset {
                power: 3,
                lambda: Complex::new(0.75, 0.60),
                name: "Byzantine Dome",
            },
            NewtonPreset {
                power: 5,
                lambda: Complex::new(1.25, -0.20),
                name: "Abyssal Starfish",
            },
            NewtonPreset {
                power: 4,
                lambda: Complex::new(0.80, 0.45),
                name: "Hyperborean Sigil",
            },
            NewtonPreset {
                power: 7,
                lambda: Complex::new(1.0, -0.30),
                name: "Prismatic Labyrinth",
            },
            NewtonPreset {
                power: 3,
                lambda: Complex::new(0.95, 0.80),
                name: "Nebula Core Spiral",
            },
            NewtonPreset {
                power: 5,
                lambda: Complex::new(0.60, 0.80),
                name: "Aura Borealis Compass",
            },
            NewtonPreset {
                power: 10,
                lambda: Complex::new(0.85, 0.0),
                name: "Obsidian Glass Lattices",
            },
            NewtonPreset {
                power: 4,
                lambda: Complex::new(1.40, -0.40),
                name: "Bio-Polymer Filament",
            },
        ];

        let p_idx: usize = get_random_integer(0, NEON_PALETTES.len() - 1);
        let color_palette = NEON_PALETTES[p_idx];

        let angle_degrees: f64 = get_random_integer(0, 359);
        let radians = angle_degrees.to_radians();

        let preset_idx: usize = get_random_integer(0, presets.len() - 1);
        let selected_preset = presets[preset_idx];

        let mut newton = Self {
            preset: selected_preset,
            scan_iterations: get_random_integer(40, 100),
            color_palette,
            zoom: 2.0,
            cos_angle: radians.cos(),
            sin_angle: radians.sin(),
        };

        newton.optimize_fit(width, height);
        newton
    }

    /// Automatically scales the viewport boundaries with randomized micro-deviations.
    pub fn optimize_fit(&mut self, width: u32, height: u32) {
        let search_limit = 1.8_f64;
        let steps = 64;
        let inv_steps_minus_1 = 1.0 / (steps - 1) as f64;
        let range = 2.0 * search_limit;

        let scan_iterations = self.scan_iterations;
        let mut active_points = Vec::with_capacity(steps * steps);

        for step_y in 0..steps {
            let ry = -search_limit + (step_y as f64 * inv_steps_minus_1) * range;
            for step_x in 0..steps {
                let rx = -search_limit + (step_x as f64 * inv_steps_minus_1) * range;
                let z = Complex::new(rx, ry);

                let (i, _, _) = compute_newton_escape(
                    z,
                    self.preset.power,
                    self.preset.lambda,
                    scan_iterations,
                );

                if i > 2 && i < scan_iterations - 2 {
                    active_points.push(z);
                }
            }
        }

        if !active_points.is_empty() {
            let (best_zoom, best_cos, best_sin) = find_optimal_framing(
                &active_points,
                width,
                height,
                self.cos_angle,
                self.sin_angle,
            );

            // Introduce a randomized scaling multiplier between 95% and 125%
            let rand_factor = get_random_integer::<_, f64>(95, 125) / 100.0;
            self.zoom = (best_zoom * rand_factor).clamp(ZOOM_RANGE[0], ZOOM_RANGE[1]);
            self.cos_angle = best_cos;
            self.sin_angle = best_sin;
        } else {
            // Roll a flat random zoom within the bounded range if no points detected
            let flat_rand = get_random_integer::<_, f64>(150, 250) / 100.0;
            self.zoom = flat_rand.clamp(ZOOM_RANGE[0], ZOOM_RANGE[1]);
        }
    }
}

/// Evaluates polynomial convergence under a relaxed Newton-Raphson iteration loop.
#[inline(always)]
fn compute_newton_escape(
    z_init: Complex,
    power: u32,
    lambda: Complex,
    scan_iterations: u32,
) -> (u32, f64, Complex) {
    let mut z = z_init;
    let p_f = power as f64;

    let mut i = 0;
    let mut diff_norm = 1.0;

    while i < scan_iterations {
        if z.norm_sq() < 1e-8 {
            break;
        }

        let z_prev_p_minus_1 = z.pow(power - 1);
        let z_p = z_prev_p_minus_1 * z;

        let f_z = z_p - Complex::new(1.0, 0.0);
        let f_prime_z = z_prev_p_minus_1 * p_f;

        let step = lambda * (f_z / f_prime_z);
        let z_next = z - step;

        diff_norm = step.norm_sq();

        if diff_norm < 1e-6 {
            z = z_next;
            break;
        }

        z = z_next;
        i += 1;
    }

    (i, diff_norm, z)
}

#[cfg(test)]
mod tests_newton {
    use super::*;
    use crate::core::Monitor;

    #[test]
    fn test_newton_generation_random() {
        let monitor = Monitor::default();
        let newton = NewtonGenerator::random(&monitor);
        assert!(newton.zoom > 0.0);
        assert!(newton.scan_iterations > 0);
    }
}