๐Ÿ”ฎ :: Greeting White Dots

BamgasiJMยท2025๋…„ 9์›” 23์ผ

Nannou <Generative Art>

๋ชฉ๋ก ๋ณด๊ธฐ
28/55
post-thumbnail

๐Ÿ“ Rust Code

use nannou::prelude::*;

const WIN_SIZE: u32 = 1000;
const NUM_POINTS: usize = 1000;
const RANGE: f32 = 230.0; // ์ค‘์‹ฌ์ ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š” ์ตœ๋Œ€ ๊ฑฐ๋ฆฌ

struct MovingPoint {
    pos: Point2,
    speed: f32,
}

struct Model {
    points: Vec<MovingPoint>,
}

fn main() {
    nannou::app(model).update(update).run();
}

fn model(app: &App) -> Model {
    app.new_window()
        .size(WIN_SIZE, WIN_SIZE)
        .view(view)
        .build()
        .unwrap();

    let mut points = Vec::new();
    for _ in 0..NUM_POINTS {
        points.push(random_point());
    }

    Model { points }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    // ์ ๋“ค ์•„๋ž˜๋กœ ์ด๋™
    for p in &mut model.points {
        p.pos.y -= p.speed;
    }

    // ํ™”๋ฉด ์•„๋ž˜ ๋ฒ—์–ด๋‚œ ์  ์ œ๊ฑฐ
    model.points.retain(|p| p.pos.y > -(WIN_SIZE as f32) / 2.0 - 50.0);

    // ๋ถ€์กฑํ•œ ์  ์ฑ„์›Œ๋„ฃ๊ธฐ
    while model.points.len() < NUM_POINTS {
        model.points.push(random_point());
    }
}

fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();
    draw.background().color(BLACK);

    // ์ค‘์‹ฌ ํ•˜์–€ ์ 
    let center = pt2(0.0, 0.0);
    draw.ellipse()
        .xy(center)
        .radius(15.0)
        .color(WHITE);

    // ์ด๋™ ์ ๋“ค
    for p in &model.points {
        let dist = p.pos.distance(center);
        if dist < RANGE {
            draw.ellipse().xy(p.pos).radius(2.0).color(WHITE);
            draw.line()
                .start(center)
                .end(p.pos)
                .weight(0.3)
                .color(WHITE);
        } else {
            draw.ellipse().xy(p.pos).radius(2.0).color(GRAY);
        }
    }

    draw.to_frame(app, &frame).unwrap();
}

fn random_point() -> MovingPoint {
    let x = random_range(-(WIN_SIZE as f32) / 2.0, (WIN_SIZE as f32) / 2.0);
    let y = (WIN_SIZE as f32) / 2.0 + 20.0; // ํ™”๋ฉด ์œ„์—์„œ ์ƒ์„ฑ
    let speed = random_range(0.5, 2.0);

    MovingPoint {
        pos: pt2(x, y),
        speed,
    }
}

๐Ÿ“ Rust Code + Comment

// nannou::prelude::*๋Š” nannou๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋ณธ ์š”์†Œ๋“ค(ํ•จ์ˆ˜, ํƒ€์ž…, ๋ชจ๋“ˆ ๋“ฑ)์„ 
// ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
use nannou::prelude::*;

// ์ฐฝ(window)์˜ ๊ฐ€๋กœ, ์„ธ๋กœ ํฌ๊ธฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์ƒ์ˆ˜์ž…๋‹ˆ๋‹ค.
// ํƒ€์ž…์€ u32 (๋ถ€ํ˜ธ ์—†๋Š” 32๋น„ํŠธ ์ •์ˆ˜)์ด๋ฉฐ, ๊ฐ’์€ 800์ž…๋‹ˆ๋‹ค.
const WIN_SIZE: u32 = 800;

