//! # Variation use crate::Coefficients2; use bytemuck::{Pod, Zeroable}; use core::f32::consts::PI; use glam::{Affine2, Vec2, vec2}; use libm::{atan2f, cosf, powf, sinf, sqrtf, tanf}; use rand::Rng; use rand::distributions::Standard; #[derive(Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct VariationParams([f32; 4]); #[derive(Copy, Clone)] #[repr(u32)] pub enum VariationKind { Linear = 0, Julia = 13, Popcorn = 17, Pdj = 24, } // UNSAFE: Sound because enum has guaranteed layout (u32) and defined zero-value unsafe impl bytemuck::Zeroable for VariationKind {} // UNSAFE: Sound because enum has guaranteed layout (u32) and defined zero-value unsafe impl bytemuck::Pod for VariationKind {} #[derive(Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct Variation { kind: VariationKind, weight: f32, params: VariationParams, } impl Variation { pub const IDENTITY: Variation = Variation { kind: VariationKind::Linear, weight: 1.0, params: VariationParams([0f32; 4]), }; pub fn new(kind: VariationKind, weight: f32, params: VariationParams) -> Variation { Variation { kind, weight, params, } } pub fn transform_point( &self, point: Vec2, rng: &mut R, coefficients: &Affine2, ) -> Vec2 { (match self.kind { VariationKind::Linear => transform_point_linear(point), VariationKind::Julia => transform_point_julia(point, rng), VariationKind::Popcorn => transform_point_popcorn(point, coefficients), VariationKind::Pdj => transform_point_pdj(point, &self.params), }) * self.weight } } fn transform_point_linear(point: Vec2) -> Vec2 { point } fn transform_point_julia(point: Vec2, rng: &mut R) -> Vec2 { let x2 = powf(point.x, 2.0); let y2 = powf(point.y, 2.0); let r = sqrtf(x2 + y2); let theta = atan2f(point.x, point.y); let omega = if rng.sample::(Standard) > 0.5 { PI } else { 0.0 }; let sqrt_r = sqrtf(r); let theta_val = theta / 2.0 + omega; vec2(sqrt_r * cosf(theta_val), sqrt_r * sinf(theta_val)) } fn transform_point_popcorn(point: Vec2, coefficients: &Affine2) -> Vec2 { vec2( point.x * coefficients.c() * sinf(tanf(3.0 * point.y)), point.y + coefficients.f() * sinf(tanf(3.0 * point.x)), ) } fn transform_point_pdj(point: Vec2, params: &VariationParams) -> Vec2 { let (pdj_a, pdj_b, pdj_c, pdj_d) = (params.0[0], params.0[1], params.0[2], params.0[3]); vec2( sinf(pdj_a * point.y) - cosf(pdj_b * point.x), sinf(pdj_c * point.x) - cosf(pdj_d * point.y), ) }