use crate::define_param;
use crate::pattern::params::{ParamType, PatternParam};
use std::any::Any;
use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub enum PlasmaBlendMode {
Additive,
Multiply,
Maximum,
}
impl Default for PlasmaBlendMode {
fn default() -> Self {
Self::Additive
}
}
define_param!(num Plasma, ComplexityParam, "complexity", "Number of sine wave components", 1.0, 10.0, 3.0);
define_param!(num Plasma, ScaleParam, "scale", "Scale of the effect", 0.1, 5.0, 1.0);
define_param!(num Plasma, FrequencyParam, "frequency", "Animation speed", 0.1, 10.0, 1.0);
define_param!(enum Plasma, BlendModeParam, "blend_mode", "Color blending mode", &["add", "multiply", "max"], "add");
#[derive(Debug, Clone)]
pub struct PlasmaParams {
pub complexity: f64,
pub scale: f64,
pub frequency: f64,
pub blend_mode: PlasmaBlendMode,
}
impl PlasmaParams {
const COMPLEXITY_PARAM: PlasmaComplexityParam = PlasmaComplexityParam;
const SCALE_PARAM: PlasmaScaleParam = PlasmaScaleParam;
const FREQUENCY_PARAM: PlasmaFrequencyParam = PlasmaFrequencyParam;
const BLEND_MODE_PARAM: PlasmaBlendModeParam = PlasmaBlendModeParam;
}
impl Default for PlasmaParams {
fn default() -> Self {
Self {
complexity: 3.0,
scale: 1.0,
frequency: 1.0,
blend_mode: PlasmaBlendMode::default(),
}
}
}
define_param!(validate PlasmaParams,
COMPLEXITY_PARAM: PlasmaComplexityParam,
SCALE_PARAM: PlasmaScaleParam,
FREQUENCY_PARAM: PlasmaFrequencyParam,
BLEND_MODE_PARAM: PlasmaBlendModeParam
);
impl PatternParam for PlasmaParams {
fn name(&self) -> &'static str {
"plasma"
}
fn description(&self) -> &'static str {
"Psychedelic plasma effect with multiple wave components"
}
fn param_type(&self) -> ParamType {
ParamType::Composite
}
fn default_value(&self) -> String {
format!(
"complexity={},scale={},frequency={},blend_mode={}",
self.complexity,
self.scale,
self.frequency,
match self.blend_mode {
PlasmaBlendMode::Additive => "add",
PlasmaBlendMode::Multiply => "multiply",
PlasmaBlendMode::Maximum => "max",
}
)
}
fn validate(&self, value: &str) -> Result<(), String> {
self.validate_params(value)
}
fn parse(&self, value: &str) -> Result<Box<dyn PatternParam>, String> {
let mut params = PlasmaParams::default();
for part in value.split(',') {
let kv: Vec<&str> = part.split('=').collect();
if kv.len() != 2 {
continue;
}
match kv[0] {
"complexity" => {
Self::COMPLEXITY_PARAM.validate(kv[1])?;
params.complexity = kv[1].parse().unwrap();
}
"scale" => {
Self::SCALE_PARAM.validate(kv[1])?;
params.scale = kv[1].parse().unwrap();
}
"frequency" => {
Self::FREQUENCY_PARAM.validate(kv[1])?;
params.frequency = kv[1].parse().unwrap();
}
"blend_mode" => {
Self::BLEND_MODE_PARAM.validate(kv[1])?;
params.blend_mode = match kv[1] {
"add" => PlasmaBlendMode::Additive,
"multiply" => PlasmaBlendMode::Multiply,
"max" => PlasmaBlendMode::Maximum,
_ => return Err("Invalid blend mode".to_string()),
};
}
invalid_param => {
return Err(format!("Invalid parameter name: {}", invalid_param));
}
}
}
Ok(Box::new(params))
}
fn sub_params(&self) -> Vec<Box<dyn PatternParam>> {
vec![
Box::new(Self::COMPLEXITY_PARAM),
Box::new(Self::SCALE_PARAM),
Box::new(Self::FREQUENCY_PARAM),
Box::new(Self::BLEND_MODE_PARAM),
]
}
fn clone_param(&self) -> Box<dyn PatternParam> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl super::Patterns {
#[inline]
pub fn plasma(&self, x_norm: f64, y_norm: f64, params: PlasmaParams) -> f64 {
let time = self.time * PI;
let x_pos = x_norm + 0.5;
let y_pos = y_norm + 0.5;
let base_freq = params.frequency * params.scale * 2.0;
let time_sin04 = self.utils.fast_sin(time * 0.4);
let time_cos043 = self.utils.fast_cos(time * 0.43);
let cx = 0.5 + 0.3 * time_sin04;
let cy = 0.5 + 0.3 * time_cos043;
let dx1 = x_pos - cx;
let dy1 = y_pos - cy;
let dist1 = (dx1 * dx1 + dy1 * dy1).sqrt();
let mut sum = 0.0;
let mut divisor = 0.0;
sum += self.utils.fast_sin(dist1 * 6.0 * base_freq + time * 0.6) * 0.8;
divisor += 0.8;
let x_freq = x_pos * 5.0 * base_freq;
let y_freq = y_pos * 5.0 * base_freq;
sum += self.utils.fast_sin(x_freq + time * 0.4) * 1.2
+ self.utils.fast_sin(y_freq + time * 0.47) * 1.2;
divisor += 2.4;
let angle = time * 0.2;
let (sin_angle, cos_angle) = (self.utils.fast_sin(angle), self.utils.fast_cos(angle));
let rx = x_pos * cos_angle - y_pos * sin_angle;
let ry = x_pos * sin_angle + y_pos * cos_angle;
sum += self.utils.fast_sin((rx + ry) * 4.0 * base_freq) * 1.4;
divisor += 1.4;
sum += self
.utils
.fast_sin((x_pos + y_pos) * 4.0 * base_freq + time * 0.3)
* 1.0
+ self
.utils
.fast_sin((x_pos - y_pos) * 4.0 * base_freq + time * 0.35)
* 1.0;
divisor += 2.0;
let complexity = params.complexity as u32;
if complexity > 0 {
let mut fi = 0.0;
for _ in 0..complexity {
let speed = 0.2 + fi * 0.04;
let cx = 0.5 + 0.4 * self.utils.fast_sin(time * speed);
let cy = 0.5 + 0.4 * self.utils.fast_cos(time * speed + PI * 0.3);
let dx = x_pos - cx;
let dy = y_pos - cy;
let dist = (dx * dx + dy * dy).sqrt();
let freq = (2.5 + fi) * base_freq; let weight = 1.0 / (fi + 1.0);
sum += self.utils.fast_sin(dist * freq + time * (0.4 + fi * 0.1)) * weight;
divisor += weight;
fi += 1.0;
}
}
let normalized = (sum / divisor) * 1.1;
(self.utils.fast_sin(normalized * PI * 0.8) + 1.0) * 0.5
}
}