// ํ™”๋ฉด์— ํ‘œ์‹œ๋  ์ (point)์˜ ์ด ๊ฐœ์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ์ƒ์ˆ˜์ž…๋‹ˆ๋‹ค.
// ํƒ€์ž…์€ usize (๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๊ณต๊ฐ„์˜ ํฌ๊ธฐ์™€ ๊ฐ™์€ ๋ถ€ํ˜ธ ์—†๋Š” ์ •์ˆ˜)์ด๋ฉฐ, ๊ฐ’์€ 20์ž…๋‹ˆ๋‹ค.
// ๋ฒกํ„ฐ์˜ ์ธ๋ฑ์‹ฑ์ด๋‚˜ ๊ธธ์ด์™€ ๊ด€๋ จ๋œ ๊ฐ’์— ์ฃผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
const NUM_POINTS: usize = 20;

// ํ™”๋ฉด ์ค‘์•™์˜ ์›์œผ๋กœ๋ถ€ํ„ฐ ์ƒํ˜ธ์ž‘์šฉ์ด ์ผ์–ด๋‚˜๋Š” ์ตœ๋Œ€ ๊ฑฐ๋ฆฌ๋ฅผ ์ €์žฅํ•˜๋Š” ์ƒ์ˆ˜์ž…๋‹ˆ๋‹ค.
// ์ด ๊ฑฐ๋ฆฌ ์•ˆ์— ๋“ค์–ด์˜จ ์ ๋“ค์€ ์ค‘์•™๊ณผ ์„ ์œผ๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.
// ํƒ€์ž…์€ f32 (32๋น„ํŠธ ๋ถ€๋™์†Œ์ˆ˜์  ์ˆ˜)์ด๋ฉฐ, ๊ฐ’์€ 150.0์ž…๋‹ˆ๋‹ค.
const RANGE: f32 = 150.0;

// ์›€์ง์ด๋Š” ์  ํ•˜๋‚˜ํ•˜๋‚˜์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌ์กฐ์ฒด(struct)์ž…๋‹ˆ๋‹ค.
struct MovingPoint {
    // ์ ์˜ 2D ์œ„์น˜(x, y ์ขŒํ‘œ)๋ฅผ ์ €์žฅํ•˜๋Š” ํ•„๋“œ์ž…๋‹ˆ๋‹ค.
    // ํƒ€์ž…์€ Point2์ด๋ฉฐ, nannou์—์„œ ์ œ๊ณตํ•˜๋Š” 2์ฐจ์› ๋ฒกํ„ฐ ํƒ€์ž…(Vec2)์˜ ๋ณ„์นญ(alias)์ž…๋‹ˆ๋‹ค.
    pos: Point2,
    
    // ์ ์ด ์•„๋ž˜๋กœ ๋–จ์–ด์ง€๋Š” ์†๋„๋ฅผ ์ €์žฅํ•˜๋Š” ํ•„๋“œ์ž…๋‹ˆ๋‹ค.
    // ํƒ€์ž…์€ f32 (32๋น„ํŠธ ๋ถ€๋™์†Œ์ˆ˜์  ์ˆ˜)์ž…๋‹ˆ๋‹ค.
    speed: f32,
}

// ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฉ”์ธ ๋ชจ๋ธ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.
struct Model {
    // ์—ฌ๋Ÿฌ ๊ฐœ์˜ MovingPoint ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๋™์  ๋ฐฐ์—ด(Vector)์ž…๋‹ˆ๋‹ค.
    // ํ™”๋ฉด์— ํ‘œ์‹œ๋  ๋ชจ๋“  ์ ๋“ค์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์—ฌ๊ธฐ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
    points: Vec<MovingPoint>,
}

// Rust ํ”„๋กœ๊ทธ๋žจ์˜ ์‹œ์ž‘์ (entry point)์ž…๋‹ˆ๋‹ค.
fn main() {
    // nannou ์•ฑ์„ ์„ค์ •ํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    // app(model): ์•ฑ์˜ ์ดˆ๊ธฐ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด model ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    // .update(update): ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ์•ฑ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด update ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    // .run(): ์„ค์ •์ด ์™„๋ฃŒ๋œ ์•ฑ์˜ ๋ฉ”์ธ ๋ฃจํ”„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
    nannou::app(model).update(update).run();
}

