๐Ÿ”ฎ :: Spinning Spiral Basic

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

Nannou <Generative Art>

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

๐Ÿ“ Rust Code

use nannou::prelude::*;

struct Model {
    angle: f32,
    spiral_params: SpiralParams,
}

struct SpiralParams {
    turns: f32,
    points_count: usize,
    spacing: f32,
    max_radius: f32,
}

impl Default for SpiralParams {
    fn default() -> Self {
        Self {
            turns: 22.0,
            points_count: 3000,
            spacing: 3.0,
            max_radius: 500.0,
        }
    }
}

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

fn model(app: &App) -> Model {
    let _window = app.new_window().title("Spiral").size(800, 800).view(view).build().unwrap();

    Model {
        angle: 0.0,
        spiral_params: SpiralParams::default(),
    }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.angle -= 0.1;
}

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

    draw.background().color(BLACK);

    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 r1 = params.spacing * t1;
        let r2 = params.spacing * t2;

        if r1 > params.max_radius {
            break;
        }

        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();
}

๐Ÿ“ Rust Code + Comment

use nannou::prelude::*; // nannou ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค

// ๋ฉ”์ธ ๋ชจ๋ธ ๊ตฌ์กฐ์ฒด - ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
struct Model {
    angle: f32, 			     // ํ˜„์žฌ ํšŒ์ „ ๊ฐ๋„ (๋ผ๋””์•ˆ)
    spiral_params: SpiralParams, // ๋‚˜์„ ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์„ ์ €์žฅํ•˜๋Š” ๊ตฌ์กฐ์ฒด
}

// ๋‚˜์„  ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ตฌ์กฐ์ฒด - ๋‚˜์„ ์˜ ๋ชจ์–‘์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ’๋“ค์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค
struct SpiralParams {
    turns: f32, 		 // ๋‚˜์„ ์˜ ํšŒ์ „ ์ˆ˜ (๋ช‡ ๋ฐ”ํ€ด ๋Œ ๊ฒƒ์ธ์ง€)
    points_count: usize, // ๋‚˜์„ ์„ ๊ตฌ์„ฑํ•˜๋Š” ์ ์˜ ๊ฐœ์ˆ˜
    spacing: f32, 		 // ๋‚˜์„  ์ ๋“ค ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ (๋‚˜์„ ์˜ ํŒฝ์ฐฝ ์ •๋„)
    max_radius: f32, 	 // ๋‚˜์„ ์˜ ์ตœ๋Œ€ ๋ฐ˜์ง€๋ฆ„ (ํ™”๋ฉด์—์„œ ๋ณด์—ฌ์งˆ ์ตœ๋Œ€ ํฌ๊ธฐ)
}

// SpiralParams์˜ ๊ธฐ๋ณธ๊ฐ’ ๊ตฌํ˜„
impl Default for SpiralParams {
    fn default() -> Self {
        Self {
            turns: 22.0, 		// ๊ธฐ๋ณธ์ ์œผ๋กœ 22๋ฐ”ํ€ด ํšŒ์ „ํ•˜๋Š” ๋‚˜์„ 
            points_count: 3000, // 3000๊ฐœ์˜ ์ ์œผ๋กœ ๋‚˜์„  ๊ตฌ์„ฑ
            spacing: 3.0, 	 	// ์  ๊ฐ„๊ฒฉ 3.0
            max_radius: 500.0,  // ์ตœ๋Œ€ ๋ฐ˜์ง€๋ฆ„ 500.0
        }
    }
}

// === ๋ฉ”์ธ ํ•จ์ˆ˜ - ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹œ์ž‘์ 
fn main() {
    nannou::app(model)  // ๋ชจ๋ธ ์ƒ์„ฑ ํ•จ์ˆ˜ ์ง€์ •
        .update(update) // ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ ์ง€์ •
        .run(); 	    // ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
}

// === ๋ชจ๋ธ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ - ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ํ˜ธ์ถœ๋จ
fn model(app: &App) -> Model {
    // ์ƒˆ๋กœ์šด ์œˆ๋„์šฐ ์ƒ์„ฑ
    let _window = app
        .new_window()
        .title("Spiral") // ์œˆ๋„์šฐ ์ œ๋ชฉ
        .size(800, 800)  // ์œˆ๋„์šฐ ํฌ๊ธฐ (800x800 ํ”ฝ์…€)
        .view(view) 	 // ๋ทฐ ํ•จ์ˆ˜ ์ง€์ • (ํ™”๋ฉด ๊ทธ๋ฆฌ๊ธฐ)
        .build() 	     // ์œˆ๋„์šฐ ๋นŒ๋“œ
        .unwrap();  	 // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ํŒจ๋‹‰

    // ์ดˆ๊ธฐ ๋ชจ๋ธ ๋ฐ˜ํ™˜
    Model {
        angle: 0.0, // ์‹œ์ž‘ ๊ฐ๋„๋Š” 0๋ผ๋””์•ˆ
        spiral_params: SpiralParams::default(), // ๊ธฐ๋ณธ ๋‚˜์„  ๋งค๊ฐœ๋ณ€์ˆ˜ ์‚ฌ์šฉ
    }
}

// === ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ - ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜์–ด ๋ชจ๋ธ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
fn update(_app: &App, model: &mut Model, _update: Update) {
    model.angle -= 0.1; // ๊ฐ๋„๋ฅผ 0.1๋ผ๋””์•ˆ์”ฉ ๊ฐ์†Œ (์‹œ๊ณ„ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ ํšŒ์ „)
}

// === ๋ทฐ ํ•จ์ˆ˜ - ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ํ•จ์ˆ˜, ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ํ˜ธ์ถœ๋จ
fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw(); // ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ Draw ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    
    // ** ๋ฐฐ๊ฒฝ ๊ทธ๋ฆฌ๊ธฐ
    draw.background().color(BLACK); // ๊ฒ€์€์ƒ‰ ๋ฐฐ๊ฒฝ์œผ๋กœ ์ฑ„์šฐ๊ธฐ
    
    // ** ํšŒ์ „์„ ์ ์šฉํ•œ ์ƒˆ๋กœ์šด Draw ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    // ๊ธฐ์กด draw ์ธ์Šคํ„ด์Šค์— ํšŒ์ „ ๋ณ€ํ™˜์„ ์ ์šฉํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ƒˆ๋กœ์šด draw ๋ณ€์ˆ˜์— ์ €์žฅ
    let draw = draw.rotate(model.angle);
    
    // ** ๋‚˜์„  ๊ทธ๋ฆฌ๊ธฐ
    let params = &model.spiral_params; // ๋‚˜์„  ๋งค๊ฐœ๋ณ€์ˆ˜ ์ฐธ์กฐ
    let max_t = params.turns * TAU;    // ์ตœ๋Œ€ ๊ฐ๋„ ๊ณ„์‚ฐ (TAU = 2ฯ€, ์™„์ „ํ•œ ์›)
    
    // ** ๋‚˜์„ ์„ ๊ตฌ์„ฑํ•˜๋Š” ์„ ๋ถ„๋“ค ๊ทธ๋ฆฌ๊ธฐ
    for i in 0..(params.points_count - 1) {
        // ํ˜„์žฌ ์ ๊ณผ ๋‹ค์Œ ์ ์˜ ๊ฐ๋„ ๊ณ„์‚ฐ (0๋ถ€ํ„ฐ max_t๊นŒ์ง€ ์„ ํ˜• ๋ณด๊ฐ„)
        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 r1 = params.spacing * t1;
        let r2 = params.spacing * t2;
        
        // ์ตœ๋Œ€ ๋ฐ˜์ง€๋ฆ„์„ ๋„˜์–ด๊ฐ€๋ฉด ๋ฃจํ”„ ์ข…๋ฃŒ (๋” ์ด์ƒ ๊ทธ๋ฆด ํ•„์š” ์—†์Œ)
        if r1 > params.max_radius { break; }
        
        // ๊ทน์ขŒํ‘œ๊ณ„์—์„œ ๋ฐ์นด๋ฅดํŠธ ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜ (x = rยทcosฮธ, y = rยทsinฮธ)
        let p1 = pt2(r1 * t1.cos(), r1 * t1.sin()); // ํ˜„์žฌ ์  ์œ„์น˜
        let p2 = pt2(r2 * t2.cos(), r2 * t2.sin()); // ๋‹ค์Œ ์  ์œ„์น˜
        
        // ์ง„ํ–‰๋ฅ  ๊ณ„์‚ฐ (0.0์—์„œ 1.0 ์‚ฌ์ด์˜ ๊ฐ’)
        let progress = i as f32 / (params.points_count - 1) as f32;
        
        // ํˆฌ๋ช…๋„ ๊ณ„์‚ฐ: ์ง„ํ–‰๋ฅ ์— ๋”ฐ๋ผ 0.1์—์„œ 0.9๋กœ ๋ณ€ํ™”
        let alpha = map_range(progress, 0.0, 1.0, 0.1, 0.9);
        
        // ์ƒ‰์ƒ ๊ณ„์‚ฐ: HSL ์ƒ‰๊ณต๊ฐ„ ์‚ฌ์šฉ
        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);  // ๋ช…๋„: ์–ด๋‘์šด ์ƒ‰์—์„œ ๋ฐ์€ ์ƒ‰์œผ๋กœ
        
        // ์„  ๋‘๊ป˜ ๊ณ„์‚ฐ: ์ง„ํ–‰๋ฅ ์— ๋”ฐ๋ผ 0.3์—์„œ 2.5๋กœ ๋ณ€ํ™”
        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)); // ์„  ์ƒ‰์ƒ (HSL + Alpha)
    }
    
    // ** ์ค‘์‹ฌ์  ๊ทธ๋ฆฌ๊ธฐ - ๋‚˜์„ ์˜ ์ค‘์‹ฌ์— ์ž‘์€ ์›์„ ๊ทธ๋ฆผ
    draw.ellipse()
        .radius(3.0) // ๋ฐ˜์ง€๋ฆ„ 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๊ฐœ์˜ ๋Œ“๊ธ€