
📝 Rust Code - Animation
use nannou::prelude::*;
fn main() {
nannou::app(model).update(update).simple_window(view).size(1000, 500).run();
}
struct Model;
fn model(_app: &App) -> Model {
Model
}
fn update(_app: &App, _model: &mut Model, _update: Update) {
}
fn ease_in_out(t: f32) -> f32 {
3.0 * t.powi(2) - 2.0 * t.powi(3)
}
fn view(app: &App, _model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(rgba(0.0, 0.0, 0.0, 1.0));
let color = rgb(0.0, 1.0, 0.8);
let base_radius = 100.0;
let delay_per_circle = 5.0 / 60.0;
for (i,x) in [200.0, 400.0, 600.0, 800.0].iter().enumerate() {
let t = app.time - (i as f32) * delay_per_circle;
let cycle = t.rem_euclid(4.0) / 4.0;
let raw = if cycle < 0.5 {
cycle * 2.0
} else {
(1.0 - cycle) * 2.0
};
let eased = ease_in_out(raw);
let radius = base_radius * eased;
draw.ellipse()
.x_y(x - 500.0, 0.0)
.radius(radius)
.color(color);
}
draw.to_frame(app, &frame).unwrap();
}
코드 설명
1.
use nannou::prelude::*
- Rust 문법: use 키워드는 모듈이나 아이템을 현재 스코프로 가져옵니다.
- nannou:
nannou::prelude::*는 nannou에서 자주 쓰이는 타입, 함수, 트레이트 등을 한 번에 가져오는 "사전 설정된 모듈 집합"입니다. 예: App, Draw,Color, Rect, Point2 등이 포함됩니다.
- 의미: 이 한 줄로 nannou의 핵심 기능들을 간편하게 사용할 수 있게 됩니다.
2.
fn main() {
nannou::app(Model)
.update(update)
.simple_window(view)
.size(1000,500)
.run();
}
- Rust 문법: fn main()은 프로그램의 진입점입니다.
- nannou:
nannou::app()은 nannou 애플리케이션을 생성하는 시작점입니다. 이후 메서드 체이닝을 통해 앱의 동작을 구성합니다.
app(Model): 초기 상태(Model)를 생성하는 함수를 지정.
.update(update): 매 프레임마다 호출되는 업데이트 함수 지정.
.simple_window(view): 단일 윈도우를 만들고, 그릴 때 view 함수를 사용하도록 설정.
.size(1000, 500): 윈도우 크기를 1000×500 픽셀로 설정.
.run(): 앱을 실행 (이벤트 루프 시작).
- 의미: nannou 앱의 구조를 설정하고 실행합니다.
3.
struct Model;
- Rust 문법:
struct는 사용자 정의 데이터 타입을 정의합니다. 여기서는 유닛 구조체(Unit struct)로, 필드가 없습니다.
- 의미: 앱의 상태를 저장할 Model이라는 타입을 정의했습니다. 현재는 상태가 없기 때문에 비어 있습니다.
4.
fn model(_app: &App) -> Model {
Model
- Rust 문법:
fn은 함수 정의.
_app: &App: App 타입의 참조를 인자로 받지만, _ 접두사는 "사용하지 않음"을 의미하여 경고를 방지.
-> Model: 반환 타입이 Model임을 명시.
- nannou:
model 함수는 앱 시작 시 호출되어 초기 상태를 반환합니다.
- 의미: Model 인스턴스를 생성하여 초기 상태를 제공합니다. 현재는 상태가 없으므로 단순히 Model을 반환.
5.
fn update(_app: &App, _model: &mut Model, _update: Update) {
}
- Rust 문법:
_app: &App: 앱 정보에 대한 참조 (사용 안 함).
_model: &mut Model: 상태 Model에 대한 가변 참조 (수정 가능).
_update: Update: Update 이벤트 정보 (시간, 델타 타임 등).
_ 접두사는 변수를 사용하지 않음을 나타내며, 컴파일러 경고를 피함.
- nannou:
update 함수는 매 프레임마다 호출되며, 상태를 업데이트하는 데 사용됩니다.
- 의미: 현재는 아무것도 하지 않음. 상태 변화 없음 → 애니메이션은
view에서 시간 기반으로 처리됨.
6.
fn ease_in_out(t: f32) -> f32 {
3.0 * t.powi(2) - 2.0 * t.powi(3)
}
- Rust 문법:
fn ease_in_out(...) -> f32: f32 타입의 실수를 받아 f32를 반환하는 함수.
t.powi(2): t의 제곱 (정수 지수 제곱, powi = power integer).
- 수학: 이 함수는 smoothstep 함수와 동일한 형태입니다. 입력이 0→1일 때, 출력이 천천히 시작하고 천천히 끝나는 S자 곡선을 만듭니다.
- 의미: 애니메이션의 움직임을 부드럽게 만들기 위한 이징(easing) 함수.
7.
fn view(app: &App, _model: &Model, frame: Frame) {
- Rust 문법:
app: &App: 앱 정보 참조 (시간, 윈도우 크기 등 접근 가능).
_model: &Model: 현재 상태 참조 (읽기 전용).
frame: Frame: 현재 프레임 버퍼 (그릴 대상).
- nannou: view 함수는 매 프레임마다 화면을 그리는 데 사용됩니다.
- 의미: 시각적 출력을 정의하는 함수.
8.
let draw = app.draw();
- nannou:
app.draw()는 Draw 객체를 생성합니다. 이 객체로 도형, 텍스트 등을 그릴 수 있습니다.
- 의미: 그리기 명령을 모아서 나중에 프레임에 렌더링할 준비를 합니다.
9.
draw.background().color(BLACK);
- nannou:
draw.background()는 배경 설정.
.color(BLACK)으로 검은색 배경을 지정.
BLACK은 nannou에서 정의된 상수 색상.
- 의미: 화면 전체를 검은색으로 채워 이전 프레임을 지우고 깔끔한 시작.
10.
let mint = rgb(0.0, 1.0, 0.8);
- Rust/nannou:
let mint: 불변 변수 mint 선언.
rgb(r, g, b): nannou의 prelude에서 제공하는 함수로, RGB 색상을 생성 (값은 0.0~1.0 사이).
- 의미: 밝은 청록색(민트색)을 정의합니다.
11.
let base_radius = 100.0;
let y = 0.0;
- Rust:
let으로 두 개의 f32 타입 변수를 정의 (Rust는 타입 추론).
- 의미:
base_radius: 원의 기본 반지름.
y = 0.0: 모든 원이 같은 y축 위치(가로 기준 중앙)에 있도록 함.
12.
let delay_per_circle = 5.0 / 60.0;
- 의미: 각 원이 다음 원보다 5프레임 늦게 애니메이션을 시작하도록 지연 시간을 계산.
- 수학: 60fps 기준 5프레임 = 5/60 ≈ 0.083초.
- 단위: 초 단위의 지연 시간.
13.
for (i, x) in [200.0, 400.0, 600.0, 800.0].iter().enumerate() {
- Rust 문법:
[200.0, ...]: 고정 배열.
.iter(): 배열의 반복자 생성 (각 원소를 참조).
.enumerate(): 인덱스 i와 값 x를 쌍으로 제공.
- 의미: 네 개의 x 위치에서 각각 원을 그리며, 인덱스 i로 지연 시간을 조절.
14.
let t = app.time - i as f32 * delay_per_circle;
- Rust:
i as f32: 정수 i를 실수로 변환.
* delay_per_circle: i번째 원의 지연 시간 계산.
- nannou: app.time은 앱이 시작된 후 흐른 시간(초 단위, f32).
- 의미: 각 원은 i에 따라 점점 더 늦게 애니메이션 시작.
15.
let cycle = (t.rem_euclid(4.0)) / 4.0;
- Rust:
.rem_euclid(4.0): 양수 나머지 연산 (음수도 처리). 주기를 4초로 반복.
/ 4.0: 결과를 0.0~1.0 범위로 정규화.
- 의미: 시간 t를 4초 주기의 0~1 사이 값으로 변환.
16.
let raw = if cycle < 0.5 {
cycle * 2.0
} else {
(1.0 - cycle) * 2.0
};
- Rust: if 표현식은 값을 반환하므로 let에 바로 할당 가능.
- 수학: 삼각파(triangle wave) 생성.
cycle < 0.5: 0→1로 증가.
cycle >= 0.5: 1→0으로 감소.
- 결과:
raw는 0→1→0을 반복하는 값.
17.
let eased = ease_in_out(raw);
- 의미: 직선적인 삼각파에 ease_in_out 함수를 적용해 부드러운 곡선으로 변형.
- 결과: 원의 크기가 더 자연스럽게 커졌다가 작아짐.
18.
let radius = base_radius * eased;
- 의미: 기본 반지름에 이징된 값을 곱해 현재 반지름을 계산.
19.
draw.ellipse()
.x_y(x - 500.0, y)
.radius(radius)
.color(mint);
- nannou:
draw.ellipse(): 원(타원) 그리기 명령 시작.
.x_y(x - 500.0, y): 위치 설정. x는 원래 좌표지만, x - 500.0은 좌표계를 중앙 기준으로 조정하기 위함.
- nannou의 기본 좌표계는 윈도우 중심이 (0,0) 이므로, 원래 x=200은 왼쪽에 있으므로 200 - 500 = -300으로 왼쪽으로 이동.
.radius(radius): 반지름 설정.
.color(mint): 색상 적용.
- 의미: 현재 프레임에 원을 그릴 명령을 추가 (아직 렌더링되지 않음).
20.
draw.to_frame(app, &frame).unwrap();
- nannou:
draw.to_frame(...): 지금까지 쌓은 그리기 명령을 실제 프레임 버퍼에 렌더링.
&frame: 현재 프레임 (렌더 타겟).
.unwrap(): 결과가 Result이므로 성공 시 값을 가져오고, 실패 시 패닉 (간단한 예제에서는 허용).
- 의미: 화면에 그림을 출력.
📝 Rust Code - 민트 원을 네 개 배치
use nannou::prelude::*;
fn main() {
nannou::app(model).simple_window(view).size(1000, 500).run();
}
struct Model;
fn model(_app: &App) -> Model {
Model
}
fn view(app: &App, _model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(BLACK);
let mint = rgb(0.0, 1.0, 0.8);
let radius = 100.0;
let y = 0.0;
for x in [200.0, 400.0, 600.0, 800.0] {
draw.ellipse()
.x_y(x - 500.0, y)
.radius(radius)
.color(mint);
}
draw.to_frame(app, &frame).unwrap();
}