// ์•ฑ์ด ์ฒ˜์Œ ์‹œ์ž‘๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜์–ด ์ดˆ๊ธฐ ๋ชจ๋ธ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
// app: &App -> ํ˜„์žฌ nannou ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์ฐธ์กฐ์ž…๋‹ˆ๋‹ค. ์ฐฝ ์ƒ์„ฑ ๋“ฑ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
// ๋ฐ˜ํ™˜ ๊ฐ’: ์ดˆ๊ธฐํ™”๋œ Model ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
fn model(app: &App) -> Model {
    // ์ƒˆ๋กœ์šด ์ฐฝ์„ ์ƒ์„ฑํ•˜๊ณ  ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    app.new_window()
        // .size(): ์ฐฝ์˜ ํฌ๊ธฐ๋ฅผ WIN_SIZE ์ƒ์ˆ˜๋ฅผ ์ด์šฉํ•ด 800x800 ํ”ฝ์…€๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        .size(WIN_SIZE, WIN_SIZE)
        // .view(): ๋งค ํ”„๋ ˆ์ž„ ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ์—ญํ• ์„ ํ•  view ํ•จ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
        .view(view)
        // .build(): ์œ„ ์„ค์ •๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ์ฐฝ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
        // .unwrap(): ์ฐฝ ์ƒ์„ฑ์ด ์‹คํŒจํ•  ๊ฒฝ์šฐ ํ”„๋กœ๊ทธ๋žจ์„ ์ค‘๋‹จ์‹œํ‚ต๋‹ˆ๋‹ค. (Result<T, E> ํƒ€์ž… ์ฒ˜๋ฆฌ)
        .build()
        .unwrap();

    // MovingPoint๋“ค์„ ๋‹ด์„ ๋น„์–ด์žˆ๋Š” ๋™์  ๋ฐฐ์—ด(Vector)์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    // mut ํ‚ค์›Œ๋“œ๋Š” ์ด ๋ณ€์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    let mut points = Vec::new();
    
    // NUM_POINTS (20)๋ฒˆ ๋งŒํผ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค.
    // _ ๋Š” ๋ฐ˜๋ณต ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.
    for _ in 0..NUM_POINTS {
        // random_point ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ ์„ ์ƒ์„ฑํ•˜๊ณ , points ๋ฒกํ„ฐ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
        points.push(random_point());
    }

    // ์ƒ์„ฑ๋œ points ๋ฒกํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” Model ๊ตฌ์กฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    // ์ด๊ฒƒ์ด ์•ฑ์˜ ์ดˆ๊ธฐ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
    Model { points }
}

// ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜์–ด ์•ฑ์˜ ๋…ผ๋ฆฌ(์ƒํƒœ)๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
// _app: &App -> ํ˜„์žฌ ์•ฑ์— ๋Œ€ํ•œ ์ฐธ์กฐ. ์ด ์ฝ”๋“œ์—์„œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์•„ _๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
// model: &mut Model -> ์•ฑ์˜ ์ƒํƒœ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” Model์— ๋Œ€ํ•œ '๊ฐ€๋ณ€' ์ฐธ์กฐ. ์ด ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// _update: Update -> ์ด์ „ ํ”„๋ ˆ์ž„ ์ดํ›„์˜ ์‹œ๊ฐ„ ๋“ฑ ์—…๋ฐ์ดํŠธ ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ์ฒด. ์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
fn update(_app: &App, model: &mut Model, _update: Update) {
    
    // model์˜ points ๋ฒกํ„ฐ์— ์žˆ๋Š” ๋ชจ๋“  ์ ๋“ค์„ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ๊ฐ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    // &mut model.points๋Š” ๋ฒกํ„ฐ์˜ ๊ฐ ์š”์†Œ(p)์— ๋Œ€ํ•œ ๊ฐ€๋ณ€ ์ฐธ์กฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.
    for p in &mut model.points {
        // ํ˜„์žฌ ์ (p)์˜ y ์ขŒํ‘œ๋ฅผ speed ๋งŒํผ ๊ฐ์†Œ์‹œ์ผœ ์•„๋ž˜๋กœ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค.
        p.pos.y -= p.speed;
    }

    // points ๋ฒกํ„ฐ์—์„œ ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š๋Š” ์š”์†Œ๋“ค์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    // retain ๋ฉ”์„œ๋“œ๋Š” ํด๋กœ์ €(closure)๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„, ํด๋กœ์ €๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์š”์†Œ๋งŒ ๋‚จ๊น๋‹ˆ๋‹ค.
    // |p| p.pos.y > -(WIN_SIZE as f32) / 2.0 - 50.0
    //   -> ์ ์˜ y์ขŒํ‘œ๊ฐ€ ํ™”๋ฉด ํ•˜๋‹จ(-400.0)๋ณด๋‹ค 50ํ”ฝ์…€ ๋” ์•„๋ž˜๋กœ ๋‚ด๋ ค๊ฐ€๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    //      (WIN_SIZE as f32)๋Š” u32 ํƒ€์ž…์˜ ์ƒ์ˆ˜๋ฅผ f32๋กœ ํ˜•๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    model.points.retain(|p| p.pos.y > -(WIN_SIZE as f32) / 2.0 - 50.0);

    // ์ ์ด ํ™”๋ฉด ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€ ์ œ๊ฑฐ๋œ ํ›„, ์ ์˜ ๊ฐœ์ˆ˜๊ฐ€ NUM_POINTS๋ณด๋‹ค ์ ์œผ๋ฉด ๋‹ค์‹œ ์ฑ„์›Œ ๋„ฃ์Šต๋‹ˆ๋‹ค.
    while model.points.len() < NUM_POINTS {
        // random_point ํ•จ์ˆ˜๋กœ ์ƒˆ๋กœ์šด ์ ์„ ์ƒ์„ฑํ•˜์—ฌ points ๋ฒกํ„ฐ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
        model.points.push(random_point());
    }
}

// ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜์–ด ํ˜„์žฌ ๋ชจ๋ธ ์ƒํƒœ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํ™”๋ฉด์— ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
// app: &App -> ํ˜„์žฌ ์•ฑ์— ๋Œ€ํ•œ ์ฐธ์กฐ. Draw ๊ฐ์ฒด๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
// model: &Model -> ํ˜„์žฌ ์•ฑ์˜ ์ƒํƒœ๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” Model์— ๋Œ€ํ•œ '๋ถˆ๋ณ€' ์ฐธ์กฐ. ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.
// frame: Frame -> ํ˜„์žฌ ๊ทธ๋ฆฌ๊ณ  ์žˆ๋Š” ํ”„๋ ˆ์ž„์— ๋Œ€ํ•œ ์ •๋ณด. ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ์„ ์—ฌ๊ธฐ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
fn view(app: &App, model: &Model, frame: Frame) {
    // ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ์œ„ํ•œ Draw ๊ฐ์ฒด๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ทธ๋ฆฌ๊ธฐ ์ž‘์—…์€ ์ด ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.
    let draw = app.draw();
    
    // ํ”„๋ ˆ์ž„์˜ ๋ฐฐ๊ฒฝ์„ ๊ฒ€์€์ƒ‰(BLACK)์œผ๋กœ ์น ํ•ฉ๋‹ˆ๋‹ค.
    draw.background().color(BLACK);

    // ํ™”๋ฉด์˜ ์ •์ค‘์•™ ์ขŒํ‘œ(0.0, 0.0)๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Point2๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    let center = pt2(0.0, 0.0);
    
    // ์ค‘์•™์— ๊ณ ์ •๋œ ์›์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
    draw.ellipse()
        // .xy(center): ์›์˜ ์ค‘์‹ฌ ์œ„์น˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        .xy(center)
        // .radius(12.0): ์›์˜ ๋ฐ˜์ง€๋ฆ„์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        .radius(12.0)
        // .color(WHITE): ์›์˜ ์ƒ‰์ƒ์„ ํฐ์ƒ‰์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
        .color(WHITE);

    // ๋ชจ๋ธ์˜ ๋ชจ๋“  ์ ๋“ค์„ ์ˆœํšŒํ•˜๋ฉฐ ํ™”๋ฉด์— ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
    for p in &model.points {
        // ํ˜„์žฌ ์ (p)๊ณผ ์ค‘์•™(center) ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
        let dist = p.pos.distance(center);
        
        // ๋งŒ์•ฝ ๊ฑฐ๋ฆฌ๊ฐ€ ๋ฏธ๋ฆฌ ์ •์˜๋œ RANGE (150.0)๋ณด๋‹ค ์ž‘๋‹ค๋ฉด,
        if dist < RANGE {
            // ์ ์„ ํฐ์ƒ‰ ์›์œผ๋กœ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
            draw.ellipse().xy(p.pos).radius(6.0).color(WHITE);
            
            // ์ค‘์•™(center)์—์„œ ํ˜„์žฌ ์ (p)๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์„ ์„ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
            draw.line()
                // .start(center): ์„ ์˜ ์‹œ์ž‘์ ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
                .start(center)
                // .end(p.pos): ์„ ์˜ ๋์ ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
                .end(p.pos)
                // .weight(1.0): ์„ ์˜ ๋‘๊ป˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
                .weight(1.0)
                // .color(WHITE): ์„ ์˜ ์ƒ‰์ƒ์„ ํฐ์ƒ‰์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
                .color(WHITE);
        } else {
            // ๊ฑฐ๋ฆฌ๊ฐ€ RANGE๋ณด๋‹ค ํฌ๋‹ค๋ฉด, ์ ์„ ํšŒ์ƒ‰(GRAY) ์›์œผ๋กœ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
            draw.ellipse().xy(p.pos).radius(6.0).color(GRAY);
        }
    }

    // ์ง€๊ธˆ๊นŒ์ง€์˜ ๋ชจ๋“  ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น๋“ค์„ ์‹ค์ œ ํ”„๋ ˆ์ž„์— ์ ์šฉํ•˜์—ฌ ํ™”๋ฉด์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
    draw.to_frame(app, &frame).unwrap();
}

// ์ƒˆ๋กœ์šด MovingPoint๋ฅผ ๋ฌด์ž‘์œ„ ์œ„์น˜์™€ ์†๋„๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ—ฌํผ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
// ๋ฐ˜ํ™˜ ๊ฐ’: ์ƒˆ๋กœ ์ƒ์„ฑ๋œ MovingPoint ์ธ์Šคํ„ด์Šค.
fn random_point() -> MovingPoint {
    // x ์ขŒํ‘œ๋ฅผ ํ™”๋ฉด์˜ ๊ฐ€๋กœ ๋ฒ”์œ„ (-400.0 ~ 400.0) ๋‚ด์—์„œ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
    let x = random_range(-(WIN_SIZE as f32) / 2.0, (WIN_SIZE as f32) / 2.0);
    
    // y ์ขŒํ‘œ๋ฅผ ํ™”๋ฉด ์ƒ๋‹จ(400.0)๋ณด๋‹ค 20ํ”ฝ์…€ ์œ„๋กœ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ ์ด ์œ„์—์„œ๋ถ€ํ„ฐ ๋‚˜ํƒ€๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    let y = (WIN_SIZE as f32) / 2.0 + 20.0;
    
    // ์†๋„๋ฅผ 0.5์—์„œ 2.0 ์‚ฌ์ด์˜ ๋ฌด์ž‘์œ„ ๊ฐ’์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
    let speed = random_range(0.5, 2.0);

    // ๊ณ„์‚ฐ๋œ ์œ„์น˜(x, y)์™€ ์†๋„(speed)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด MovingPoint ๊ตฌ์กฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    // pt2(x, y)๋Š” x, y ์ขŒํ‘œ๋กœ Point2 ํƒ€์ž…์„ ๋งŒ๋“œ๋Š” ๋‹จ์ถ• ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
    MovingPoint {
        pos: pt2(x, y),
        speed,
    }
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€