๐Ÿ”ฎ :: Dynamic Color Grid ver.2

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

Nannou <Generative Art>

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

๐Ÿ“ Rust Code

use nannou::prelude::*;
use rand::Rng;

struct Model {
    color_coefficients: Vec<f32>, // ๊ฐ ๊ทธ๋ฆฌ๋“œ ์…€์˜ hue ๊ณ„์ˆ˜ ๊ฐ’์„ ์ €์žฅ
    grid_size: usize,             // ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ (N x N)
    max_grid_size: usize,         // ์ตœ๋Œ€ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ (2^8 = 256)
    show_too_dense: bool,         // Too Dense ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ ์—ฌ๋ถ€\
    animation_offset: Vec<f32>    // ๊ฐ ์…€์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ ์‹œ๊ฐ„์„ ๋‹ค๋ฅด๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ์˜คํ”„์…‹
}

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

fn model(app: &App) -> Model {
    app.new_window()
        .size(1000, 1000)
        .view(view)
        .key_pressed(key_pressed)
        .build()
        .unwrap();
    
    // ์ดˆ๊ธฐ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ๋ฅผ 2๋กœ ์„ค์ •, ์ตœ๋Œ€ ํฌ๊ธฐ๋Š” 2^8 = 256
    let grid_size = 2;
    let max_grid_size = 256; // 2^8
    let total_cells = grid_size * grid_size;
    
    // ๋žœ๋คํ•œ hue ๊ณ„์ˆ˜ ๊ฐ’์œผ๋กœ ๊ทธ๋ฆฌ๋“œ ์ƒ‰์ƒ ์ดˆ๊ธฐํ™”
    let mut rng = rand::thread_rng();
    let color_coefficients = (0..total_cells).map(|_| rng.gen_range(0.0..1.0)).collect();
    
    // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์˜คํ”„์…‹ ๋ฒกํ„ฐ๋ฅผ ๋žœ๋คํ•˜๊ฒŒ ์ƒ์„ฑ
    // PI * 2.0์œผ๋กœ ํ•œ ์ฃผ๊ธฐ๋ฅผ ๊ตฌํ˜„
    let animation_offset = (0..total_cells).map(|_| rng.gen_range(0.0..PI * 2.0)).collect();
    
    Model {
        color_coefficients,
        grid_size,
        max_grid_size,
        show_too_dense: false,
        animation_offset,
    }
}

fn update(_app: &App, _model: &mut Model, _update: Update) {
    // Too Dense ๋ฉ”์‹œ์ง€๋ฅผ ์ผ์ • ์‹œ๊ฐ„ ํ›„์— ์‚ฌ๋ผ์ง€๊ฒŒ ํ•จ
    // ๊ฐ„๋‹จํ•œ ๊ตฌํ˜„: ๋‹ค์Œ ํ”„๋ ˆ์ž„์—์„œ ๊ณ„์† ํ‘œ์‹œ
}

fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();
    let win = app.window_rect();
    
    // ๋ฐฐ๊ฒฝ์„ ๊ฒ€์€์ƒ‰์œผ๋กœ
    draw.background().color(BLACK);
    
    let grid_size = model.grid_size;
    let cell_width = win.w() / grid_size as f32;
    let cell_height = win.h() / grid_size as f32;
    
    for i in 0..grid_size {
        for j in 0..grid_size {
            let idx = i * grid_size + j;
            
            if idx >= model.color_coefficients.len() {
                continue;
            }
            
            // โœจ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋กœ์ง ์‹œ์ž‘
            let base_saturation = model.color_coefficients[idx];
            let animation_offset = model.animation_offset[idx];

            // app.time์— ๋”ฐ๋ผ -1.0 ~ 1.0 ์‚ฌ์ด๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š” ์‚ฌ์ธํŒŒ(sin) ๊ฐ’์„ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
            // ์—ฌ๊ธฐ์— ๊ฐ ์…€์˜ ๊ณ ์œ  ์˜คํ”„์…‹์„ ๋”ํ•ด ๋ชจ๋‘ ๋‹ค๋ฅธ ํƒ€์ด๋ฐ์— ์›€์ง์ด๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
            let sin_wave = (app.time + animation_offset).sin();
            
            // ์‚ฌ์ธํŒŒ ๊ฒฐ๊ณผ(-1.0 ~ 1.0)๋ฅผ 0.0 ~ 1.0 ๋ฒ”์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
            // (sin_wave + 1.0) -> 0.0 ~ 2.0  /  2.0 -> 0.0 ~ 1.0
            let time_coefficient = (sin_wave + 1.0) / 2.0;

            // ์ตœ์ข… ์ฑ„๋„๋Š” (๊ธฐ๋ณธ ์ฑ„๋„ * ์‹œ๊ฐ„ ๊ณ„์ˆ˜)๋กœ ๊ณ„์‚ฐํ•˜์—ฌ ๋ฐ˜์ง์ด๋Š” ํšจ๊ณผ๋ฅผ ์ค๋‹ˆ๋‹ค.
            let color_factor = base_saturation * time_coefficient;
            // โœจ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋กœ์ง ๋

            let x = win.left() + cell_width * (j as f32 + 0.5);
            let y = win.top() - cell_height * (i as f32 + 0.5);
            
            draw.rect()
                .x_y(x, y)
                .w_h(cell_width, cell_height)
                // HSL์˜ S(์ฑ„๋„) ๊ฐ’์— ๋ฐฉ๊ธˆ ๊ณ„์‚ฐํ•œ animated_saturation์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
                .color(hsl(color_factor, color_factor, 0.7));
        }
    }
    
    // Too Dense ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
    if model.show_too_dense {
        draw.text("Too Dense")
            .x_y(0.0, 0.0)
            .color(WHITE)
            .font_size(48)
            .align_text_middle_y();
    }
    
    draw.to_frame(app, &frame).unwrap();
}

