


use nannou::prelude::*;
use nannou::rand::{random_f32, random_range};
const CIRCLE_RADIUS: f32 = 0.5; // Global constant for circle radius
fn main() {
nannou::app(model).update(update).run();
}
struct Circle {
position: Point2,
velocity: Vec2,
}
struct Model {
circles: Vec<Circle>,
color: Rgba,
}
fn model(app: &App) -> Model {
let _window = app.new_window()
.size(500, 500)
.view(view)
.build()
.unwrap();
let num_circles = 22000; // Number of circles
let win = app.window_rect();
let half_width = win.w() / 2.0;
let half_height = win.h() / 2.0;
let mut circles = Vec::with_capacity(num_circles);
for _ in 0..num_circles {
let position = pt2(
random_range(-half_width + CIRCLE_RADIUS, half_width - CIRCLE_RADIUS),
random_range(-half_height + CIRCLE_RADIUS, half_height - CIRCLE_RADIUS),
);
let speed = random_range(0.5, 1.0);
let angle = random_f32() * TAU;
let velocity = vec2(angle.cos(), angle.sin()) * speed;
circles.push(Circle { position, velocity });
}
Model {
circles,
color: rgba(1.0, 0.5, 0.75, 1.0), // pink
}
}
fn update(app: &App, model: &mut Model, _update: Update) {
let win = app.window_rect();
let half_width = win.w() / 2.0 - CIRCLE_RADIUS; // Adjust for radius
let half_height = win.h() / 2.0 - CIRCLE_RADIUS;
for circle in &mut model.circles {
circle.position += circle.velocity;
// Bounce off walls
if circle.position.x.abs() > half_width {
circle.velocity.x = -circle.velocity.x;
circle.position.x = circle.position.x.clamp(-half_width, half_width);
}
if circle.position.y.abs() > half_height {
circle.velocity.y = -circle.velocity.y;
circle.position.y = circle.position.y.clamp(-half_height, half_height);
}
}
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
let win = app.window_rect();
// Clear to black only on first frame
if frame.nth() == 0 {
draw.background().color(BLACK);
}
// Draw semi-transparent rect for trail effect
draw.rect()
.wh(win.wh())
.xy(win.xy())
.color(rgba(0.0, 0.0, 0.0, 0.05));
// Draw each circle
for circle in &model.circles {
draw.ellipse()
.xy(circle.position)
.radius(CIRCLE_RADIUS)
.color(model.color);
}
draw.to_frame(app, &frame).unwrap();
}
use nannou::prelude::*;
use nannou::rand::{random_f32, random_range};
prelude::*: ์์ฃผ ์ฐ๋ ํ์ /ํจ์(์: Point2, Vec2, Rgba, TAU ๋ฑ)๋ฅผ ๋ชจ๋ ํฌํจ.random_f32(): 0.0~1.0 ์ฌ์ด์ ๋ถ๋์์์ ๋์.random_range(a, b): a ์ด์ b ๋ฏธ๋ง์ ์ค์ ๋์.
const CIRCLE_RADIUS: f32 = 0.3; // Global constant for circle radius
fn main() {
nannou::app(model).update(update).run();
}
model: ์ด๊ธฐ ์ํ ์์ฑ ํจ์.update: ๋งค ํ๋ ์๋ง๋ค ์ํ ๊ฐฑ์ .run(): ๋ฃจํ ์์ โ ์ฐฝ ์์ฑ, ์ด๋ฒคํธ ์ฒ๋ฆฌ, ๋ ๋๋ง ๋ฐ๋ณต.
struct Circle {
position: Point2,
velocity: Vec2,
}
position: ํ์ฌ ํ๋ฉด ์ขํ (x, y).velocity: ์๋ ๋ฒกํฐ โ ํ๋ ์๋ง๋ค ์ด ๊ฐ์ position์ ๋ํด ์์ง์ ๊ตฌํ.
struct Model {
circle: Vec<Circle>,
color: Rgba.
}
circles: ๋ชจ๋ ์์ ๋ฐฐ์ด (15,000๊ฐ).color: ๋ชจ๋ ์์ ์ ์ฉํ ์์ โ ๋ฐํฌ๋ช ํํฌ.
model() ํจ์fn model(app: &App) -> Model {
let _window = app.new_window()
.size(1000, 1000)
.view(view)
.build()
.unwrape();
์ฑ ์ด๊ธฐํ : 1000x1000 ํฌ๊ธฐ์ ์ฐฝ์ ์์ฑํ๊ณ , view ํจ์๋ฅผ ๋ ๋๋ฌ๋ก ์ง์
_window: ์์ฑ๋ ์ฐฝ์ ํธ๋ค (์ฌ์ฉ ์ ํด์ _ ์ ๋์ฌ)..view(view): ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋ ๊ทธ๋ฆฌ๊ธฐ ํจ์ ์ง์ .
let num_circles = 15000; // Number of circles
let win = app.window_rect();
let half_width = win.w() / 2.0;
let half_height = win.h() / 2.0;
let mut circles = Vec::with_capacity(num_circles);
์ ์ด๊ธฐํ๋ฅผ ์ํ ์ค๋น
win.window_rect(): ์ฐฝ์ ๊ฒฝ๊ณ ์ฌ๊ฐํ ๋ฐํ โ ์ค์ฌ (0,0), ํญ/๋์ด ๊ธฐ์ค.half_width/height: ํ๋ฉด ๋ฐ ๋๋น/๋์ด โ ์์ด ํ๋ฉด ๋ฐ์ผ๋ก ๋๊ฐ์ง ์๋๋ก ์ ํํ๊ธฐ ์ํจ.Vec::with_capacity: ๋ฉ๋ชจ๋ฆฌ ๋ฏธ๋ฆฌ ํ ๋น โ ์ฑ๋ฅ ํฅ์ (๊ถ์ฅ).
for _ in 0..num_circles {
let position = pt2(
random_range(-half_width + CIRCLE_RADIUS, half_width - CIRCLE_RADIUS),
random_range(-half_height + CIRCLE_RADIUS, half_height - CIRCLE_RADIUS),
);
๊ฐ ์์ ์ด๊ธฐ ์์น๋ฅผ ๋ฌด์์๋ก ์ค์
pt2(x, y): Point2 ์์ฑ์.+ CIRCLE_RADIUS: ์์ด ํ๋ฉด ๋ฐ์ผ๋ก ์์ ธ๋๊ฐ์ง ์๋๋ก ์ฌ์ ๊ณต๊ฐ ํ๋ณด.
let speed = random_range(0.5, 1.0);
let angle = random_f32() * TAU;
let velocity = vec2(angle.cos(), angle.sin()) * speed;
๊ฐ ์์ ์ด๊ธฐ ์๋ ๋ฒกํฐ(velocity) ์ค์ : ๋ฌด์์ ๋ฐฉํฅ๊ณผ ์๋๊ฐ ์ด์ฉ
TAU= 2ฯ: ์์ฃผ์จ์ 2๋ฐฐ โ 0~TAU๋ 0~360๋.vec2(cos, sin): ๋จ์ ๋ฒกํฐ โ ๋ฐฉํฅ๋ง ํํ.* speed: ์๋ ํฌ๊ธฐ ์กฐ์ (0.5~1.0).
circles.push(Circle { position, velocity });
}
์์ฑ๋ ์์ ๋ฒกํฐ์ ์ถ๊ฐ
Model {
circles,
color: rgba(1.0, 0.5, 0.75, 1.0), // Semi-transparent pink
}
}
์ต์ข ๋ชจ๋ธ ๋ฐํ.
- ์์: ๋นจ๊ฐ 100%, ๋ น์ 50%, ํ๋ 75%, ์ํ 100% โ ๋ถํฌ๋ช ํํฌ
- ํธ๋ ์ผ ํจ๊ณผ๋ ์๋ view์์ ๋ณ๋๋ก ๊ตฌํ๋จ.
fn update(app: &App, model: &mut Model, _update: Update) {
let win = app.window_rect();
let half_width = win.w() / 2.0 - CIRCLE_RADIUS;
let half_height = win.h() / 2.0 - CIRCLE_RADIUS;
ํ๋ฉด ๊ฒฝ๊ณ ๊ณ์ฐ. ์์ ๋ฐ์ง๋ฆ์ ๋บ โ ์์ ๊ฐ์ฅ์๋ฆฌ๊ฐ ๊ฒฝ๊ณ์ ๋ฟ์ ๋ ํ๊ธฐ๋๋ก.
for circle in &mut model.circles {
circle.position += circle.velocity;
์์น ์ ๋ฐ์ดํธ โ ์๋๋ฅผ ๋ํจ์ผ๋ก์จ ์์ง์ ๊ตฌํ.
- ๋ฌผ๋ฆฌํ์
x = x + v * dt์ ์ ์ฌํ๋, ์ฌ๊ธฐ์๋dt=1๋ก ๊ณ ์ (ํ๋ ์๋น 1๋จ์ ์ด๋).
// Bounce off walls
if circle.position.x.abs() > half_width {
circle.velocity.x = -circle.velocity.x;
circle.position.x = circle.position.x.clamp(-half_width, half_width);
}
if circle.position.y.abs() > half_height {
circle.velocity.y = -circle.velocity.y;
circle.position.y = circle.position.y.clamp(-half_height, half_height);
}
}
}
๋ฒฝ ์ถฉ๋ ๊ฐ์ง ๋ฐ ๋ฐ์ฌ ๊ตฌํ
.abs() > half_width: ํ๋ฉด ์ข/์ฐ ๊ฒฝ๊ณ๋ฅผ ๋์๋์ง ํ์ธ.velocity.x = -velocity.x: x์ถ ์๋ ๋ฐ์ โ ํ๊น ํจ๊ณผ..clamp(...): ํน์๋ผ๋ ๊ฒฝ๊ณ๋ฅผ ์ด์ง ๋์์ ๊ฒฝ์ฐ ๊ฐ์ ๋ก ๊ฒฝ๊ณ ๋ด๋ก ๋น๊ฒจ์ด โ ๋์ ์ค์ฐจ ๋ฐฉ์ง.
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
let win = app.window_rect();
๊ทธ๋ฆฌ๊ธฐ ์ปจํ ์คํธ์ ์ฐฝ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
// Clear to black only on first frame
if frame.nth() == 0 {
draw.background().color(BLACK);
}
์ฒซ ํ๋ ์์๋ง ๋ฐฐ๊ฒฝ์ ๊ฒ์ ์์ผ๋ก ํด๋ฆฌ์ด
- ์ดํ ํ๋ ์์์๋ ํด๋ฆฌ์ดํ์ง ์๊ณ ๋จ๊ฒจ์ trail ํจ๊ณผ๋ฅผ ๊ตฌํํจ.
// Draw semi-transparent rect for trail effect
draw.rect()
.wh(win.wh())
.xy(win.xy())
.color(rgba(0.0, 0.0, 0.0, 0.05));
์ ์ฒด ํ๋ฉด์ ๊ฑฐ์ ํฌ๋ช ํ ๊ฒ์ ์ฌ๊ฐํ์ ๊ทธ๋ฆผ
- ์ํ๊ฐ
0.05โ ์ด์ ํ๋ ์์ 5% ์ ๋ ๋จ๊ธฐ๊ณ 95% ์ด๋ก๊ฒ โ ๋ถ๋๋ฌ์ด ์์ ํจ๊ณผ.- ์ด ๋ฐฉ์์ผ๋ก "์ง์ฐ์ง ์๊ณ ๋ง๊ทธ๋ฆฌ๋" ํธ๋ ์ผ ๊ตฌํ.
// Draw each circle
for circle in &model.circles {
draw.ellipse()
.xy(circle.position)
.radius(CIRCLE_RADIUS)
.color(model.color);
}
๋ชจ๋ ์์ ํ์ฌ ์์น์ ๊ทธ๋ฆผ
.ellipse(): ์ ๋๋ ํ์ ์์ฑ โ ์ฌ๊ธฐ์ ๋ฐ์ง๋ฆ์ด ๊ฐ์ ์..radius(CIRCLE_RADIUS): ์ ์ญ ์์ ์ฌ์ฉ โ ์ผ๊ด๋ ํฌ๊ธฐ..color(model.color): ๋ชจ๋ธ์ ์ ์ฅ๋ ์์ ์ฌ์ฉ.
draw.to_frame(app, &frame).unwrap();
}
๊ทธ๋ฆฐ ๋ด์ฉ์ ์ค์ ํ๋ ์ ๋ฒํผ์ ์ถ๋ ฅ
- ์คํจ ์ panic โ
unwrap์ฌ์ฉ (๊ฐ๋จํ ์์ ์์ ๊ด์ฐฎ์).