

// =============================================================================
// 0. ๋ชจ๋ ๋ฐ ํฌ๋ ์ดํธ ์ํฌํธ (Imports)
// =============================================================================
// `nannou::prelude::*`๋ nannou ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ์ ํ์ํ ๋๋ถ๋ถ์ ๊ธฐ๋ณธ ํ์
๊ณผ ํจ์๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
use nannou::prelude::*;
// Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด nannou์ noise ๋ชจ๋์์ Perlin ๊ตฌ์กฐ์ฒด์ NoiseFn ํธ๋ ์ดํธ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
use nannou::noise::{NoiseFn, Perlin};
// =============================================================================
// 1. ๋ฉ์ธ ํจ์ (Application Entry Point)
// =============================================================================
// Rust ํ๋ก๊ทธ๋จ์ ์ง์
์ ์
๋๋ค.
fn main() {
nannou::app(model)
.update(update)
.run();
}
// =============================================================================
// 2. ๋ผ์ธ ๊ตฌ์กฐ์ฒด ์ ์ (Line Structure)
// =============================================================================
// `Line`์ ํ๋ฉด์ ๊ทธ๋ ค์ง ํ๋์ ๊ณก์ ์ ํํํฉ๋๋ค.
// ๊ฐ ๋ผ์ธ์ ์ฌ๋ฌ ๊ฐ์ ์ ์ผ๋ก ๊ตฌ์ฑ๋๋ฉฐ, ๋
ธ์ด์ฆ ํ๋์ ์ํด ๋ณํ๋ฉ๋๋ค.
struct Line {
// ์๋ณธ ์ ๋ค์ ์์น (๋ณํ๋์ง ์์ ์ด๊ธฐ ์์น)
// ์ด ์ ๋ค์ ๋ณํ์ง ์๊ณ , ๋ณํ ๊ณ์ฐ์ ๊ธฐ์ค์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
original_points: Vec<Point2>,
// ํ์ฌ ๋ณํ๋ ์ ๋ค์ ์์น
// ๋
ธ์ด์ฆ ํ๋์ ์ํฅ์ ๋ฐ์ ๋งค ํ๋ ์๋ง๋ค ๋ค์ ๊ณ์ฐ๋ฉ๋๋ค.
deformed_points: Vec<Point2>,
}
impl Line {
// ๋ผ์ธ ์์ฑ์: ์์์ , ๋์ , ๊ทธ๋ฆฌ๊ณ ์ ์ ๊ฐ์๋ฅผ ๋ฐ์ ๋ผ์ธ์ ์ด๊ธฐํํฉ๋๋ค.
// `segments`๋ ๋ผ์ธ์ ๋ช ๊ฐ์ ์ ์ผ๋ก ๋๋์ง ๊ฒฐ์ ํฉ๋๋ค (๋ง์์๋ก ๋ถ๋๋ฌ์ด ๋ณํ).
fn new(start: Point2, end: Point2, segments: usize) -> Self {
let mut original_points = Vec::new();
// ์์์ ์์ ๋์ ๊น์ง ๊ท ๋ฑํ๊ฒ ๋ถํ ๋ ์ ๋ค์ ์์ฑํฉ๋๋ค.
// ์ ํ ๋ณด๊ฐ(linear interpolation)์ ์ฌ์ฉํ์ฌ ์ค๊ฐ ์ ๋ค์ ๊ณ์ฐํฉ๋๋ค.
for i in 0..=segments {
// t๋ 0.0์์ 1.0๊น์ง์ ๋ณด๊ฐ ๋น์จ์
๋๋ค.
// i=0์ผ ๋ t=0.0 (์์์ ), i=segments์ผ ๋ t=1.0 (๋์ )
let t = i as f32 / segments as f32;
// ์ ํ ๋ณด๊ฐ ๊ณต์: point = start + t * (end - start)
// start * (1-t) + end * t ์ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํฉ๋๋ค.
let x = start.x + t * (end.x - start.x);
let y = start.y + t * (end.y - start.y);
original_points.push(pt2(x, y));
}
// ์ด๊ธฐ์๋ ๋ณํ๋ ์ ๋ค๋ ์๋ณธ๊ณผ ๋์ผํ๊ฒ ์ค์ ํฉ๋๋ค.
let deformed_points = original_points.clone();
Line {
original_points,
deformed_points,
}
}
// ๋
ธ์ด์ฆ ํ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ผ์ธ์ ๋ณํ์ํต๋๋ค.
// `perlin`: ๋
ธ์ด์ฆ ์์ฑ๊ธฐ
// `time`: ์๊ฐ ์คํ์
(์ ๋๋ฉ์ด์
์ ์ํด ์ฌ์ฉ)
// `strength`: ๋ณํ์ ๊ฐ๋ (๊ฐ์ด ํด์๋ก ๋ ๋ง์ด ํ์ด์ง)
fn update(&mut self, perlin: &Perlin, time: f64, strength: f32) {
// ๊ฐ ์ ์ ๋ํด ๋
ธ์ด์ฆ ๊ธฐ๋ฐ ๋ณํ์ ์ ์ฉํฉ๋๋ค.
for i in 0..self.original_points.len() {
let original = self.original_points[i];
// ํ์ฌ ์ ์ ์์น์์ ๋
ธ์ด์ฆ ๊ฐ์ ๊ณ์ฐํฉ๋๋ค.
// 3D ๋
ธ์ด์ฆ๋ฅผ ์ฌ์ฉ: (x, y, time)
// - x, y๋ฅผ 0.003๋ฐฐ๋ก ์ค์ผ์ผ๋งํ์ฌ ๋ถ๋๋ฌ์ด ๋ณํ์ ๋ง๋ญ๋๋ค.
// (์ค์ผ์ผ ๊ฐ์ด ์์์๋ก ๋ ํฐ ํจํด, ๋ถ๋๋ฌ์ด ๋ณํ)
// - time์ ์ธ ๋ฒ์งธ ์ฐจ์์ผ๋ก ์ฌ์ฉํ์ฌ ์๊ฐ์ ๋ฐ๋ผ ๋
ธ์ด์ฆ๊ฐ ๋ณํฉ๋๋ค.
let noise_x = perlin.get([
original.x as f64 * 0.004,
original.y as f64 * 0.001,
time,
]);
// y ๋ฐฉํฅ ๋ณํ์ ์ํ ๋
๋ฆฝ์ ์ธ ๋
ธ์ด์ฆ๋ฅผ ๊ณ์ฐํฉ๋๋ค.
// 10000.0 ์คํ์
์ ์ถ๊ฐํ์ฌ x์ y๊ฐ ์๋ก ๋ค๋ฅธ ๋
ธ์ด์ฆ ํจํด์ ๋ฐ๋๋ก ํฉ๋๋ค.
// ์คํ์
์ด ์์ผ๋ฉด x์ y๊ฐ ๋์ผํ ํจํด์ผ๋ก ๋ณํ๋์ด ๋๊ฐ์ ๋ฐฉํฅ์ผ๋ก๋ง ์์ง์
๋๋ค.
let noise_y = perlin.get([
original.x as f64 * 0.004 + 10000.0, // offset ์ถ๊ฐ
original.y as f64 * 0.001 + 10000.0, // offset ์ถ๊ฐ
time,
]);
// ๋
ธ์ด์ฆ ๊ฐ์ ๋ณ์(displacement)๋ก ๋ณํํฉ๋๋ค.
// Perlin ๋
ธ์ด์ฆ๋ -1.0 ~ 1.0 ๊ฐ์ ๋ฐํํ๋ฏ๋ก, ์ด๋ฅผ ์ง์ ๋ณ์๋ก ์ฌ์ฉํฉ๋๋ค.
// `strength`๋ฅผ ๊ณฑํ์ฌ ๋ณํ์ ์ ๋๋ฅผ ์กฐ์ ํฉ๋๋ค.
let offset_x = noise_x as f32 * strength;
let offset_y = noise_y as f32 * strength;
// ์๋ณธ ์์น์ ๋ณ์๋ฅผ ๋ํด ๋ณํ๋ ์์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
// ์ด๋ ๊ฒ ํ๋ฉด ๋ผ์ธ์ด ๋
ธ์ด์ฆ ํ๋๋ฅผ ๋ฐ๋ผ ์์ฐ์ค๋ฝ๊ฒ ํ์ด์ง๋๋ค.
self.deformed_points[i] = pt2(
original.x + offset_x,
original.y + offset_y,
);
}
}
}
// =============================================================================
// 3. ๋ชจ๋ธ ์ ์ (State Structure)
// =============================================================================
// `Model` ๊ตฌ์กฐ์ฒด๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ฒด ์ํ๋ฅผ ์ ์ฅํฉ๋๋ค.
struct Model {
// Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ ์ธ์คํด์ค
perlin: Perlin,
// ํ๋ฉด์ ๊ทธ๋ ค์ง ๋ชจ๋ ๋ผ์ธ๋ค์ ๋ฒกํฐ
lines: Vec<Line>,
// ์๊ฐ ์คํ์
: ๋
ธ์ด์ฆ ํ๋๋ฅผ ์๊ฐ์ ๋ฐ๋ผ ๋ณํ์ํค๊ธฐ ์ํ ๋ณ์
time_offset: f64,
}
// =============================================================================
// 4. ๋ชจ๋ธ ์ด๊ธฐํ ํจ์ (Model Initialization)
// =============================================================================
// `model` ํจ์๋ ์ฑ ์์ ์ ํ ๋ฒ๋ง ํธ์ถ๋๋ฉฐ, ์ด๊ธฐ ๋ผ์ธ๋ค์ ์์ฑํฉ๋๋ค.
fn model(app: &App) -> Model {
// ์๋ก์ด ์ฐฝ์ ์์ฑํ๊ณ ์์ฑ์ ์ค์ ํฉ๋๋ค.
app.new_window()
.size(1000, 1000)
.title("Perlin Noise Flow Field - Line Deformation")
.view(view)
.build()
.unwrap();
// Perlin ๋
ธ์ด์ฆ ์์ฑ๊ธฐ๋ฅผ ์ด๊ธฐํํฉ๋๋ค.
let perlin = Perlin::new();
// ๋ผ์ธ ๋ฒกํฐ๋ฅผ ์์ฑํฉ๋๋ค.
let mut lines = Vec::new();
// ์ํ ๋ผ์ธ๋ค์ ์์ฑํฉ๋๋ค.
// y์ถ ๋ฐฉํฅ์ผ๋ก -n๋ถํฐ n๊น์ง nํฝ์
๊ฐ๊ฒฉ์ผ๋ก ๋ผ์ธ์ ๋ฐฐ์นํฉ๋๋ค.
// `step_by(n)`์ nํฝ์
๋ง๋ค ํ๋์ ๋ผ์ธ์ ์์ฑํฉ๋๋ค.
for y in (-500..=500).step_by(20) {
// ๊ฐ ์ํ ๋ผ์ธ์ ํ๋ฉด์ ์ผ์ชฝ(-n)์์ ์ค๋ฅธ์ชฝ(n)๊น์ง ๊ทธ๋ ค์ง๋๋ค.
// `segments: n`์ ๊ฐ ๋ผ์ธ์ n๊ฐ์ ์ ์ผ๋ก ๋๋๋๋ค.
// ์ ์ด ๋ง์์๋ก ๋ ๋ถ๋๋ฝ๊ณ ์ธ๋ฐํ ๋ณํ์ด ๊ฐ๋ฅํฉ๋๋ค.
lines.push(Line::new(
pt2(-500.0, y as f32),
pt2(500.0, y as f32),
100, // ๊ฐ ๋ผ์ธ์ n๊ฐ์ ์ธ๊ทธ๋จผํธ๋ก ๋ถํ
));
}
// ์์ง ๋ผ์ธ๋ค์ ์์ฑํฉ๋๋ค.
// x์ถ ๋ฐฉํฅ์ผ๋ก -n๋ถํฐ n๊น์ง nํฝ์
๊ฐ๊ฒฉ์ผ๋ก ๋ผ์ธ์ ๋ฐฐ์นํฉ๋๋ค.
for x in (-500..=500).step_by(20) {
// ๊ฐ ์์ง ๋ผ์ธ์ ํ๋ฉด์ ์๋(-n)์์ ์(n)๊น์ง ๊ทธ๋ ค์ง๋๋ค.
lines.push(Line::new(
pt2(x as f32, -500.0),
pt2(x as f32, 500.0),
100, // ๊ฐ ๋ผ์ธ์ n๊ฐ์ ์ธ๊ทธ๋จผํธ๋ก ๋ถํ
));
}
Model {
perlin,
lines,
time_offset: 0.0,
}
}
// =============================================================================
// 5. ์
๋ฐ์ดํธ ํจ์ (Per-Frame Update Logic)
// =============================================================================
// `update` ํจ์๋ ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋์ด ๋
ธ์ด์ฆ ํ๋๋ฅผ ์
๋ฐ์ดํธํ๊ณ
// ๋ชจ๋ ๋ผ์ธ์ ๋ณํ์ํต๋๋ค.
fn update(_app: &App, model: &mut Model, _update: Update) {
// ์๊ฐ ์คํ์
์ ์ฆ๊ฐ์์ผ ๋
ธ์ด์ฆ ํ๋๋ฅผ ๋ณํ์ํต๋๋ค.
// 0.01์ ์ ๋๋ฉ์ด์
์๋๋ฅผ ์กฐ์ ํ๋ ๊ฐ์
๋๋ค.
// - ๊ฐ์ด ํด์๋ก ๋น ๋ฅด๊ฒ ๋ณํํฉ๋๋ค.
// - ๊ฐ์ด ์์์๋ก ์ฒ์ฒํ, ๋ถ๋๋ฝ๊ฒ ๋ณํํฉ๋๋ค.
model.time_offset += 0.005;
// ๊ฐ ๋ผ์ธ์ ๋
ธ์ด์ฆ ํ๋์ ๋ฐ๋ผ ๋ณํ์ํต๋๋ค.
for line in &mut model.lines {
// `strength: 100.0`์ ๋ณํ์ ๊ฐ๋์
๋๋ค.
// ์ด ๊ฐ์ด ํด์๋ก ๋ผ์ธ์ด ๋ ๋ง์ด ํ์ด์ง๋๋ค.
line.update(&model.perlin, model.time_offset, 100.0);
}
}
// =============================================================================
// 6. ๋ทฐ ํจ์ (Rendering Logic)
// =============================================================================
// `view` ํจ์๋ ๋งค ํ๋ ์๋ง๋ค ํ๋ฉด์ ๊ทธ๋ฆฝ๋๋ค.
fn view(app: &App, model: &Model, frame: Frame) {
// ๋๋ก์ฐ ์ปจํ
์คํธ๋ฅผ ์์ฑํฉ๋๋ค.
let draw = app.draw();
// ๋ฐฐ๊ฒฝ์์ HSLA ๋ชจ๋๋ก ์ค์ ํฉ๋๋ค.
draw.background().color(hsla(0.0, 0.0, 0.02, 1.0));
// ๋ชจ๋ ๋ผ์ธ์ ๊ทธ๋ฆฝ๋๋ค.
for line in &model.lines {
// ๋ผ์ธ์ ๊ฐ ์ธ๊ทธ๋จผํธ๋ฅผ ๊ทธ๋ฆฝ๋๋ค.
// ๋ณํ๋ ์ ๋ค์ ์ํํ๋ฉด์ ์ฐ์๋ ๋ ์ ์ฌ์ด์ ์ ์ ๊ทธ๋ฆฝ๋๋ค.
// `.windows(2)`๋ ๋ฒกํฐ์ ์ฐ์๋ ๋ ์์๋ฅผ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ก ์ํํฉ๋๋ค.
// ์: [A, B, C, D] -> [A, B], [B, C], [C, D]
for window in line.deformed_points.windows(2) {
// window[0]์ ์ ์ ์์์ , window[1]์ ๋์ ์
๋๋ค.
draw.line()
.start(window[0])
.end(window[1])
.weight(0.5) // ์ ์ ๋๊ป 1ํฝ์
.color(rgba(1.0, 1.0, 1.0, 0.8)); // RGBA ๋ชจ๋: ์ฝ๊ฐ ํฌ๋ช
ํ ํฐ์
// R=1.0, G=1.0, B=1.0 (ํฐ์), A=0.8 (80% ๋ถํฌ๋ช
)
}
}
// ๋๋ก์ฐ ์ปจํ
์คํธ์ ๋ด์ฉ์ ํ๋ ์ ๋ฒํผ์ ๋ ๋๋งํฉ๋๋ค.
draw.to_frame(app, &frame).unwrap();
}