๐Ÿ”ฎ :: Spinning Spiral Pulse

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

Nannou <Generative Art>

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

๐Ÿ“ Rust Code

use nannou::prelude::*;
use nannou::noise::*; // ํŽ„๋ฆฐ ๋…ธ์ด์ฆˆ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€

struct Model {
    angle: f32,
    spiral_params: SpiralParams,
    noise: Perlin, // ํŽ„๋ฆฐ ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ ์ถ”๊ฐ€
    noise_seed: u32, // ๋…ธ์ด์ฆˆ ์‹œ๋“œ (์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณ€ํ™”์‹œํ‚ฌ ๊ฒƒ)
}

struct SpiralParams {
    turns: f32,
    points_count: usize,
    spacing: f32,
    max_radius: f32,
    noise_strength: f32, // ๋…ธ์ด์ฆˆ ๊ฐ•๋„ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ถ”๊ฐ€
    noise_scale: f32, // ๋…ธ์ด์ฆˆ ์Šค์ผ€์ผ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ถ”๊ฐ€
}

impl Default for SpiralParams {
    fn default() -> Self {
        Self {
            turns: 22.0,
            points_count: 3000,
            spacing: 3.0,
            max_radius: 500.0,
            noise_strength: 15.0, // ๋…ธ์ด์ฆˆ์— ์˜ํ•œ ๋ฐ˜์ง€๋ฆ„ ๋ณ€ํ™” ์ตœ๋Œ€ ๊ฐ•๋„
            noise_scale: 0.02, // ๋…ธ์ด์ฆˆ์˜ ๊ณต๊ฐ„์  ์Šค์ผ€์ผ (๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ๋” ์„ธ๋ฐ€ํ•œ ๋ณ€ํ™”)
        }
    }
}

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

fn model(app: &App) -> Model {
    let _window = app
        .new_window()
        .title("ํ”๋“ค๋ฆฌ๋Š” ๋‚˜์„ ")
        .size(800, 800)
        .view(view)
        .build()
        .unwrap();

    Model {
        angle: 0.0,
        spiral_params: SpiralParams::default(),
        noise: Perlin::new(), // ํŽ„๋ฆฐ ๋…ธ์ด์ฆˆ ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™”
        noise_seed: 0, // ์ดˆ๊ธฐ ๋…ธ์ด์ฆˆ ์‹œ๋“œ
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.angle -= 0.01;
    model.noise_seed += 1; // ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋…ธ์ด์ฆˆ ์‹œ๋“œ ์ฆ๊ฐ€ (์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ)
}

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

    // ๋ฐฐ๊ฒฝ
    draw.background().color(BLACK);

    // ํšŒ์ „์„ ์ ์šฉํ•œ ์ƒˆ๋กœ์šด Draw ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    let draw = draw.rotate(model.angle);

    // ๋‚˜์„  ๊ทธ๋ฆฌ๊ธฐ
    let params = &model.spiral_params;
    let max_t = params.turns * TAU;

    for i in 0..params.points_count - 1 {
        let t1 = ((i as f32) / ((params.points_count - 1) as f32)) * max_t;
        let t2 = (((i + 1) as f32) / ((params.points_count - 1) as f32)) * max_t;

        // ๊ธฐ๋ณธ ๋ฐ˜์ง€๋ฆ„ ๊ณ„์‚ฐ
        let base_r1 = params.spacing * t1;
        let base_r2 = params.spacing * t2;

        if base_r1 > params.max_radius {
            break;
        }

        // ํŽ„๋ฆฐ ๋…ธ์ด์ฆˆ๋ฅผ ์ ์šฉํ•œ ๋ฐ˜์ง€๋ฆ„ ๊ณ„์‚ฐ
        // ๋…ธ์ด์ฆˆ ์ž…๋ ฅ: ๊ฐ๋„(t)์™€ ์‹œ๊ฐ„(noise_seed)์„ ํ•จ๊ป˜ ์‚ฌ์šฉ
        let noise_val1 = model.noise.get([
            (t1 as f64) * (params.noise_scale as f64),
            (model.noise_seed as f64) * 0.01, // ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ณ€ํ™”
        ]) as f32;

        let noise_val2 = model.noise.get([
            (t2 as f64) * (params.noise_scale as f64),
            (model.noise_seed as f64) * 0.01, // ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ๋ณ€ํ™”
        ]) as f32;

        // ๋…ธ์ด์ฆˆ ๊ฐ•๋„ ์ ์šฉ (-1.0 ~ 1.0 ๋ฒ”์œ„์˜ ๋…ธ์ด์ฆˆ๋ฅผ ์›ํ•˜๋Š” ๊ฐ•๋„๋กœ ์Šค์ผ€์ผ๋ง)
        let r1 = base_r1 + noise_val1 * params.noise_strength;
        let r2 = base_r2 + noise_val2 * params.noise_strength;

        // ๊ทน์ขŒํ‘œ๊ณ„์—์„œ ๋ฐ์นด๋ฅดํŠธ ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜
        let p1 = pt2(r1 * t1.cos(), r1 * t1.sin());
        let p2 = pt2(r2 * t2.cos(), r2 * t2.sin());

        let progress = (i as f32) / ((params.points_count - 1) as f32);
        let alpha = map_range(progress, 0.0, 1.0, 0.1, 0.9);

        let hue = map_range(progress, 0.0, 1.0, 0.8, 0.5);
        let saturation = map_range(progress, 0.0, 1.0, 0.3, 0.8);
        let lightness = map_range(progress, 0.0, 1.0, 0.3, 0.7);

        let weight = map_range(progress, 0.0, 1.0, 0.3, 2.5);

        draw.line().start(p1).end(p2).weight(weight).color(hsla(hue, saturation, lightness, alpha));
    }

    // ์ค‘์‹ฌ์ 
    draw.ellipse().radius(3.0).color(hsla(0.6, 0.8, 0.9, 0.8));

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

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

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