
๐ Rust Code
use nannou::prelude::*;
const CANVAS_SIZE: u32 = 1000;
const GRAVITY_ACCEL_PIXELS: f32 = 30.0;
const INITIAL_RADIUS: f32 = 1.5;
const SPAWN_INTERVAL: f32 = 0.0;
const MAX_CIRCLES: usize = 1000;
struct Drop {
position: Point2,
velocity: f32,
radius: f32,
color: Hsla,
is_active: bool,
}
impl Drop {
fn activate(canvas_w: f32) -> Self {
let x = random_range(-canvas_w / 2.0, canvas_w / 2.0);
let y = canvas_w / 2.0;
Drop {
position: pt2(x, y),
velocity: 0.0,
radius: INITIAL_RADIUS,
color: hsla(random(), 0.8, 0.5, 1.0),
is_active: true,
}
}
fn update(&mut self, dt: f32, canvas_h: f32) {
if !self.is_active {
return;
}
self.velocity += GRAVITY_ACCEL_PIXELS * dt;
self.position.y -= self.velocity * dt;
if self.position.y < -canvas_h / 2.0 - self.radius {
self.is_active = false;
}
}
fn draw(&self, draw: &Draw) {
if !self.is_active {
return;
}
draw.ellipse().xy(self.position).radius(self.radius).color(self.color);
}
}
struct Model {
drops: Vec<Drop>,
last_spawn_time: f32,
canvas_w: f32,
canvas_h: f32,
}
impl Model {
fn find_inactive_drop(&mut self) -> Option<&mut Drop> {
self.drops.iter_mut().find(|d| !d.is_active)
}
fn spawn_drop(&mut self, time: f32) {
if time - self.last_spawn_time >= SPAWN_INTERVAL {
let canvas_width = self.canvas_w;
if let Some(drop) = self.find_inactive_drop() {
*drop = Drop::activate(canvas_width);
} else if self.drops.len() < MAX_CIRCLES {
self.drops.push(Drop::activate(canvas_width));
}
self.last_spawn_time = time;
}
}
}
fn model(app: &App) -> Model {
app.new_window()
.size(CANVAS_SIZE, CANVAS_SIZE)
.title("Rain in Hue")
.view(view)
.build()
.unwrap();
let rect = app.window_rect();
let drops = Vec::with_capacity(MAX_CIRCLES);
Model {
drops,
last_spawn_time: 0.0,
canvas_w: rect.w(),
canvas_h: rect.h(),
}
}
fn update(app: &App, model: &mut Model, update: Update) {
let time = app.time;
let dt = update.since_last.as_secs_f32();
model.spawn_drop(time);
for drop in &mut model.drops {
drop.update(dt, model.canvas_h);
}
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
if frame.nth() == 0 {
draw.background().color(hsla(0.0, 0.0, 0.01, 1.0));
} else {
draw.rect().wh(app.window_rect().wh()).color(hsla(0.0, 0.0, 0.01, 0.01));
}
for drop in model.drops.iter().filter(|d| d.is_active) {
drop.draw(&draw);
}
draw.to_frame(app, &frame).unwrap();
}
fn main() {
nannou::app(model).update(update).run();
}