



SPACEBAR: ๊ฐํ(Blooming) ์์
R: ๋ฆฌ์ ํด์ ๋ค์ ์์
S: ์คํฌ๋ฆฐ์ท ์ ์ฅ
use nannou::prelude::*;
const WIDTH: f32 = 800.0;
const HEIGHT: f32 = 800.0;
const NUM_POINTS: usize = 20;
fn main() {
nannou::app(model)
.update(update)
.event(event)
.run();
}
struct MovingPoint {
pos: Vec2,
vel: Vec2,
}
struct Model {
points: Vec<MovingPoint>,
width: f32,
height: f32,
running: bool,
screenshot_count: u32, // ์คํฌ๋ฆฐ์ท ์นด์ดํฐ
}
fn model(app: &App) -> Model {
let _window_id = app
.new_window()
.size(WIDTH as u32, HEIGHT as u32)
.title("Constellation Bloom")
.view(view)
.build()
.unwrap();
let points = (0..NUM_POINTS)
.map(|_| MovingPoint {
pos: vec2(0.0, 0.0),
vel: vec2(0.0, 0.0),
})
.collect();
Model {
points,
width: WIDTH,
height: HEIGHT,
running: false,
screenshot_count: 0,
}
}
fn update(_app: &App, model: &mut Model, update: Update) {
if !model.running {
return;
}
let dt = update.since_last.as_secs_f32();
let half_w = model.width / 2.0;
let half_h = model.height / 2.0;
for mp in &mut model.points {
mp.pos += mp.vel * dt;
// X์ถ ๋ฐ์ฌ
if mp.pos.x > half_w {
let overflow = mp.pos.x - half_w;
mp.pos.x = half_w - overflow;
mp.vel.x *= -1.0;
} else if mp.pos.x < -half_w {
let overflow = -half_w - mp.pos.x;
mp.pos.x = -half_w + overflow;
mp.vel.x *= -1.0;
}
// Y์ถ ๋ฐ์ฌ
if mp.pos.y > half_h {
let overflow = mp.pos.y - half_h;
mp.pos.y = half_h - overflow;
mp.vel.y *= -1.0;
} else if mp.pos.y < -half_h {
let overflow = -half_h - mp.pos.y;
mp.pos.y = -half_h + overflow;
mp.vel.y *= -1.0;
}
}
}
fn event(app: &App, model: &mut Model, event: Event) {
if let Event::WindowEvent { simple, .. } = event {
if let Some(e) = simple {
match e {
KeyPressed(Key::Space) => {
for mp in &mut model.points {
mp.vel = vec2(
random_range(-150.0, 150.0),
random_range(-150.0, 150.0),
);
}
model.running = true;
}
KeyPressed(Key::R) => {
for mp in &mut model.points {
mp.pos = vec2(0.0, 0.0);
mp.vel = vec2(0.0, 0.0);
}
model.running = false;
}
KeyPressed(Key::S) => {
model.screenshot_count += 1;
let filename = format!("artwork_{:03}.png", model.screenshot_count);
app.main_window().capture_frame(filename);
println!("๐ธ Screenshot saved!");
}
_ => {}
}
}
}
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
let overlay = hsla(0.0, 0.0, 0.0, 0.04);
draw.rect().w_h(model.width, model.height).color(overlay);
let white = hsla(0.0, 0.0, 1.0, 1.0);
for win in model.points.windows(2) {
let p1 = win[0].pos;
let p2 = win[1].pos;
draw.line().start(p1).end(p2).stroke_weight(3.0).color(white);
}
draw.to_frame(app, &frame).unwrap();
}
// nannou::prelude::* ๋ nannou ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ ๋ ํ์ํ
// ๊ธฐ๋ณธ์ ์ธ ํจ์, ํ์
, ๊ตฌ์กฐ์ฒด ๋ฑ์ ๋ชจ๋ ๊ฐ์ ธ์ค๋ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์
๋๋ค.
// ์ด๊ฑธ ์ฌ์ฉํ๋ฉด app, vec2, hsla ๋ฑ nannou์ ์ฌ๋ฌ ๊ธฐ๋ฅ์ ๋ฐ๋ก ์ธ ์ ์์ต๋๋ค.
use nannou::prelude::*;
// --- ์์ ์ ์ ---
// ์์๋ ํ๋ก๊ทธ๋จ ์ ์ฒด์์ ๋ณํ์ง ์๋ ๊ฐ์ ์ ์ฅํ ๋ ์ฌ์ฉํฉ๋๋ค.
// ์์ฑ๋ ์ฐฝ(window)์ ๋๋น๋ฅผ 800.0 ํฝ์
๋ก ์ค์ ํฉ๋๋ค.
// f32๋ 32๋นํธ ๋ถ๋์์์ ์ซ์ ํ์
์ผ๋ก, ์์์ ์ ํฌํจํ๋ ์ซ์๋ฅผ ๋ค๋ฃฐ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
const WIDTH: f32 = 800.0;
// ์์ฑ๋ ์ฐฝ์ ๋์ด๋ฅผ 800.0 ํฝ์
๋ก ์ค์ ํฉ๋๋ค.
const HEIGHT: f32 = 800.0;
// ํ๋ฉด์ ํ์ํ๊ณ ์์ง์ผ ์ ์ ๊ฐ์๋ฅผ 20๊ฐ๋ก ์ค์ ํฉ๋๋ค.
// usize๋ ๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ ํฌ๊ธฐ๋ฅผ ๋ํ๋ผ ๋ ์ฃผ๋ก ์ฌ์ฉ๋๋ ๋ถํธ ์๋ ์ ์ ํ์
์
๋๋ค.
const NUM_POINTS: usize = 20;
// --- main ํจ์ ---
// ๋ชจ๋ Rust ํ๋ก๊ทธ๋จ์ ์์์ ์
๋๋ค. ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ฉด ์ด ํจ์๊ฐ ๊ฐ์ฅ ๋จผ์ ํธ์ถ๋ฉ๋๋ค.
fn main() {
// nannou ์ฑ์ ์ค์ ํ๊ณ ์คํํฉ๋๋ค.
nannou::app(model) // 'model' ํจ์๋ฅผ ์ฌ์ฉํด ์ฑ์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ ํฉ๋๋ค.
.update(update) // ๋งค ํ๋ ์๋ง๋ค 'update' ํจ์๋ฅผ ํธ์ถํ์ฌ ์ฑ์ ์ํ๋ฅผ ๊ฐฑ์ ํฉ๋๋ค.
.event(event) // ํค๋ณด๋ ์
๋ ฅ, ๋ง์ฐ์ค ์์ง์ ๋ฑ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด 'event' ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
.run(); // ์์์ ์ค์ ํ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ฑ์ ์คํํฉ๋๋ค.
}
// --- ๋ฐ์ดํฐ ๊ตฌ์กฐ์ฒด ์ ์ ---
// struct(๊ตฌ์กฐ์ฒด)๋ ๊ด๋ จ๋ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ๋จ์๋ก ๋ฌถ๋ ์ฌ์ฉ์ ์ ์ ํ์
์
๋๋ค.
// ์์ง์ด๋ ์ ํ๋๋ฅผ ํํํ๊ธฐ ์ํ ๊ตฌ์กฐ์ฒด์
๋๋ค.
struct MovingPoint {
// pos๋ ์ ์ ํ์ฌ ์์น๋ฅผ ๋ํ๋
๋๋ค. Vec2๋ 2์ฐจ์ ๋ฒกํฐ(x, y ์ขํ)๋ฅผ ์ ์ฅํ๋ ํ์
์
๋๋ค.
pos: Vec2,
// vel์ ์ ์ ์๋(velocity)๋ฅผ ๋ํ๋
๋๋ค. x์ถ๊ณผ y์ถ ๋ฐฉํฅ์ผ๋ก ์ผ๋ง๋ ๋น ๋ฅด๊ฒ ์์ง์ด๋์ง๋ฅผ ์ ์ฅํฉ๋๋ค.
vel: Vec2,
}
// ์ด ํ๋ก๊ทธ๋จ์ ์ ์ฒด ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฉ์ธ ๊ตฌ์กฐ์ฒด์
๋๋ค.
// ์ฑ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ ์ด 'Model' ์์ ์ ์ฅ๋ฉ๋๋ค.
struct Model {
// ํ๋ฉด์ ์๋ ๋ชจ๋ ์ ๋ค์ ์ ์ฅํ๋ ๋ฒกํฐ(๋์ ๋ฐฐ์ด)์
๋๋ค.
points: Vec<MovingPoint>,
// ์ฐฝ์ ๋๋น๋ฅผ ์ ์ฅํฉ๋๋ค.
width: f32,
// ์ฐฝ์ ๋์ด๋ฅผ ์ ์ฅํฉ๋๋ค.
height: f32,
// ์ ๋๋ฉ์ด์
์ด ํ์ฌ ์คํ ์ค์ธ์ง(true) ์๋๋ฉด ๋ฉ์ถฐ์๋์ง(false) ์ํ๋ฅผ ์ ์ฅํฉ๋๋ค.
running: bool,
// ์คํฌ๋ฆฐ์ท์ ์ ์ฅํ ๋ ํ์ผ ์ด๋ฆ์ด ๊ฒน์น์ง ์๋๋ก ์ซ์๋ฅผ ์ธ๋ ์นด์ดํฐ์
๋๋ค.
screenshot_count: u32,
}
// --- nannou ํจ์๋ค ---
// ์ด ํจ์๋ค์ nannou ํ๋ ์์ํฌ์ ์ํด ํน์ ์์ ์ ์๋์ผ๋ก ํธ์ถ๋ฉ๋๋ค.
// 1. model ํจ์: ์ฑ์ด ์ฒ์ ์์๋ ๋ ๋ฑ ํ ๋ฒ ํธ์ถ๋ฉ๋๋ค.
// ์ฑ์ ์ด๊ธฐ ์ํ(Model ๊ตฌ์กฐ์ฒด)๋ฅผ ์ค์ ํ๊ณ ๋ฐํํ๋ ์ญํ ์ ํฉ๋๋ค.
fn model(app: &App) -> Model {
// ์ฑ์ ๋ฉ์ธ ์ฐฝ์ ์์ฑํ๊ณ ์ค์ ํฉ๋๋ค.
app.new_window()
.size(WIDTH as u32, HEIGHT as u32) // ์ฐฝ ํฌ๊ธฐ๋ฅผ ์์๋ก ์ค์ ํฉ๋๋ค. u32๋ก ํ์
๋ณํ์ด ํ์ํฉ๋๋ค.
.title("Constellation Bloom") // ์ฐฝ์ ์ ๋ชฉ์ ์ค์ ํฉ๋๋ค.
.view(view) // ํ๋ฉด์ ๊ทธ๋ฆฌ๋ ์ญํ ์ 'view' ํจ์๊ฐ ๋ด๋นํ๋๋ก ์ง์ ํฉ๋๋ค.
.build() // ์ค์ ์ ๋ฐํ์ผ๋ก ์ฐฝ์ ์ค์ ๋ก ์์ฑํฉ๋๋ค.
.unwrap(); // ์ฐฝ ์์ฑ์ด ์คํจํ ๊ฒฝ์ฐ ํ๋ก๊ทธ๋จ์ ์ค๋จ์ํต๋๋ค.
// NUM_POINTS ๊ฐ์๋งํผ MovingPoint๋ฅผ ์์ฑํ์ฌ ๋ฒกํฐ์ ๋ด์ต๋๋ค.
let points = (0..NUM_POINTS) // 0๋ถํฐ NUM_POINTS-1 ๊น์ง ๋ฐ๋ณตํฉ๋๋ค.
.map(|_| {
// ๊ฐ ์ ์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ ํฉ๋๋ค.
MovingPoint {
pos: vec2(0.0, 0.0), // ์ด๊ธฐ ์์น๋ ํ๋ฉด ์ค์(0, 0)์
๋๋ค.
vel: vec2(0.0, 0.0), // ์ด๊ธฐ ์๋๋ 0์ด๋ฏ๋ก ์ฒ์์๋ ์์ง์ด์ง ์์ต๋๋ค.
}
})
.collect(); // map์ ํตํด ์์ฑ๋ ๋ชจ๋ MovingPoint๋ค์ ๋ชจ์ Vec<MovingPoint> ๋ฒกํฐ๋ก ๋ง๋ญ๋๋ค.
// ์์ฑ๋ Model ๊ตฌ์กฐ์ฒด๋ฅผ ์์ฑํ์ฌ ๋ฐํํฉ๋๋ค. ์ด๊ฒ์ด ์ฑ์ ์ด๊ธฐ ์ํ๊ฐ ๋ฉ๋๋ค.
Model {
points, // ์์์ ์์ฑํ ์ ๋ค์ ๋ฒกํฐ
width: WIDTH,
height: HEIGHT,
running: false, // ์ฒ์์๋ ์ ๋๋ฉ์ด์
์ด ๋ฉ์ถฐ์๋๋ก false๋ก ์ค์ ํฉ๋๋ค.
screenshot_count: 0, // ์คํฌ๋ฆฐ์ท ์นด์ดํฐ ์ด๊ธฐํ
}
}
// 2. update ํจ์: ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋ฉ๋๋ค. (๋ณดํต 1์ด์ 60๋ฒ)
// ์ฑ์ ์ํ(๋ฐ์ดํฐ)๋ฅผ ๋ณ๊ฒฝํ๋ ๋ก์ง์ ๋ด๋นํฉ๋๋ค. (์: ์ ์ ์์น ์ด๋)
fn update(_app: &App, model: &mut Model, update: Update) {
// model.running์ด false์ด๋ฉด (์ ๋๋ฉ์ด์
์ด ๋ฉ์ถ ์ํ๋ผ๋ฉด)
// ์๋ฌด๊ฒ๋ ์
๋ฐ์ดํธํ์ง ์๊ณ ํจ์๋ฅผ ์ฆ์ ์ข
๋ฃํฉ๋๋ค.
if !model.running {
return;
}
// ์ด์ ํ๋ ์๊ณผ ํ์ฌ ํ๋ ์ ์ฌ์ด์ ์๊ฐ ๊ฐ๊ฒฉ(์ด ๋จ์)์ ๊ตฌํฉ๋๋ค.
// ์ด ๊ฐ์ ์ฌ์ฉํ๋ฉด ์ปดํจํฐ ์ฑ๋ฅ(ํ๋ ์๋ฅ )์ ์๊ด์์ด ์ผ์ ํ ์๋๋ก ์์ง์ด๊ฒ ํ ์ ์์ต๋๋ค.
let dt = update.since_last.as_secs_f32();
// ํ๋ฉด ๊ฒฝ๊ณ ๊ณ์ฐ์ ํธ๋ฆฌํ๊ฒ ํ๊ธฐ ์ํด ๋๋น์ ๋์ด์ ์ ๋ฐ ๊ฐ์ ๋ฏธ๋ฆฌ ๊ณ์ฐํด ๋ก๋๋ค.
// nannou์ ์ขํ๊ณ๋ ์ค์์ด (0,0)์ด๋ฏ๋ก, ๊ฒฝ๊ณ๋ -width/2 ~ +width/2 ์
๋๋ค.
let half_w = model.width / 2.0;
let half_h = model.height / 2.0;
// model์ ์๋ ๋ชจ๋ ์ ๋ค์ ์ํํ๋ฉด์ ์์น๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
// '&mut model.points'๋ ์ ๋ค์ ๋ฐ์ดํฐ๋ฅผ '์์ ๊ฐ๋ฅํ๋๋ก' ๋น๋ ค์ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
for mp in &mut model.points {
// ํ์ฌ ์์น(pos)์ ์๋(vel)์ ์๊ฐ ๊ฐ๊ฒฉ(dt)์ ๊ณฑํ ๊ฐ์ ๋ํด ๋ค์ ์์น๋ฅผ ๊ณ์ฐํฉ๋๋ค.
// (์ด๋ ๊ฑฐ๋ฆฌ = ์๋ * ์๊ฐ)
mp.pos += mp.vel * dt;
// --- ํ๋ฉด ๊ฒฝ๊ณ ์ถฉ๋ ์ฒ๋ฆฌ ---
// X์ถ ์ค๋ฅธ์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ชํ์ ๋
if mp.pos.x > half_w {
let overflow = mp.pos.x - half_w; // ๊ฒฝ๊ณ๋ฅผ ์ผ๋ง๋ ๋์ด๊ฐ๋์ง ๊ณ์ฐ
mp.pos.x = half_w - overflow; // ๋์ด๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์์น๋ฅผ ์กฐ์
mp.vel.x *= -1.0; // x์ถ ์ด๋ ๋ฐฉํฅ์ ๋ฐ๋๋ก ๋ฐ๊ฟ๋๋ค (๋ฐ์ฌ).
}
// X์ถ ์ผ์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ชํ์ ๋
else if mp.pos.x < -half_w {
let overflow = -half_w - mp.pos.x; // ๊ฒฝ๊ณ๋ฅผ ์ผ๋ง๋ ๋์ด๊ฐ๋์ง ๊ณ์ฐ
mp.pos.x = -half_w + overflow; // ๋์ด๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์์น๋ฅผ ์กฐ์
mp.vel.x *= -1.0; // x์ถ ์ด๋ ๋ฐฉํฅ์ ๋ฐ๋๋ก ๋ฐ๊ฟ๋๋ค.
}
// Y์ถ ์์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ชํ์ ๋
if mp.pos.y > half_h {
let overflow = mp.pos.y - half_h; // ๊ฒฝ๊ณ๋ฅผ ์ผ๋ง๋ ๋์ด๊ฐ๋์ง ๊ณ์ฐ
mp.pos.y = half_h - overflow; // ๋์ด๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์์น๋ฅผ ์กฐ์
mp.vel.y *= -1.0; // y์ถ ์ด๋ ๋ฐฉํฅ์ ๋ฐ๋๋ก ๋ฐ๊ฟ๋๋ค.
}
// Y์ถ ์๋์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ชํ์ ๋
else if mp.pos.y < -half_h {
let overflow = -half_h - mp.pos.y; // ๊ฒฝ๊ณ๋ฅผ ์ผ๋ง๋ ๋์ด๊ฐ๋์ง ๊ณ์ฐ
mp.pos.y = -half_h + overflow; // ๋์ด๊ฐ ๋งํผ ์์ชฝ์ผ๋ก ์์น๋ฅผ ์กฐ์
mp.vel.y *= -1.0; // y์ถ ์ด๋ ๋ฐฉํฅ์ ๋ฐ๋๋ก ๋ฐ๊ฟ๋๋ค.
}
}
}
// 3. event ํจ์: ํค๋ณด๋, ๋ง์ฐ์ค ๋ฑ ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ํธ์ถ๋ฉ๋๋ค.
// ์ฌ์ฉ์ ์
๋ ฅ์ ๋ฐ๋ผ ์ฑ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์ญํ ์ ํฉ๋๋ค.
fn event(app: &App, model: &mut Model, event: Event) {
// ๋ฐ์ํ ์ด๋ฒคํธ๊ฐ '์ฐฝ(Window)๊ณผ ๊ด๋ จ๋ ์ด๋ฒคํธ'์ธ์ง ํ์ธํฉ๋๋ค.
if let Event::WindowEvent { simple, .. } = event {
// 'simple' ๋ณ์์ ํค๋ณด๋/๋ง์ฐ์ค ์
๋ ฅ ๊ฐ์ ๋จ์ํ๋ ์ด๋ฒคํธ ์ ๋ณด๊ฐ ๋ด๊ฒจ์๋์ง ํ์ธํฉ๋๋ค.
if let Some(e) = simple {
// 'e'์ ๋ด๊ธด ์ด๋ฒคํธ์ ์ข
๋ฅ์ ๋ฐ๋ผ ๋ค๋ฅธ ๋์์ ์ํํฉ๋๋ค.
match e {
// ์คํ์ด์ค๋ฐ๊ฐ ๋๋ ธ์ ๋
KeyPressed(Key::Space) => {
// ๋ชจ๋ ์ ๋ค์ ์ํํ๋ฉด์
for mp in &mut model.points {
// ๊ฐ ์ ์ ์๋(vel)๋ฅผ x, y ๊ฐ๊ฐ -150 ~ 150 ์ฌ์ด์ ๋ฌด์์ ๊ฐ์ผ๋ก ์ค์ ํฉ๋๋ค.
mp.vel = vec2(
random_range(-150.0, 150.0),
random_range(-150.0, 150.0),
);
}
// ์ ๋๋ฉ์ด์
์ ์์ํ๋๋ก running ์ํ๋ฅผ true๋ก ๋ณ๊ฒฝํฉ๋๋ค.
model.running = true;
}
// 'R' ํค๊ฐ ๋๋ ธ์ ๋ (Reset)
KeyPressed(Key::R) => {
// ๋ชจ๋ ์ ๋ค์ ์ํํ๋ฉด์
for mp in &mut model.points {
// ์์น๋ฅผ ๋ค์ ํ๋ฉด ์ค์(0,0)์ผ๋ก ์ด๊ธฐํํฉ๋๋ค.
mp.pos = vec2(0.0, 0.0);
// ์๋๋ฅผ 0์ผ๋ก ๋ง๋ค์ด ์์ง์์ ๋ฉ์ถฅ๋๋ค.
mp.vel = vec2(0.0, 0.0);
}
// ์ ๋๋ฉ์ด์
์ ๋ฉ์ถ๋๋ก running ์ํ๋ฅผ false๋ก ๋ณ๊ฒฝํฉ๋๋ค.
model.running = false;
}
// 'S' ํค๊ฐ ๋๋ ธ์ ๋ (Screenshot)
KeyPressed(Key::S) => {
// ์คํฌ๋ฆฐ์ท ์นด์ดํฐ๋ฅผ 1 ์ฆ๊ฐ์ํต๋๋ค.
model.screenshot_count += 1;
// "artwork_001.png", "artwork_002.png" ์ ๊ฐ์ ํ์์ ํ์ผ ์ด๋ฆ์ ๋ง๋ญ๋๋ค.
let filename = format!("artwork_{:03}.png", model.screenshot_count);
// ํ์ฌ ์ฐฝ์ ํ๋ฉด์ ์บก์ฒํ์ฌ ํด๋น ํ์ผ ์ด๋ฆ์ผ๋ก ์ ์ฅํฉ๋๋ค.
app.main_window().capture_frame(filename);
// ์ ์ฅ ์๋ฃ ๋ฉ์์ง๋ฅผ ์ฝ์์ ์ถ๋ ฅํฉ๋๋ค.
println!("๐ธ Screenshot saved!");
}
// ๊ทธ ์ธ ๋ค๋ฅธ ํค๊ฐ ๋๋ ธ์ ๋๋ ์๋ฌด๊ฒ๋ ํ์ง ์์ต๋๋ค.
_ => {}
}
}
}
}
// 4. view ํจ์: ๋งค ํ๋ ์๋ง๋ค 'update' ํจ์๊ฐ ๋๋ ํ ํธ์ถ๋ฉ๋๋ค.
// ํ์ฌ 'Model'์ ์ํ๋ฅผ ๋ฐํ์ผ๋ก ํ๋ฉด์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆฌ๋ ์ญํ ์ ํฉ๋๋ค.
fn view(app: &App, model: &Model, frame: Frame) {
// ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ์ ์๋ ๋๊ตฌ(Draw ๊ฐ์ฒด)๋ฅผ ์ค๋นํฉ๋๋ค.
let draw = app.draw();
// ๋งค ํ๋ ์๋ง๋ค ๋ฐํฌ๋ช
ํ ๊ฒ์์ ์ฌ๊ฐํ์ ํ๋ฉด ์ ์ฒด์ ๋ฎ์ด์์๋๋ค.
// alpha(ํฌ๋ช
๋) ๊ฐ์ด 0.04๋ก ๋งค์ฐ ๋ฎ๊ธฐ ๋๋ฌธ์, ์ด์ ํ๋ ์์ ์์์ด ํฌ๋ฏธํ๊ฒ ๋จ๋ ํจ๊ณผ๋ฅผ ์ค๋๋ค.
let overlay = hsla(0.0, 0.0, 0.0, 0.04); // ์์(H), ์ฑ๋(S), ๋ช
๋(L), ํฌ๋ช
๋(A)
draw.rect() // ์ฌ๊ฐํ์ ๊ทธ๋ฆฝ๋๋ค.
.w_h(model.width, model.height) // ์ฌ๊ฐํ์ ๋๋น์ ๋์ด๋ฅผ ์ฐฝ ํฌ๊ธฐ์ ๊ฐ๊ฒ ์ค์ ํฉ๋๋ค.
.color(overlay); // ์์์ ์ ์ํ ๋ฐํฌ๋ช
๊ฒ์์์ ์น ํฉ๋๋ค.
// ์ ์ ๊ทธ๋ฆด ๋ ์ฌ์ฉํ ํฐ์์ ์ ์ํฉ๋๋ค.
let white = hsla(0.0, 0.0, 1.0, 1.0); // ๋ช
๋๊ฐ 1.0์ด๋ฉด ํฐ์, ํฌ๋ช
๋๋ 1.0(๋ถํฌ๋ช
)
// 'windows(2)'๋ ์ ๋ชฉ๋ก์์ ์ฐ์๋ ๋ ๊ฐ์ ์ ์ ๋ฌถ์ด์ ์ํํ๊ฒ ํด์ค๋๋ค.
// ์๋ฅผ ๋ค์ด ์ ์ด [p1, p2, p3, p4] ๋ผ๋ฉด, [p1,p2], [p2,p3], [p3,p4] ์์๋ก ๋ฐ๋ณตํฉ๋๋ค.
// ์ด๋ฅผ ํตํด ๊ฐ ์ ๊ณผ ๊ทธ ๋ค์ ์ ์ ์ ์ผ๋ก ์ฐ๊ฒฐํ ์ ์์ต๋๋ค.
for win in model.points.windows(2) {
let p1 = win[0].pos; // ์ฒซ ๋ฒ์งธ ์ ์ ์์น
let p2 = win[1].pos; // ๋ ๋ฒ์งธ ์ ์ ์์น
draw.line() // ์ ์ ๊ทธ๋ฆฝ๋๋ค.
.start(p1) // ์ ์ ์์์ ์ p1์ผ๋ก ์ค์
.end(p2) // ์ ์ ๋์ ์ p2๋ก ์ค์
.stroke_weight(3.0) // ์ ์ ๋๊ป๋ฅผ 3.0 ํฝ์
๋ก ์ค์
.color(white); // ์ ์ ์์์ ํฐ์์ผ๋ก ์ค์
}
// ์ง๊ธ๊น์ง 'draw'๋ฅผ ํตํด ๊ทธ๋ฆฐ ๋ชจ๋ ๋ด์ฉ์ ์ค์ ์ฐฝ์ ํ๋ ์์ ์ ์ฉํ์ฌ ํ๋ฉด์ ๋ณด์ฌ์ค๋๋ค.
draw.to_frame(app, &frame).unwrap();
}