use rdpe::prelude::*;
#[derive(Particle, Clone)]
struct Particle {
position: Vec3,
velocity: Vec3,
#[color]
color: Vec3,
energy: f32,
phase: f32,
}
fn main() {
Simulation::<Particle>::new()
.with_particle_count(5000)
.with_bounds(1.0)
.with_spatial_config(0.15, 32)
.with_spawner(|ctx| {
let i = ctx.index;
let angle = i as f32 * 0.1;
let radius = 0.3 + (i as f32 * 0.001) % 0.4;
Particle {
position: Vec3::new(
angle.cos() * radius,
ctx.random_range(-0.1, 0.1),
angle.sin() * radius,
),
velocity: Vec3::ZERO,
color: Vec3::new(0.5, 0.7, 1.0),
energy: 1.0,
phase: ctx.random_range(0.0, 6.28),
}
})
.with_rule(Rule::custom_dynamic(r#"
// Pulsing radial force from center
let to_center = -p.position;
let dist = length(to_center);
if dist > 0.01 {
let pulse = sin(uniforms.time * uniforms.pulse_freq + p.phase) * 0.5 + 0.5;
let force = normalize(to_center) * uniforms.radial_strength * pulse;
p.velocity += force * uniforms.delta_time;
}
// Spinning motion
let tangent = normalize(cross(vec3<f32>(0.0, 1.0, 0.0), to_center));
p.velocity += tangent * uniforms.spin_speed * uniforms.delta_time;
"#)
.with_param("pulse_freq", 3.0)
.with_param("radial_strength", 2.0)
.with_param("spin_speed", 1.5)
)
.with_rule(Rule::custom_dynamic(r#"
// Energy-based coloring
let speed = length(p.velocity);
let energy_color = vec3<f32>(
uniforms.color_r + speed * 0.2,
uniforms.color_g * p.energy,
uniforms.color_b * (1.0 - speed * 0.1)
);
p.color = clamp(energy_color, vec3<f32>(0.0), vec3<f32>(1.0));
// Energy decay
p.energy = max(0.1, p.energy - uniforms.energy_decay * uniforms.delta_time);
"#)
.with_param("color_r", 0.4)
.with_param("color_g", 0.8)
.with_param("color_b", 1.0)
.with_param("energy_decay", 0.1)
)
.with_rule(Rule::neighbor_custom_dynamic(r#"
if neighbor_dist < uniforms.interact_radius && neighbor_dist > 0.01 {
// Soft boundary - repel when too close, attract when far
let ideal_dist = uniforms.ideal_spacing;
let diff = neighbor_dist - ideal_dist;
let force_dir = neighbor_dir * sign(diff);
let force_mag = abs(diff) * uniforms.interact_strength;
p.velocity += force_dir * force_mag * uniforms.delta_time;
// Energy transfer on close proximity
if neighbor_dist < ideal_dist * 0.5 {
p.energy = min(1.0, p.energy + uniforms.energy_transfer);
}
}
"#)
.with_param("interact_radius", 0.12)
.with_param("ideal_spacing", 0.05)
.with_param("interact_strength", 3.0)
.with_param("energy_transfer", 0.01)
)
.with_rule(Rule::Drag(1.5))
.with_rule(Rule::SpeedLimit { min: 0.0, max: 2.0 })
.with_rule(Rule::BounceWalls { restitution: 1.0 })
.with_particle_inspector()
.with_rule_inspector()
.run().expect("Simulation failed");
}