

use nannou::prelude::*;
const GRID_SIZE: usize = 100; // 50x50 grid
const PARTICLE_SPACING: f32 = 10.0; // Particle spacing (adjusted for screen size)
const VOID_RADIUS: f32 = 100.0; // Void area radius (around mouse)
const REPULSION_FORCE: f32 = 2.0; // Repulsion force strength
const DAMPING: f32 = 0.95; // Velocity damping (to slow down particles)
struct Particle {
position: Vec2,
velocity: Vec2,
home_position: Vec2, // Initial position (for reset)
}
struct Model {
particles: Vec<Particle>,
window_rect: Rect,
}
fn main() {
nannou::app(model)
.update(update)
.event(event) // Add event handling for key input
.run();
}
fn model(app: &App) -> Model {
let window = app.new_window().size(800, 800).view(view).build().unwrap();
let window_rect = app.window(window).unwrap().rect();
let mut particles = Vec::new();
let start_x = - (GRID_SIZE as f32 / 2.0) * PARTICLE_SPACING;
let start_y = - (GRID_SIZE as f32 / 2.0) * PARTICLE_SPACING;
for i in 0..GRID_SIZE {
for j in 0..GRID_SIZE {
let pos_x = start_x + i as f32 * PARTICLE_SPACING;
let pos_y = start_y + j as f32 * PARTICLE_SPACING;
let pos = vec2(pos_x, pos_y);
particles.push(Particle {
position: pos,
velocity: vec2(0.0, 0.0),
home_position: pos,
});
}
}
Model {
particles,
window_rect,
}
}
fn update(app: &App, model: &mut Model, update: Update) {
let mouse = app.mouse.position();
let dt = update.since_last.as_secs_f32();
for p in model.particles.iter_mut() {
let to_mouse = mouse - p.position;
let dist = to_mouse.length(); // Use length() instead of magnitude()
if dist < VOID_RADIUS && dist > 0.0 {
// Void effect: calculate direction to flee from mouse
let repulsion_dir = -to_mouse.normalize(); // Opposite direction
let force = repulsion_dir * (REPULSION_FORCE * (1.0 - dist / VOID_RADIUS)); // Force based on distance
p.velocity += force;
}
// Pull slightly toward home position (stabilization)
let to_home = p.home_position - p.position;
p.velocity += to_home * 0.01;
// Apply velocity and damping
p.position += p.velocity * dt * 60.0; // Frame-rate independent
p.velocity *= DAMPING;
// Clamp position to window boundaries (optional: keep particles on screen)
p.position = p.position.clamp(model.window_rect.bottom_left(), model.window_rect.top_right());
}
}
fn event(_app: &App, model: &mut Model, event: Event) {
if let Event::WindowEvent {
id: _,
simple: Some(WindowEvent::KeyPressed(Key::N)),
} = event {
// Reset particles to home position when 'n' is pressed
for p in model.particles.iter_mut() {
p.position = p.home_position;
p.velocity = vec2(0.0, 0.0);
}
}
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(BLACK); // Black background
for p in &model.particles {
draw.ellipse()
.xy(p.position)
.radius(2.0) // Small white dots
.color(WHITE);
}
draw.to_frame(app, &frame).unwrap();
}
50x50 ๊ทธ๋ฆฌ๋์ ์ ์(์์ ํฐ ์ )๋ค์ ๋ธ๋ ๋ฐฐ๊ฒฝ์ ๋ฐฐ์นํ๊ณ , ๋ง์ฐ์ค ์์ง์์ ๋ฐ๋ผ "void" ํจ๊ณผ(๋ง์ฐ์ค ์ฃผ์ ๋ฐ๊ฒฝ ๋ด ์ ์๋ค์ด ๋๋ง๊ฐ)๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ตฌํํฉ๋๋ค. 'n' ํค๋ฅผ ๋๋ฅด๋ฉด ๋ชจ๋ ์ ์๊ฐ ์ด๊ธฐ ๊ทธ๋ฆฌ๋ ์์น๋ก ๋ฆฌ์ ๋ฉ๋๋ค.