fn key_pressed(_app: &App, model: &mut Model, key: Key) {
    match key {
        Key::C => {
            // C ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋ชจ๋“  ์…€์˜ hue ๊ณ„์ˆ˜ ๊ฐ’์„ ๋žœ๋คํ•˜๊ฒŒ ๋ณ€๊ฒฝ
            let total_cells = model.grid_size * model.grid_size;
            
            // ์ƒ‰์ƒ ๊ณ„์ˆ˜ ๋ฐฐ์—ด ํฌ๊ธฐ๊ฐ€ ๋งž๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ํ•„์š”์‹œ resize
            if model.color_coefficients.len() != total_cells {
                model.color_coefficients.resize(total_cells, 0.0);
            }
            
            let mut rng = rand::thread_rng();
            for coefficient in &mut model.color_coefficients {
                *coefficient = rng.gen_range(0.0..1.0);
            }
            model.show_too_dense = false;
        }
        Key::Up => {
            // ์œ„ ํ™”์‚ดํ‘œ: ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ 2๋ฐฐ (์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ)
            if model.grid_size * 2 <= model.max_grid_size {
                model.grid_size *= 2;
                let total_cells = model.grid_size * model.grid_size;
                
                // ์ƒˆ๋กœ์šด ํฌ๊ธฐ์— ๋งž๊ฒŒ ์ƒ‰์ƒ ๊ณ„์ˆ˜ ๋ฐฐ์—ด ์žฌ์ƒ์„ฑ
                let mut rng = rand::thread_rng();
                model.color_coefficients = (0..total_cells)
                    .map(|_| rng.gen_range(0.0..1.0))
                    .collect();
                // ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋‹ˆ ์˜คํ”„์…‹๋„ ์ƒˆ๋กœ ์ƒ์„ฑ
                model.animation_offset = (0..total_cells)
                    .map(|_| rng.gen_range(0.0..PI * 2.0))
                    .collect();
                model.show_too_dense = false;
            } else {
                // ์ตœ๋Œ€ ํฌ๊ธฐ ์ดˆ๊ณผ์‹œ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
                model.show_too_dense = true;
            }
        }
        Key::Down => {
            // ์•„๋ž˜ ํ™”์‚ดํ‘œ: ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ ์ ˆ๋ฐ˜ (์ตœ์†Œ 1 ์œ ์ง€)
            if model.grid_size > 1 {
                model.grid_size /= 2;
                let total_cells = model.grid_size * model.grid_size;
                
                // ์ƒˆ๋กœ์šด ํฌ๊ธฐ์— ๋งž๊ฒŒ ์ƒ‰์ƒ ๊ณ„์ˆ˜ ๋ฐฐ์—ด ์žฌ์ƒ์„ฑ
                let mut rng = rand::thread_rng();
                model.color_coefficients = (0..total_cells)
                    .map(|_| rng.gen_range(0.0..1.0))
                    .collect();
                // ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋‹ˆ ์˜คํ”„์…‹๋„ ์ƒˆ๋กœ ์ƒ์„ฑ
                model.animation_offset = (0..total_cells)
                    .map(|_| rng.gen_range(0.0..PI * 2.0))
                    .collect();
            }
            model.show_too_dense = false;
        }
        _ => {}
    }
}
---
profile
Coding Art with Blender / oF / Processing / p5.js / nannou

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