:: BASIC_09_sine_curve

BamgasiJM·2025년 9월 19일

Nannou <BASIC>

목록 보기
22/41
post-thumbnail

📝 Rust Code

use nannou::prelude::*;

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

struct Model {
    time: f32,
}

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

    Model { time: 0.0 }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
    model.time += 0.02;
}

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

    draw.background().color(rgba(0.1, 0.1, 0.1, 1.0));

    let points: Vec<Point2> = (-50..=50)
        .map(|i| {
            let x = i as f32 * 10.0;
            let y = (x * 0.1 + model.time).sin() * 100.0;
            pt2(x, y)
        })
        .collect();

    draw.polyline()
        .weight(3.0)
        .color(rgba(0.0, 0.8, 1.0, 0.8))
        .points(points.clone());

    for (i, &point) in points.iter().enumerate() {
        let progress = i as f32 / points.len() as f32;
        let color = rgba(
            progress,                              
            0.5 + 0.5 * (model.time * 2.0).sin(), 
            1.0 - progress,                        
            0.9,                                   
        );

        draw.ellipse().xy(point).radius(5.0).color(color);
    }

    draw.line()
        .start(pt2(-500.0, 0.0))
        .end(pt2(500.0, 0.0))
        .weight(1.0)
        .color(rgba(1.0, 1.0, 1.0, 0.2));

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

📝 Rust Code + Comment

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

// 프로그램 진입점: Nannou 앱을 설정하고 실행
fn main() {
    nannou::app(model).update(update).run();
}

// 애플리케이션 상태를 저장하는 구조체
// `time`은 애니메이션의 진행 정도를 나타내는 누적 시간 변수
struct Model {
    time: f32,
}

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

    // 초기 상태 반환: 시간을 0.0부터 시작
    Model { time: 0.0 }
}

// 매 프레임 상태를 업데이트하는 함수
fn update(_app: &App, model: &mut Model, _update: Update) {
    // 매 프레임마다 시간을 0.02씩 증가시킴 → 애니메이션 속도 조절
    // 값이 클수록 사인파가 더 빠르게 움직임
    model.time += 0.02;
}

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

    // 배경을 짙은 회색으로 설정
    draw.background().color(rgba(0.1, 0.1, 0.1, 1.0));

    // 사인파를 구성할 점들의 좌표를 생성:
    // x 범위: -500 ~ +500 (i: -50 ~ +50, 간격 10)
    let points: Vec<Point2> = (-50..=50)
        .map(|i| {
            let x = i as f32 * 10.0;                     // x 좌표: -500 ~ +500
            let y = (x * 0.1 + model.time).sin() * 100.0; // y = sin(위상) * 진폭
            pt2(x, y)                                    // (x, y) 점 생성
        })
        .collect();

    // 사인파 곡선을 선으로 연결하여 그리기
    draw.polyline()
        .weight(3.0)                            // 선 두께: 3.0
        .color(rgba(0.0, 0.8, 1.0, 0.8))        // 청록색, 약간 투명
        .points(points.clone());                // 위에서 생성한 점들 사용

    // 사인파 위의 각 점에 원(ellipse)을 그림 — 색상은 점 위치와 시간에 따라 변화
    for (i, &point) in points.iter().enumerate() {
        // 진행도: 첫 번째 점은 0.0, 마지막 점은 1.0
        let progress = i as f32 / points.len() as f32;

        // 색상을 동적으로 계산:
        let color = rgba(
            progress,                              // 빨강: 왼쪽→오른쪽으로 증가
            0.5 + 0.5 * (model.time * 2.0).sin(),  // 초록: 시간에 따라 -0.5~+0.5 진동 → 0~1 범위
            1.0 - progress,                        // 파랑: 왼쪽→오른쪽으로 감소
            0.9,                                   // 알파: 거의 불투명
        );

        // 각 점 위치에 반지름 5.0인 원 그리기
        draw.ellipse().xy(point).radius(5.0).color(color);
    }

    // 중앙 가이드 라인: y = 0인 수평선 (사인파의 기준선)
    draw.line()
        .start(pt2(-500.0, 0.0))   // 왼쪽 끝
        .end(pt2(500.0, 0.0))     // 오른쪽 끝
        .weight(1.0)               // 얇은 선
        .color(rgba(1.0, 1.0, 1.0, 0.2)); // 흰색, 매우 투명

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

📐 사인(Sine)과 코사인(Cosine)의 원리

1. 기본 개념

  • 사인(sin)과 코사인(cos)은 삼각함수로, 원의 움직임을 수학적으로 표현하는 함수입니다.
  • 단위 원(반지름 = 1) 위의 점을 생각해 봅시다:
    • 각도 θ(라디안)만큼 회전한 위치의:
      x 좌표 = cos(θ)
      y 좌표 = sin(θ)

2. 파형으로서의 사인파

  • y = sin(x)는 x가 증가함에 따라 y가 -1에서 +1 사이를 주기적으로 진동하는 곡선입니다.
  • 이 곡선이 바로 사인파(sine wave)이며, 자연계의 진동, 음파, 빛 등에서 흔히 나타납니다.

3. 이 코드에서의 사인파 구조

let y = (x * 0.1 + model.time).sin() * 100.0;
  • x * 0.1: x 좌표를 압축 → 파형의 주기(한 주기 길이)를 조절
    (계수가 작을수록 파가 더 넓어짐)
  • + model.time: 시간이 지남에 따라 전체 파형이 왼쪽으로 이동 (애니메이션 효과)
  • * 100.0: 진폭(amplitude) → 파의 높이를 100픽셀로 확대

💡 왜 왼쪽으로 움직일까?
일반적으로 y = sin(x - vt)는 오른쪽 이동, y = sin(x + vt)는 왼쪽 이동입니다.
여기서 model.time이 증가하면 위상이 증가하므로, 파는 왼쪽으로 흐르는 것처럼 보입니다.

4. 코사인과의 차이

  • cos(θ) = sin(θ + π/2) → 코사인파는 사인파보다 ¼ 주기(90도) 앞선 모양입니다.
  • 시각적으로는 시작점이 최대값(1)인 파형입니다.

5. 사인/코사인의 주요 특성

  • 주기성: sin(θ + 2π) = sin(θ) → 2π(≈6.28)마다 반복
  • 범위: 항상 -1 ≤ sin(θ) ≤ 1
  • 부드러운 곡선: 미분 가능 → 자연스러운 애니메이션에 적합

이러한 특성 덕분에 사인파는 프로그래밍 아트, 게임, 오디오 처리 등에서 매우 자주 사용됩니다.


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

0개의 댓글