:: BASIC_10_color_circle_following_cursor

BamgasiJM·2025년 8월 31일

Nannou <BASIC>

목록 보기
23/41
post-thumbnail

📝 Rust Code

use nannou::prelude::*;

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

struct Model {
    circles: Vec<Circle>,
}

struct Circle {
    pos: Point2,
    radius: f32,
    hue: f32,
}

fn model(app: &App) -> Model {
    app.new_window()
        .size(800, 800)
        .view(view)
        .build()
        .unwrap();
    Model { circles: Vec::new() }
}

fn update(app: &App, model: &mut Model, _update: Update) {
    let pos = app.mouse.position();
    let circle = Circle {
        pos,
        radius: 10.0, 
        hue: (app.elapsed_frames() as f32) % 360.0,
    };
    model.circles.push(circle);

    for c in &mut model.circles {
        c.radius *= 1.02;
    }

    model.circles.retain(|c| c.radius < 200.0);
}

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

    for c in &model.circles {
        let color = hsl(c.hue / 360.0, 0.7, 0.5);
        draw.ellipse()
            .xy(c.pos)
            .radius(c.radius)
            .color(color) 
            .stroke(BLACK)
            .stroke_weight(1.0);
    }

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

📝 Rust Code + Comment

// Nannou의 기본 기능을 사용하기 위한 모듈 임포트
use nannou::prelude::*;

// 프로그램 진입점: Nannou 애플리케이션 설정 및 실행
fn main() {
    nannou::app(model).update(update).run();
}

// 애플리케이션의 전체 상태를 저장하는 구조체
// 여러 개의 원(Circle)을 벡터로 관리
struct Model {
    circles: Vec<Circle>,
}

// 각 원의 속성을 정의하는 구조체
struct Circle {
    pos: Point2,   // 원의 중심 좌표 (x, y)
    radius: f32,   // 원의 반지름
    hue: f32,      // 색상의 색조(Hue) — 0.0 ~ 360.0 범위 (도 단위)
}

// 애플리케이션 초기화 함수
fn model(app: &App) -> Model {
    // 새 창 생성 및 설정
    app.new_window()
        .size(800, 800)   // 정사각형 창 (800x800 픽셀)
        .view(view)       // 렌더링 콜백으로 `view` 함수 지정
        .build()
        .unwrap();

    // 초기 상태: 빈 원 목록
    Model { circles: Vec::new() }
}

// 매 프레임 상태를 업데이트하는 함수
fn update(app: &App, model: &mut Model, _update: Update) {
    // 현재 마우스 커서 위치를 가져와 새 원 생성
    let pos = app.mouse.position();
    let circle = Circle {
        pos,
        radius: 10.0, // 초기 반지름은 10.0
        // 색조(hue)를 프레임 수에 따라 변경 → 시간이 지남에 따라 색상이 순환
        hue: (app.elapsed_frames() as f32) % 360.0,
    };
    // 새 원을 벡터 끝에 추가
    model.circles.push(circle);

    // 모든 원의 반지름을 2%씩 증가시켜 "확장" 효과 구현
    for c in &mut model.circles {
        c.radius *= 1.02; // 매 프레임 1.02배 → 부드러운 확대 애니메이션
    }

    // 반지름이 200.0 이상인 원은 화면에서 제거 (성능 및 시각적 과부하 방지)
    // `retain`은 조건을 만족하는 요소만 남기고 나머지를 제거함
    model.circles.retain(|c| c.radius < 200.0);
}

// 매 프레임 화면을 그리는 함수
fn view(app: &App, model: &Model, frame: Frame) {
    let draw = app.draw();

    // 배경을 매우 어두운 회색으로 설정 (HSLA 색상 사용)
    // hsla(색조, 채도, 밝기, 투명도)
    draw.background().color(hsla(0.0, 0.0, 0.01, 1.0));

    // 저장된 모든 원을 순회하며 그리기
    for c in &model.circles {
        // HSL 색상 모델로 색상 생성:
        // - hue: 0.0~1.0 범위로 정규화 (원래는 0~360도)
        // - saturation(채도): 0.7 → 선명한 색상
        // - lightness(밝기): 0.5 → 중간 밝기
        let color = hsl(c.hue / 360.0, 0.7, 0.5);

        // 타원(원) 그리기:
        draw.ellipse()
            .xy(c.pos)           // 중심 위치
            .radius(c.radius)    // 반지름
            .color(color)        // 채우기 색상
            .stroke(BLACK)       // 테두리 색상: 검정
            .stroke_weight(1.0); // 테두리 두께: 1.0
    }

    // 모든 그리기 명령을 프레임에 적용
    draw.to_frame(app, &frame).unwrap();
}

🔍 app.elapsed_frames()

  • 애플리케이션이 시작된 이후 경과된 프레임 수를 반환합니다.
  • 타입: u64 (부호 없는 64비트 정수)
  • 사용 예:
let hue = (app.elapsed_frames() as f32) % 360.0;

: 이 코드에서는 프레임 수를 색상의 색조(Hue) 값으로 사용합니다.
: % 360.0을 통해 0 ~ 360도 사이의 순환 값을 얻어, 시간이 지남에 따라 색상이 무지개처럼 순환하게 됩니다.

  • 특징:
    • 매 프레임마다 1씩 증가 (60 FPS면 1초에 60 증가)
    • 애니메이션 타이밍, 색상 변화, 패턴 생성 등에 유용하게 사용됨

🔍 retain(|c| ...)

Vec<T>의 메서드로, 주어진 조건을 만족하는 요소만 남기고 나머지를 제거합니다.

  • 문법:
vec.retain(|item| 조건식);

: 조건식이 true이면 해당 요소 유지, false이면 제거

  • 이 코드에서의 사용:
model.circles.retain(|c| c.radius < 200.0);

: 반지름이 200.0 미만인 원만 유지
: 반지름이 200.0 이상인 원은 화면에서 자동 삭제

  • 장점:
    • 메모리 누수 방지 (끝없이 원이 쌓이지 않음)
    • 성능 유지 (너무 많은 도형을 그리지 않음)
    • 시각적 혼잡도 감소

💡 retain은 가변 참조(&mut Vec)에서만 사용 가능하며, 내부적으로 효율적으로 요소를 필터링합니다.
일반적인 filter().collect()와 달리 기존 벡터를 직접 수정하므로 메모리 재할당이 적습니다.


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

0개의 댓글