Files
enkou/enkou-shaders/src/variation.rs
T
2026-06-27 18:25:41 -04:00

102 lines
2.7 KiB
Rust

//! # 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<R: Rng>(
&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<R: Rng>(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::<f32, _>(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),
)
}