๐Ÿ”ฎ :: Connecting Dots - REFACTORING

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

Nannou <Generative Art>

๋ชฉ๋ก ๋ณด๊ธฐ
2/55
post-thumbnail
  • ๐Ÿ”ฎ :: Connecting Dots ver.2์˜ ๋ฆฌํŒฉํ† ๋ง
  • ์  ์‚ฌ์ด์— ๋”ฐ๋ผ์„œ ์„ ์˜ ์•ŒํŒŒ๊ฐ’์ด ๋‹ฌ๋ผ์ง€๋Š” ๋กœ์ง ์ถ”๊ฐ€
use nannou::prelude::*;
use std::path::Path;

// ===== ์ „์—ญ ์ƒ์ˆ˜ =====
const WINDOW_SIZE: u32 = 1200;
const HALF_WINDOW: f32 = (WINDOW_SIZE / 2) as f32;

const POINT_COLOR: (f32, f32, f32) = (1.0, 1.0, 1.0);
const LINE_COLOR: (f32, f32, f32, f32) = (1.0, 1.0, 1.0, 0.1);
const LINE_DISTANCE_THRESHOLD: f32 = 70.0;
const LINE_WEIGHT: f32 = 0.4;
const POINT_SPEED: f32 = 0.3;
const NUM_POINTS: usize = 3000;
// ====================

struct Point {
    position: Vec2,
    velocity: Vec2,
}

struct Model {
    points: Vec<Point>,
    counter: u32,
}

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

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

    let points = (0..NUM_POINTS)
        .map(|_| Point {
            position: pt2(
                random_range(-HALF_WINDOW, HALF_WINDOW),
                random_range(-HALF_WINDOW, HALF_WINDOW),
            ),
            velocity: pt2(
                random_range(-POINT_SPEED, POINT_SPEED),
                random_range(-POINT_SPEED, POINT_SPEED),
            ),
        })
        .collect();

    Model { points, counter: 1 }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    for point in &mut model.points {
        point.position += point.velocity;

        // ๊ฒฝ๊ณ„ ์ฒดํฌ ๋ฐ ๋ฐ˜์‚ฌ
        if point.position.x.abs() > HALF_WINDOW {
            point.velocity.x = -point.velocity.x;
            point.position.x = point.position.x.clamp(-HALF_WINDOW, HALF_WINDOW);
        }
        if point.position.y.abs() > HALF_WINDOW {
            point.velocity.y = -point.velocity.y;
            point.position.y = point.position.y.clamp(-HALF_WINDOW, HALF_WINDOW);
        }
    }
}

fn event(app: &App, model: &mut Model, event: Event) {
    if let Event::WindowEvent {
        simple: Some(WindowEvent::KeyPressed(key)),
        ..
    } = event
    {
        match key {
            Key::P => save_frame(app, model, "png"),
            Key::J => save_frame(app, model, "jpg"),
            _ => {}
        }
    }
}

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

    // ์  ๊ทธ๋ฆฌ๊ธฐ
    for point in &model.points {
        draw.ellipse().xy(point.position).radius(1.0).rgb(
            POINT_COLOR.0,
            POINT_COLOR.1,
            POINT_COLOR.2,
        );
    }

    // ์„  ๊ทธ๋ฆฌ๊ธฐ - ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ฅธ ์•ŒํŒŒ๊ฐ’ ๋ณ€ํ™”
    let threshold_sq = LINE_DISTANCE_THRESHOLD * LINE_DISTANCE_THRESHOLD;

    for i in 0..model.points.len() {
        for j in i + 1..model.points.len() {
            let p1 = &model.points[i];
            let p2 = &model.points[j];

            let dx = p2.position.x - p1.position.x;
            let dy = p2.position.y - p1.position.y;
            let distance_sq = dx * dx + dy * dy;

            if distance_sq < threshold_sq {
                // ๊ฑฐ๋ฆฌ์— ๋ฐ˜๋น„๋ก€ํ•˜๋Š” ์•ŒํŒŒ๊ฐ’ ๊ณ„์‚ฐ (๊ฐ€๊นŒ์šธ์ˆ˜๋ก ๋ถˆํˆฌ๋ช…)
                let distance = distance_sq.sqrt();
                let alpha = (1.0 - distance / LINE_DISTANCE_THRESHOLD) * LINE_COLOR.3;

                draw.line()
                    .start(p1.position)
                    .end(p2.position)
                    .weight(LINE_WEIGHT)
                    .rgba(LINE_COLOR.0, LINE_COLOR.1, LINE_COLOR.2, alpha);
            }
        }
    }

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

fn save_frame(app: &App, model: &mut Model, format: &str) {
    let filename = format!("output_{:04}.{}", model.counter, format);
    let path = Path::new(&filename);
    app.main_window().capture_frame(path);
    println!("Frame saved as {}", filename);
    model.counter += 1;
}
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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