

๐ Rust Code
use nannou::noise::{NoiseFn, Perlin};
use nannou::prelude::*;
struct Particle {
position: Vec2,
velocity: Vec2,
history: Vec<Vec2>,
hue: f32,
}
struct Model {
particles: Vec<Particle>,
perlin: Perlin,
time: f64,
frame_count: usize,
}
fn main() {
nannou::app(model).update(update).run();
}
fn model(app: &App) -> Model {
app.new_window()
.size(800, 800)
.view(view)
.build()
.unwrap();
let perlin = Perlin::new();
let particles = spawn_particles(&perlin);
Model {
particles,
perlin,
time: 0.0,
frame_count: 0,
}
}
fn spawn_particles(_perlin: &Perlin) -> Vec<Particle> {
let mut particles = Vec::new();
let center = pt2(0.0, 0.0);
for _ in 0..1000 {
let angle = random_range(0.0, TAU);
let velocity = vec2(angle.cos(), angle.sin()) * random_range(1.0, 3.0);
particles.push(Particle {
position: center,
velocity,
history: Vec::new(),
hue: random_f32(),
});
}
particles
}
fn update(_app: &App, model: &mut Model, _update: Update) {
model.time += 0.0002;
model.frame_count += 1;
if model.frame_count % 300 == 0 {
model.particles = spawn_particles(&model.perlin);
}
for p in model.particles.iter_mut() {
let n = model.perlin.get([
p.position.x as f64 * 0.005,
p.position.y as f64 * 0.005,
model.time,
]);
let angle_offset = map_range(n, -1.0, 1.0, -0.3, 0.3);
let current_angle = p.velocity.angle();
let new_angle = current_angle + angle_offset as f32;
let speed = p.velocity.length() * 1.01;
p.velocity = vec2(new_angle.cos(), new_angle.sin()) * speed;
p.position += p.velocity;
p.history.push(p.position);
if p.history.len() > 25 {
p.history.remove(0);
}
}
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.rect()
.wh(app.window_rect().wh())
.color(srgba(0.0, 0.0, 0.0, 0.2));
for p in &model.particles {
if p.history.len() > 1 {
draw.polyline()
.weight(0.8)
.points(p.history.clone())
.color(hsla(
p.hue,
0.9,
0.6,
0.6,
));
}
}
draw.to_frame(app, &frame).unwrap();
}