

use nannou::prelude::*;
fn main() {
nannou::app(model).update(update).run();
}
struct Model {
colors: Vec<Rgba>,
rotation: f32,
}
fn model(app: &App) -> Model {
app.new_window().view(view).build().unwrap();
Model {
colors: vec![
rgba(1.0, 0.0, 0.0, 0.5), // 빨강
rgba(0.0, 1.0, 0.0, 0.5), // 초록
rgba(0.0, 0.0, 1.0, 0.5) // 파랑
],
rotation: 0.0,
}
}
fn update(_app: &App, model: &mut Model, _update: Update) {
model.rotation += 0.01;
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(rgba(0.0, 0.0, 0.0, 1.0));
// 여러 도형 그리기
for (i, &color) in model.colors.iter().enumerate() {
let angle = model.rotation + ((i as f32) * TAU) / 3.0;
let x = angle.cos() * 100.0;
let y = angle.sin() * 100.0;
draw.rect().xy(pt2(x, y)).wh(vec2(80.0, 80.0)).color(color).rotate(model.rotation);
}
draw.to_frame(app, &frame).unwrap();
}
// Nannou 라이브러리의 주요 기능을 사용하기 위해 prelude 모듈을 가져옵니다.
// 이 모듈은 자주 사용되는 타입과 함수들을 자동으로 import 해줍니다.
use nannou::prelude::*;
// 프로그램의 진입점입니다.
// Nannou 애플리케이션을 생성하고 실행합니다.
fn main() {
// nannou::app() 함수는 애플리케이션 빌더를 반환합니다.
// - model: 애플리케이션 시작 시 초기 상태를 설정하는 함수
// - update: 매 프레임마다 상태를 업데이트하는 함수
// - run(): 실제로 창을 열고 렌더링 루프를 시작합니다.
nannou::app(model).update(update).run();
}
// 애플리케이션의 상태를 저장하는 구조체입니다.
// 이 구조체는 프로그램 전체에서 유지되는 데이터를 담당합니다.
struct Model {
// 세 가지 반투명 색상을 저장하는 벡터입니다.
// Rgba는 Nannou에서 제공하는 색상 타입으로, (빨강, 초록, 파랑, 투명도)로 구성됩니다.
colors: Vec<Rgba>,
// 도형의 회전 각도를 저장하는 변수입니다.
// f32 타입으로, 매 프레임마다 조금씩 증가하면서 회전 애니메이션을 구현합니다.
rotation: f32,
}
// 애플리케이션이 시작될 때 한 번만 호출되는 함수입니다.
// 초기 상태(Model)를 반환합니다.
fn model(app: &App) -> Model {
// 새로운 창을 생성하고, view 함수를 렌더링 콜백으로 설정합니다.
// build()는 창 생성을 완료하고, unwrap()은 오류가 발생하면 프로그램을 중단시킵니다.
app.new_window().view(view).build().unwrap();
// 초기 Model 인스턴스를 반환합니다.
Model {
// 세 가지 색상을 반투명(알파 = 0.5)으로 설정합니다.
// 각 색상은 Rgba 타입이며, 값은 0.0 ~ 1.0 사이입니다.
colors: vec![
rgba(1.0, 0.0, 0.0, 0.5), // 빨간색 (Red)
rgba(0.0, 1.0, 0.0, 0.5), // 초록색 (Green)
rgba(0.0, 0.0, 1.0, 0.5) // 파란색 (Blue)
],
// 초기 회전 각도는 0.0 라디안입니다.
rotation: 0.0,
}
}
// 매 프레임마다 호출되는 함수로, 애플리케이션의 상태를 업데이트합니다.
// - _app: 애플리케이션 객체 (여기서는 사용되지 않아서 _로 시작)
// - model: 가변 참조로, 상태를 수정할 수 있음
// - _update: 업데이트 이벤트 정보 (여기서는 사용되지 않음)
fn update(_app: &App, model: &mut Model, _update: Update) {
// 매 프레임마다 회전 각도를 0.01 라디안씩 증가시킵니다.
// 이 값이 커질수록 회전 속도가 빨라집니다.
model.rotation += 0.01;
}
// 매 프레임마다 화면을 그리는 함수입니다.
// - app: 애플리케이션 객체
// - model: 현재 상태 (읽기 전용)
// - frame: 현재 프레임 버퍼 (렌더링 결과를 여기에 그립니다)
fn view(app: &App, model: &Model, frame: Frame) {
// 그리기 명령을 담을 Draw 객체를 생성합니다.
let draw = app.draw();
// 배경을 검은색(완전 불투명)으로 설정합니다.
draw.background().color(rgba(0.0, 0.0, 0.0, 1.0));
// colors 벡터에 저장된 각 색상에 대해 도형을 그립니다.
// enumerate()로 인덱스(i)와 색상(color)을 동시에 얻습니다.
for (i, &color) in model.colors.iter().enumerate() {
// 각 도형의 중심 위치를 원형으로 배치하기 위한 각도 계산
// - model.rotation: 전체 회전 애니메이션 적용
// - (i as f32) * TAU / 3.0: 3개의 도형을 원 위에 균등하게 배치
// (TAU = 2π, 즉 360도)
let angle = model.rotation + ((i as f32) * TAU) / 3.0;
// 삼각함수를 사용해 원형 경로 상의 (x, y) 좌표 계산
// 반지름은 100.0 픽셀
let x = angle.cos() * 100.0;
let y = angle.sin() * 100.0;
// 사각형(rectangle)을 그립니다:
// - xy(pt2(x, y)): 사각형 중심 좌표 설정 (pt2는 2D 점 생성)
// - wh(vec2(80.0, 80.0)): 너비(width)와 높이(height)를 80x80으로 설정
// - color(color): 현재 반복에서의 색상 적용
// - rotate(model.rotation): 사각형 자체도 전체 회전 각도만큼 회전
draw.rect()
.xy(pt2(x, y))
.wh(vec2(80.0, 80.0))
.color(color)
.rotate(model.rotation);
}
// 그리기 명령을 실제 프레임 버퍼(frame)에 적용합니다.
// unwrap()은 렌더링 오류 시 프로그램을 중단시킵니다.
draw.to_frame(app, &frame).unwrap();
}