

use nannou::prelude::*;
use nannou::noise::{NoiseFn, Perlin};
fn main() {
nannou::app(model).update(update).run();
}
struct Particle {
pos: Vec2,
prev_pos: Vec2,
vel: Vec2,
acc: Vec2,
}
impl Particle {
fn new(x: f32, y: f32) -> Self {
let pos = vec2(x, y);
Particle {
pos,
prev_pos: pos,
vel: Vec2::ZERO,
acc: Vec2::ZERO,
}
}
fn update(&mut self) {
self.vel += self.acc;
self.vel = self.vel.clamp_length_max(4.0);
self.prev_pos = self.pos;
self.pos += self.vel;
self.acc *= 0.0;
}
fn apply_force(&mut self, force: Vec2) {
self.acc += force;
}
fn edges(&mut self, rect: &Rect) {
if self.pos.x > rect.right() {
self.pos.x = rect.left();
self.prev_pos.x = self.pos.x;
}
if self.pos.x < rect.left() {
self.pos.x = rect.right();
self.prev_pos.x = self.pos.x;
}
if self.pos.y > rect.top() {
self.pos.y = rect.bottom();
self.prev_pos.y = self.pos.y;
}
if self.pos.y < rect.bottom() {
self.pos.y = rect.top();
self.prev_pos.y = self.pos.y;
}
}
}
struct Model {
perlin: Perlin,
particles: Vec<Particle>,
time: f64,
noise_scale: f64,
}
fn model(app: &App) -> Model {
let window_id = app.new_window()
.size(1024, 1024)
.title("Perlin Noise Flow Field")
.view(view)
.build()
.unwrap();
let win = app.window(window_id).unwrap().rect();
let perlin = Perlin::new();
let time = 0.0;
let noise_scale = 0.005;
let particles = (0..500)
.map(|_| {
let x = random_range(win.left(), win.right());
let y = random_range(win.bottom(), win.top());
Particle::new(x, y)
})
.collect();
Model {
perlin,
particles,
time,
noise_scale,
}
}
fn update(app: &App, model: &mut Model, _update: Update) {
let win = app.window_rect();
for p in &mut model.particles {
let noise_val = model.perlin.get([
p.pos.x as f64 * model.noise_scale,
p.pos.y as f64 * model.noise_scale,
model.time,
]);
let angle = map_range(noise_val, -1.0, 1.0, 0.0, 2.0 * PI as f64) as f32;
let force = vec2(angle.cos(), angle.sin()) * 0.1;
p.apply_force(force);
p.update();
p.edges(&win);
}
model.time += 0.005;
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
if app.elapsed_frames() == 1 {
draw.background().color(BLACK);
}
draw.rect()
.wh(app.window_rect().wh())
.color(srgba(0.0, 0.0, 0.0, 0.05));
for p in &model.particles {
let noise_val_color = model.perlin.get([
p.pos.x as f64 * model.noise_scale,
p.pos.y as f64 * model.noise_scale,
model.time + 100.0, // ์์ ๋ณํ์ ๋ค๋ฅธ ๋
ธ์ด์ฆ ์๋๋ฅผ ์ฃผ๊ธฐ ์ํด ์์์ ๊ฐ์ ๋ํจ
]);
let hue = map_range(noise_val_color, -1.0, 1.0, 0.5, 1.0);
let color = hsla(hue as f32, 0.7, 0.6, 0.7);
draw.line()
.start(p.prev_pos)
.end(p.pos)
.weight(1.5)
.color(color);
}
draw.to_frame(app, &frame).unwrap();
}
// nannou์ prelude(๊ฐ์ฅ ์์ฃผ ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ๋ค์ ๋ชจ์)๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
// App, Frame, vec2, aabb, rect, PI, TAU, HSL, ๋ฑ ํต์ฌ ํ์
๊ณผ ํจ์๋ค์ด ํฌํจ๋์ด ์์ด ํธ๋ฆฌํฉ๋๋ค.
use nannou::prelude::*;
// nannou์ ๋ด์ฅ๋ noise ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ์,
// ๋
ธ์ด์ฆ ๊ฐ ์์ฑ์ ์ํ `get` ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ NoiseFn ํธ๋ ์(trait)์ ๊ฐ์ ธ์ต๋๋ค.
use nannou::noise::{NoiseFn, Perlin};
// ======================================================================
// ==== 1. ์ ์ญ ์์ ์ค์ (Global Constants)
// ======================================================================
// ์ํธ์ํฌ์ ํน์ฑ์ ๊ฒฐ์ ํ๋ ์ฃผ์ ํ๋ผ๋ฏธํฐ๋ค์ ์์๋ก ์ ์ํฉ๋๋ค.
// ์ด๋ ๊ฒ ํ๋ฉด ์ฝ๋ ์๋จ์์ ์ฝ๊ฒ ๊ฐ์ ์กฐ์ ํ๊ณ ์คํํ ์ ์์ผ๋ฉฐ, '๋ง๋ฒ์ ์ซ์'๋ฅผ ํผํ ์ ์์ต๋๋ค.
// `const`๋ ์ปดํ์ผ ํ์์ ๊ฐ์ด ๊ฒฐ์ ๋๋ ๋ถ๋ณ ์์์
๋๋ค.
const NUM_PARTICLES: usize = 500; // ํ๋ฉด์ ์์ฑํ ํํฐํด์ ์ด ๊ฐ์. `usize`๋ ์ปฌ๋ ์
์ ์ธ๋ฑ์ฑ์ด๋ ํฌ๊ธฐ๋ฅผ ๋ํ๋ผ ๋ ์ฌ์ฉ๋๋ ๋ถํธ ์๋ ์ ์ ํ์
์
๋๋ค.
const NOISE_SCALE: f64 = 0.005; // ๋
ธ์ด์ฆ ํ๋์ ์ค์ผ์ผ. ๊ฐ์ด ์์์๋ก ๋
ธ์ด์ฆ ํจํด์ด ํ๋๋์ด(zoom-in) ๋ถ๋๋ฌ์ด ํ๋ฆ์, ํด์๋ก ์๊ธ์๊ธํ ํ๋ฆ์ ๋ง๋ญ๋๋ค.
const MAX_SPEED: f32 = 4.0; // ๊ฐ ํํฐํด์ด ๊ฐ์ง ์ ์๋ ์ต๋ ์๋ ฅ. ์ด ๊ฐ์ ํตํด ํํฐํด์ด ๋๋ฌด ๋น ๋ฅด๊ฒ ์์ง์ด๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค.
const FORCE_STRENGTH: f32 = 0.1; // ๋
ธ์ด์ฆ ํ๋๊ฐ ํํฐํด์ ๊ฐํ๋ ํ์ ์ธ๊ธฐ. ํด์๋ก ๋
ธ์ด์ฆ ํ๋์ ํ๋ฆ์ ๋ ๊ฐํ๊ฒ ๋ฐ๋ผ๊ฐ๋๋ค.
const TIME_INCREMENT: f64 = 0.005; // ๋งค ํ๋ ์๋ง๋ค ์๊ฐ์ ์ผ๋ง๋ ์ฆ๊ฐ์ํฌ์ง ๊ฒฐ์ . ๋
ธ์ด์ฆ ํ๋๊ฐ ๋ณํํ๋ ์๋๋ฅผ ์ ์ดํฉ๋๋ค.
const TRAIL_ALPHA: f32 = 0.05; // ์์ ํจ๊ณผ๋ฅผ ๋ง๋ค๊ธฐ ์ํ ๋ฐฐ๊ฒฝ์ ํฌ๋ช
๋. 0.0์ ๊ฐ๊น์ธ์๋ก ์์์ด ๊ธธ๊ฒ ๋จ๊ณ , 1.0์ ๊ฐ๊น์ฐ๋ฉด ๊ฑฐ์ ๋จ์ง ์์ต๋๋ค.
// ======================================================================
// ==== 2. ๋ฉ์ธ ํจ์ (Main Function)
// ======================================================================
// Rust ํ๋ก๊ทธ๋จ์ ์ง์
์ (entry point)์
๋๋ค.
fn main() {
// nannou ์ฑ์ ์ค์ ํ๊ณ ์คํํฉ๋๋ค. ๋น๋(builder) ํจํด์ ์ฌ์ฉํฉ๋๋ค.
// app(model): `model` ํจ์๋ฅผ ์ฌ์ฉํด ์ฑ์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ ํฉ๋๋ค.
// .update(update): ๋งค ํ๋ ์๋ง๋ค ์ํ๋ฅผ ๋ณ๊ฒฝํ `update` ํจ์๋ฅผ ์ง์ ํฉ๋๋ค.
// .run(): ๋ชจ๋ ์ค์ ์ ๋ง์น๊ณ ์ฑ์ ๋ฉ์ธ ๋ฃจํ๋ฅผ ์์ํฉ๋๋ค.
nannou::app(model).update(update).run();
}
// ======================================================================
// ==== 3. ํํฐํด ๊ตฌ์กฐ์ฒด ์ ์ (Particle Struct)
// ======================================================================
// ๊ฐ๋ณ ํํฐํด์ ์ํ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ์ปค์คํ
๋ฐ์ดํฐ ํ์
(๊ตฌ์กฐ์ฒด)์
๋๋ค.
struct Particle {
pos: Vec2, // ํํฐํด์ ํ์ฌ ์์น (x, y ์ขํ). `Vec2`๋ nannou์ 2์ฐจ์ ๋ฒกํฐ ํ์
์
๋๋ค.
prev_pos: Vec2, // ํํฐํด์ ์ด์ ํ๋ ์ ์์น. ํ์ฌ ์์น์ ์ฐ๊ฒฐํ์ฌ ๊ถค์ (์ )์ ๊ทธ๋ฆฌ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
vel: Vec2, // ํํฐํด์ ์๋ (x, y ๋ฐฉํฅ์ ์๋). ๋งค ํ๋ ์ ์์น์ ๋ํด์ ธ ์์ง์์ ๋ง๋ญ๋๋ค.
}
// ======================================================================
// ==== 4. ํํฐํด ๋ฉ์๋ ๊ตฌํ (Particle Implementation)
// ======================================================================
// `impl` ํค์๋๋ ํน์ ๊ตฌ์กฐ์ฒด(์ฌ๊ธฐ์๋ Particle)์ ๋ํ ๋ฉ์๋(ํจ์)๋ฅผ ๊ตฌํํ๋ ๋ธ๋ก์
๋๋ค.
impl Particle {
// `Particle`์ ์์ฑ์(constructor) ์ญํ ์ ํ๋ ์ฐ๊ด ํจ์(associated function)์
๋๋ค.
// `&self`๋ฅผ ์ธ์๋ก ๋ฐ์ง ์์ผ๋ฏ๋ก `Particle::new(...)` ํํ๋ก ํธ์ถํฉ๋๋ค.
fn new(x: f32, y: f32) -> Self {
let pos = vec2(x, y); // `vec2` ํจ์๋ก `Vec2` ํ์
์ ์์น ๋ฒกํฐ๋ฅผ ์์ฑํฉ๋๋ค.
// ์๋ก์ด Particle ์ธ์คํด์ค๋ฅผ ์์ฑํ์ฌ ๋ฐํํฉ๋๋ค.
Particle {
pos, // ํ์ฌ ์์น๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
prev_pos: pos, // ์ฒ์์๋ ์ด์ ์์น์ ํ์ฌ ์์น๊ฐ ๊ฐ์ต๋๋ค.
vel: Vec2::ZERO, // ์ฒ์ ์๋๋ 0์
๋๋ค. `Vec2::ZERO`๋ `vec2(0.0, 0.0)`์ ๊ฐ์ต๋๋ค.
}
}
// ์ธ๋ถ์์ ์ฃผ์ด์ง ํ(force)์ ๊ธฐ๋ฐ์ผ๋ก ํํฐํด์ ์๋์ ์์น๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
// `&mut self`๋ ์ด ๋ฉ์๋๊ฐ Particle ์ธ์คํด์ค์ ๋ฐ์ดํฐ๋ฅผ '์์ (mutate)'ํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
fn update_with_force(&mut self, force: Vec2) {
// ํ์ ๊ฐ์๋๋ก ๊ฐ์ฃผํ์ฌ ์๋์ ๋ํฉ๋๋ค. (์ง๋=1๋ก ๊ฐ์ )
let acc = force;
// ์๋ ์
๋ฐ์ดํธ ๋ฐ ์ต๋ ์๋ ฅ ์ ํ
self.vel += acc;
// `clamp_length_max`๋ ๋ฒกํฐ์ ๋ฐฉํฅ์ ์ ์งํ ์ฑ, ํฌ๊ธฐ(๊ธธ์ด)๊ฐ ์ต๋๊ฐ์ ๋์ง ์๋๋ก ์ ํํ๋ ํธ๋ฆฌํ ๋ฉ์๋์
๋๋ค.
self.vel = self.vel.clamp_length_max(MAX_SPEED);
// ์์น ์
๋ฐ์ดํธ
self.prev_pos = self.pos; // ํ์ฌ ์์น๋ฅผ ์ด์ ์์น๋ก ๊ธฐ๋กํฉ๋๋ค.
self.pos += self.vel; // ์๋๋ฅผ ํ์ฌ ์์น์ ๋ํ์ฌ ๋ค์ ์์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
}
// ํํฐํด์ด ํ๋ฉด ๊ฒฝ๊ณ๋ฅผ ๋ฒ์ด๋ฌ์ ๋, ๋ฐ๋ํธ์์ ๋ค์ ๋ํ๋๋๋ก ์ฒ๋ฆฌํฉ๋๋ค (ํ๋ฉด ๊ฐ์ธ๊ธฐ).
fn edges(&mut self, rect: &Rect) {
// `rect`๋ ํ๋ฉด์ ์ฌ๊ฐํ ์์ญ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
if self.pos.x > rect.right() { self.pos.x = rect.left(); self.prev_pos.x = self.pos.x; }
if self.pos.x < rect.left() { self.pos.x = rect.right(); self.prev_pos.x = self.pos.x; }
if self.pos.y > rect.top() { self.pos.y = rect.bottom();self.prev_pos.y = self.pos.y; }
if self.pos.y < rect.bottom() { self.pos.y = rect.top(); self.prev_pos.y = self.pos.y; }
// ๊ฒฝ๊ณ๋ฅผ ๋์ ๋ `prev_pos`๋ ํจ๊ป ์์ ํด์ฃผ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
// ๊ทธ๋ ์ง ์์ผ๋ฉด ํ๋ฉด์ ๊ฐ๋ก์ง๋ฅด๋ ๊ธด ์ ์ด ๊ทธ๋ ค์ง๊ฒ ๋ฉ๋๋ค.
}
}
// ======================================================================
// ==== 5. ๋ชจ๋ธ(์ํ) ๊ตฌ์กฐ์ฒด ์ ์ (Model Struct)
// ======================================================================
// nannou ์ฑ์ ์ ์ฒด ์ํ(state)๋ฅผ ๋ด๋ ๊ตฌ์กฐ์ฒด์
๋๋ค.
// ์ด `Model`์ ์ธ์คํด์ค๊ฐ `update`์ `view` ํจ์ ์ฌ์ด์์ ๊ณ์ ์ ๋ฌ๋๋ฉฐ ์ฑ์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
struct Model {
perlin: Perlin, // Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ ์ธ์คํด์ค.
particles: Vec<Particle>, // ๋ชจ๋ `Particle`๋ค์ ๋ด๊ณ ์๋ ๋์ ๋ฐฐ์ด(Vector).
time: f64, // ์ ๋๋ฉ์ด์
์ ์ํ ์๊ฐ ๊ฐ. 3D ๋
ธ์ด์ฆ์ z์ถ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
}
// ======================================================================
// ==== 6. ๋ชจ๋ธ ์ด๊ธฐํ ํจ์ (Model Initialization)
// ======================================================================
// ์ฑ์ด ์์๋ ๋ ๋จ ํ ๋ฒ ํธ์ถ๋์ด `Model`์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ ํฉ๋๋ค.
fn model(app: &App) -> Model {
// ์ฐฝ(window)์ ์์ฑํ๊ณ , ํฌ๊ธฐ/์ ๋ชฉ ๋ฑ์ ์ค์ ํ ๋ค, `view` ํจ์๋ฅผ ์ด ์ฐฝ์ ๋ ๋๋ง ๋ฃจํ๋ก ์ง์ ํฉ๋๋ค.
app.new_window()
.size(1024, 1024)
.title("Perlin Noise Flow Field (Refactored & Commented)")
.view(view) // ์ด ์ฐฝ์ ๊ทธ๋ฆด ๋ `view` ํจ์๋ฅผ ์ฌ์ฉํ๋ผ๊ณ ์๋ ค์ค๋๋ค.
.build()
.unwrap(); // `build()`๋ `Result`๋ฅผ ๋ฐํํ๋ฏ๋ก, `unwrap()`์ผ๋ก ์ฑ๊ณต์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์ถ์ถํฉ๋๋ค.
let win = app.window_rect(); // ํ์ฌ ์ฐฝ์ ์ฌ๊ฐํ ์์ญ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
let perlin = Perlin::new(); // ์๋ก์ด Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ๋ฅผ ๋ง๋ญ๋๋ค.
// Rust์ ์ดํฐ๋ ์ดํฐ(iterator)์ `map`, `collect`๋ฅผ ์ฌ์ฉํ์ฌ ํํฐํด์ ํจ์จ์ ์ผ๋ก ์์ฑํฉ๋๋ค.
let particles = (0..NUM_PARTICLES) // 0๋ถํฐ `NUM_PARTICLES - 1`๊น์ง์ ์ซ์ ๋ฒ์๋ฅผ ๋ง๋ญ๋๋ค.
.map(|_| { // ๊ฐ ์ซ์์ ๋ํด ๋ค์ ํด๋ก์ (closure, ์ต๋ช
ํจ์)๋ฅผ ์คํํฉ๋๋ค. `_`๋ ์ซ์๋ฅผ ์ฌ์ฉํ์ง ์๊ฒ ๋ค๋ ์๋ฏธ์
๋๋ค.
// ํ๋ฉด ๋ด ๋ฌด์์ ์์น๋ฅผ ์ง์ ํฉ๋๋ค.
let x = random_range(win.left(), win.right());
let y = random_range(win.bottom(), win.top());
Particle::new(x, y) // ํด๋น ์์น์ ์๋ก์ด ํํฐํด์ ์์ฑํฉ๋๋ค.
})
.collect(); // `map`์ ํตํด ์์ฑ๋ ๋ชจ๋ `Particle`๋ค์ ๋ชจ์ `Vec<Particle>` ์ปฌ๋ ์
์ ๋ง๋ญ๋๋ค.
// ์์ฑ๋ ์ด๊ธฐ ์ํ๋ฅผ ๋ด์ `Model` ์ธ์คํด์ค๋ฅผ ๋ฐํํฉ๋๋ค.
Model {
perlin,
particles,
time: 0.0, // ์๊ฐ์ 0์์ ์์ํฉ๋๋ค.
}
}
// ======================================================================
// ==== 7. ์ํ ์
๋ฐ์ดํธ ํจ์ (State Update Function)
// ======================================================================
// ๋งค ํ๋ ์๋ง๋ค `view` ํจ์๊ฐ ํธ์ถ๋๊ธฐ ์ง์ ์ ์คํ๋ฉ๋๋ค.
// ์ฑ์ ๋
ผ๋ฆฌ์ ์ธ ์ํ(์์น, ์๋ ๋ฑ)๋ฅผ ๊ณ์ฐํ๊ณ ๋ณ๊ฒฝํ๋ ์ญํ ์ ํฉ๋๋ค.
fn update(app: &App, model: &mut Model, _update: Update) {
let win = app.window_rect();
// `&mut model.particles`๋ฅผ ํตํด ๊ฐ ํํฐํด์ ๊ฐ๋ณ์ ์ผ๋ก ๋น๋ ค์ ์ํ๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
for p in &mut model.particles {
// ํํฐํด์ ํ์ฌ ์์น(x, y)์ ํ์ฌ ์๊ฐ(time)์ ์ด์ฉํด 3D Perlin ๋
ธ์ด์ฆ ๊ฐ์ ์ป์ต๋๋ค.
let noise_val = model.perlin.get([
p.pos.x as f64 * NOISE_SCALE, // x, y ์ขํ์ ์ค์ผ์ผ์ ๊ณฑํด ๋
ธ์ด์ฆ ์์ธ๋๋ฅผ ์กฐ์ ํฉ๋๋ค.
p.pos.y as f64 * NOISE_SCALE, // `get`์ f64 ๋ฐฐ์ด์ ๋ฐ์ผ๋ฏ๋ก ํ์
์บ์คํ
(as f64)์ด ํ์ํฉ๋๋ค.
model.time, // z์ถ ๊ฐ์ผ๋ก ์๊ฐ์ ๋ฃ์ด ๋
ธ์ด์ฆ ํ๋๊ฐ ์๊ฐ์ ๋ฐ๋ผ ๋ณํ๊ฒ ํฉ๋๋ค.
]);
// ๋
ธ์ด์ฆ ๊ฐ(-1.0 ~ 1.0)์ ๊ฐ๋(0 ~ 2ฯ) ๋ฒ์๋ก ๋ณํํฉ๋๋ค.
// `TAU`๋ nannou prelude์ ์ ์๋ ์์์ด๋ฉฐ `2.0 * PI`์ ๊ฐ์ต๋๋ค. ์ ์ ์ฒด๋ฅผ ๋ค๋ฃฐ ๋ ๊ฐ๋
์ฑ์ด ์ข์ต๋๋ค.
let angle = map_range(noise_val, -1.0, 1.0, 0.0, TAU as f64) as f32;
// ๊ณ์ฐ๋ ๊ฐ๋๋ฅผ ๋ฐํ์ผ๋ก ํ(force) ๋ฒกํฐ๋ฅผ ์์ฑํฉ๋๋ค.
// `(angle.cos(), angle.sin())`์ ํด๋น ๊ฐ๋์ ๋ฐฉํฅ์ ๋ํ๋ด๋ ๋จ์ ๋ฒกํฐ(๊ธธ์ด๊ฐ 1์ธ ๋ฒกํฐ)๋ฅผ ๋ง๋ญ๋๋ค.
let force = vec2(angle.cos(), angle.sin()) * FORCE_STRENGTH;
// ๊ณ์ฐ๋ ํ์ผ๋ก ํํฐํด์ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๊ณ , ํ๋ฉด ๊ฒฝ๊ณ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
p.update_with_force(force);
p.edges(&win);
}
// ๋ค์ ํ๋ ์์ ์ํด ์๊ฐ์ ์ฝ๊ฐ ์ฆ๊ฐ์ํต๋๋ค.
model.time += TIME_INCREMENT;
}
// ======================================================================
// ==== 8. ๋๋ก์ ํจ์ (View / Drawing Function)
// ======================================================================
// ๋งค ํ๋ ์๋ง๋ค `update` ํจ์๊ฐ ์คํ๋ ํ์ ํธ์ถ๋ฉ๋๋ค.
// ํ์ฌ `Model`์ ์ํ๋ฅผ ๋ฐํ์ผ๋ก ํ๋ฉด์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆฌ๋ ์ญํ ์ ํฉ๋๋ค.
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw(); // `Draw` ์ธ์คํด์ค๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ๋ชจ๋ ๊ทธ๋ฆฌ๊ธฐ ๋ช
๋ น์ ์ด `draw` ๊ฐ์ฒด๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋๋ค.
// ์ฒซ ํ๋ ์์๋ง ๋ฐฐ๊ฒฝ์ ์์ ํ ๊ฒ์์์ผ๋ก ์น ํฉ๋๋ค.
if app.elapsed_frames() == 1 {
draw.background().color(BLACK);
}
// ๋งค ํ๋ ์, ์ด์ ์ ๊ทธ๋ ค์ง ๊ทธ๋ฆผ ์์ ๋ฐํฌ๋ช
ํ ๊ฒ์ ์ฌ๊ฐํ์ ๋ฎ์ด ๊ทธ๋ฆฝ๋๋ค.
// ์ด ๊ณผ์ ์ด ๋ฐ๋ณต๋๋ฉด์ ํํฐํด์ ์์ง์์ ์์ฐ์ค๋ฌ์ด ์์(trail) ํจ๊ณผ๊ฐ ์๊น๋๋ค.
draw.rect()
.wh(app.window_rect().wh()) // ์ฐฝ ์ ์ฒด ํฌ๊ธฐ์ ์ฌ๊ฐํ
.color(srgba(0.0, 0.0, 0.0, TRAIL_ALPHA)); // `srgba`๋ ํฌ๋ช
๋๋ฅผ ํฌํจํ ์์์
๋๋ค.
// ๋ชจ๋ ํํฐํด์ ์ํํ๋ฉฐ ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค. ์ด๋ฒ์๋ ์ํ๋ฅผ ์์ ํ์ง ์์ผ๋ฏ๋ก `&model.particles`๋ก ๋น๋ ค์ต๋๋ค.
for p in &model.particles {
// ์์์ ๊ฒฐ์ ํ๊ธฐ ์ํด ๋ค๋ฅธ ๋
ธ์ด์ฆ ๊ฐ์ ๊ฐ์ ธ์ต๋๋ค.
// `time + 100.0` ์ฒ๋ผ ์๊ฐ์ ํฐ ๊ฐ์ ๋ํด์ฃผ๋ฉด, ์์ง์์ ๋ฐฉํฅ๊ณผ ์์ ๋ณํ๊ฐ
// ์๋ก ๋ค๋ฅธ ํจํด์ ๊ฐ์ง๊ฒ ๋์ด ๋ ํฅ๋ฏธ๋ก์ด ์๊ฐ์ ํจ๊ณผ๋ฅผ ๋ง๋ญ๋๋ค.
let noise_val_color = model.perlin.get([
p.pos.x as f64 * NOISE_SCALE,
p.pos.y as f64 * NOISE_SCALE,
model.time + 100.0,
]);
// ๋
ธ์ด์ฆ ๊ฐ์ HSL ์์ ๋ชจ๋ธ์ ์์(Hue) ๊ฐ์ผ๋ก ๋ณํํฉ๋๋ค.
// HSL ๋ชจ๋ธ์์ Hue ๊ฐ์ ์ฐ์์ ์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ฌด์ง๊ฐ์ฒ๋ผ ๋ถ๋๋ฌ์ด ์์ ์ ํ์ ์ป์ ์ ์์ต๋๋ค.
let hue = map_range(noise_val_color, -1.0, 1.0, 0.5, 1.0); // ํ๋์~๋ณด๋ผ์~๋นจ๊ฐ์ ๊ณ์ด
let color = hsla(hue as f32, 0.7, 0.6, 0.7); // Hue, Saturation, Lightness, Alpha
// ํํฐํด์ ์ด์ ์์น์์ ํ์ฌ ์์น๊น์ง ์ ์ ๊ทธ์ด ๊ถค์ ์ ํํํฉ๋๋ค.
draw.line()
.start(p.prev_pos)
.end(p.pos)
.weight(1.5) // ์ ์ ๋๊ป
.color(color);
}
// ์ง๊ธ๊น์ง ๋ฒํผ์ ์์ธ ๋ชจ๋ ๊ทธ๋ฆฌ๊ธฐ ๋ช
๋ น๋ค์ ์ค์ ํ๋ ์์ ๋ ๋๋งํฉ๋๋ค.
draw.to_frame(app, &frame).unwrap();
}