

use nannou::prelude::*;
struct Model {
angle: f32,
spiral_params: SpiralParams,
}
struct SpiralParams {
turns: f32,
points_count: usize,
spacing: f32,
max_radius: f32,
}
impl Default for SpiralParams {
fn default() -> Self {
Self {
turns: 22.0,
points_count: 3000,
spacing: 3.0,
max_radius: 500.0,
}
}
}
fn main() {
nannou::app(model).update(update).run();
}
fn model(app: &App) -> Model {
let _window = app.new_window().title("Spiral").size(800, 800).view(view).build().unwrap();
Model {
angle: 0.0,
spiral_params: SpiralParams::default(),
}
}
fn update(_app: &App, model: &mut Model, _update: Update) {
model.angle -= 0.1;
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(BLACK);
let draw = draw.rotate(model.angle);
let params = &model.spiral_params;
let max_t = params.turns * TAU;
for i in 0..params.points_count - 1 {
let t1 = ((i as f32) / ((params.points_count - 1) as f32)) * max_t;
let t2 = (((i + 1) as f32) / ((params.points_count - 1) as f32)) * max_t;
let r1 = params.spacing * t1;
let r2 = params.spacing * t2;
if r1 > params.max_radius {
break;
}
let p1 = pt2(r1 * t1.cos(), r1 * t1.sin());
let p2 = pt2(r2 * t2.cos(), r2 * t2.sin());
let progress = (i as f32) / ((params.points_count - 1) as f32);
let alpha = map_range(progress, 0.0, 1.0, 0.1, 0.9);
let hue = map_range(progress, 0.0, 1.0, 0.8, 0.5);
let saturation = map_range(progress, 0.0, 1.0, 0.3, 0.8);
let lightness = map_range(progress, 0.0, 1.0, 0.3, 0.7);
let weight = map_range(progress, 0.0, 1.0, 0.3, 2.5);
draw.line().start(p1).end(p2).weight(weight).color(hsla(hue, saturation, lightness, alpha));
}
draw.ellipse().radius(3.0).color(hsla(0.6, 0.8, 0.9, 0.8));
draw.to_frame(app, &frame).unwrap();
}
use nannou::prelude::*; // nannou ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ๋ค์ ๊ฐ์ ธ์ต๋๋ค
// ๋ฉ์ธ ๋ชจ๋ธ ๊ตฌ์กฐ์ฒด - ์ ํ๋ฆฌ์ผ์ด์
์ ์ํ๋ฅผ ์ ์ฅํฉ๋๋ค
struct Model {
angle: f32, // ํ์ฌ ํ์ ๊ฐ๋ (๋ผ๋์)
spiral_params: SpiralParams, // ๋์ ์ ๋งค๊ฐ๋ณ์๋ค์ ์ ์ฅํ๋ ๊ตฌ์กฐ์ฒด
}
// ๋์ ๋งค๊ฐ๋ณ์ ๊ตฌ์กฐ์ฒด - ๋์ ์ ๋ชจ์์ ๊ฒฐ์ ํ๋ ๊ฐ๋ค์ ์ ์ฅํฉ๋๋ค
struct SpiralParams {
turns: f32, // ๋์ ์ ํ์ ์ (๋ช ๋ฐํด ๋ ๊ฒ์ธ์ง)
points_count: usize, // ๋์ ์ ๊ตฌ์ฑํ๋ ์ ์ ๊ฐ์
spacing: f32, // ๋์ ์ ๋ค ์ฌ์ด์ ๊ฐ๊ฒฉ (๋์ ์ ํฝ์ฐฝ ์ ๋)
max_radius: f32, // ๋์ ์ ์ต๋ ๋ฐ์ง๋ฆ (ํ๋ฉด์์ ๋ณด์ฌ์ง ์ต๋ ํฌ๊ธฐ)
}
// SpiralParams์ ๊ธฐ๋ณธ๊ฐ ๊ตฌํ
impl Default for SpiralParams {
fn default() -> Self {
Self {
turns: 22.0, // ๊ธฐ๋ณธ์ ์ผ๋ก 22๋ฐํด ํ์ ํ๋ ๋์
points_count: 3000, // 3000๊ฐ์ ์ ์ผ๋ก ๋์ ๊ตฌ์ฑ
spacing: 3.0, // ์ ๊ฐ๊ฒฉ 3.0
max_radius: 500.0, // ์ต๋ ๋ฐ์ง๋ฆ 500.0
}
}
}
// === ๋ฉ์ธ ํจ์ - ์ ํ๋ฆฌ์ผ์ด์
์ ์์์
fn main() {
nannou::app(model) // ๋ชจ๋ธ ์์ฑ ํจ์ ์ง์
.update(update) // ์
๋ฐ์ดํธ ํจ์ ์ง์
.run(); // ์ ํ๋ฆฌ์ผ์ด์
์คํ
}
// === ๋ชจ๋ธ ์ด๊ธฐํ ํจ์ - ์ ํ๋ฆฌ์ผ์ด์
์์ ์ ํธ์ถ๋จ
fn model(app: &App) -> Model {
// ์๋ก์ด ์๋์ฐ ์์ฑ
let _window = app
.new_window()
.title("Spiral") // ์๋์ฐ ์ ๋ชฉ
.size(800, 800) // ์๋์ฐ ํฌ๊ธฐ (800x800 ํฝ์
)
.view(view) // ๋ทฐ ํจ์ ์ง์ (ํ๋ฉด ๊ทธ๋ฆฌ๊ธฐ)
.build() // ์๋์ฐ ๋น๋
.unwrap(); // ์๋ฌ ๋ฐ์ ์ ํจ๋
// ์ด๊ธฐ ๋ชจ๋ธ ๋ฐํ
Model {
angle: 0.0, // ์์ ๊ฐ๋๋ 0๋ผ๋์
spiral_params: SpiralParams::default(), // ๊ธฐ๋ณธ ๋์ ๋งค๊ฐ๋ณ์ ์ฌ์ฉ
}
}
// === ์
๋ฐ์ดํธ ํจ์ - ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋์ด ๋ชจ๋ธ ์ํ ์
๋ฐ์ดํธ
fn update(_app: &App, model: &mut Model, _update: Update) {
model.angle -= 0.1; // ๊ฐ๋๋ฅผ 0.1๋ผ๋์์ฉ ๊ฐ์ (์๊ณ ๋ฐ๋ ๋ฐฉํฅ ํ์ )
}
// === ๋ทฐ ํจ์ - ํ๋ฉด์ ๊ทธ๋ฆฌ๋ ํจ์, ๋งค ํ๋ ์๋ง๋ค ํธ์ถ๋จ
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw(); // ๊ทธ๋ฆผ์ ๊ทธ๋ฆฌ๊ธฐ ์ํ Draw ์ธ์คํด์ค ์์ฑ
// ** ๋ฐฐ๊ฒฝ ๊ทธ๋ฆฌ๊ธฐ
draw.background().color(BLACK); // ๊ฒ์์ ๋ฐฐ๊ฒฝ์ผ๋ก ์ฑ์ฐ๊ธฐ
// ** ํ์ ์ ์ ์ฉํ ์๋ก์ด Draw ์ธ์คํด์ค ์์ฑ
// ๊ธฐ์กด draw ์ธ์คํด์ค์ ํ์ ๋ณํ์ ์ ์ฉํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์๋ก์ด draw ๋ณ์์ ์ ์ฅ
let draw = draw.rotate(model.angle);
// ** ๋์ ๊ทธ๋ฆฌ๊ธฐ
let params = &model.spiral_params; // ๋์ ๋งค๊ฐ๋ณ์ ์ฐธ์กฐ
let max_t = params.turns * TAU; // ์ต๋ ๊ฐ๋ ๊ณ์ฐ (TAU = 2ฯ, ์์ ํ ์)
// ** ๋์ ์ ๊ตฌ์ฑํ๋ ์ ๋ถ๋ค ๊ทธ๋ฆฌ๊ธฐ
for i in 0..(params.points_count - 1) {
// ํ์ฌ ์ ๊ณผ ๋ค์ ์ ์ ๊ฐ๋ ๊ณ์ฐ (0๋ถํฐ max_t๊น์ง ์ ํ ๋ณด๊ฐ)
let t1 = (i as f32 / (params.points_count - 1) as f32) * max_t;
let t2 = ((i + 1) as f32 / (params.points_count - 1) as f32) * max_t;
// ํ์ฌ ์ ๊ณผ ๋ค์ ์ ์ ๋ฐ์ง๋ฆ ๊ณ์ฐ (๊ฐ๊ฒฉ ร ๊ฐ๋)
let r1 = params.spacing * t1;
let r2 = params.spacing * t2;
// ์ต๋ ๋ฐ์ง๋ฆ์ ๋์ด๊ฐ๋ฉด ๋ฃจํ ์ข
๋ฃ (๋ ์ด์ ๊ทธ๋ฆด ํ์ ์์)
if r1 > params.max_radius { break; }
// ๊ทน์ขํ๊ณ์์ ๋ฐ์นด๋ฅดํธ ์ขํ๊ณ๋ก ๋ณํ (x = rยทcosฮธ, y = rยทsinฮธ)
let p1 = pt2(r1 * t1.cos(), r1 * t1.sin()); // ํ์ฌ ์ ์์น
let p2 = pt2(r2 * t2.cos(), r2 * t2.sin()); // ๋ค์ ์ ์์น
// ์งํ๋ฅ ๊ณ์ฐ (0.0์์ 1.0 ์ฌ์ด์ ๊ฐ)
let progress = i as f32 / (params.points_count - 1) as f32;
// ํฌ๋ช
๋ ๊ณ์ฐ: ์งํ๋ฅ ์ ๋ฐ๋ผ 0.1์์ 0.9๋ก ๋ณํ
let alpha = map_range(progress, 0.0, 1.0, 0.1, 0.9);
// ์์ ๊ณ์ฐ: HSL ์๊ณต๊ฐ ์ฌ์ฉ
let hue = map_range(progress, 0.0, 1.0, 0.8, 0.5); // ์์: ๋ณด๋ผ์์์ ์ฒญ๋ก์์ผ๋ก
let saturation = map_range(progress, 0.0, 1.0, 0.3, 0.8); // ์ฑ๋: ๋ฎ์ ์ฑ๋์์ ๋์ ์ฑ๋๋ก
let lightness = map_range(progress, 0.0, 1.0, 0.3, 0.7); // ๋ช
๋: ์ด๋์ด ์์์ ๋ฐ์ ์์ผ๋ก
// ์ ๋๊ป ๊ณ์ฐ: ์งํ๋ฅ ์ ๋ฐ๋ผ 0.3์์ 2.5๋ก ๋ณํ
let weight = map_range(progress, 0.0, 1.0, 0.3, 2.5);
// ์ ๋ถ ๊ทธ๋ฆฌ๊ธฐ
draw.line()
.start(p1) // ์ ๋ถ์ ์์์
.end(p2) // ์ ๋ถ์ ๋์
.weight(weight) // ์ ๋๊ป
.color(hsla(hue, saturation, lightness, alpha)); // ์ ์์ (HSL + Alpha)
}
// ** ์ค์ฌ์ ๊ทธ๋ฆฌ๊ธฐ - ๋์ ์ ์ค์ฌ์ ์์ ์์ ๊ทธ๋ฆผ
draw.ellipse()
.radius(3.0) // ๋ฐ์ง๋ฆ 3.0 ํฝ์
.color(hsla(0.6, 0.8, 0.9, 0.8)); // ์ฐํ ์ฒญ๋ก์, ์ฝ๊ฐ ํฌ๋ช
// ** ๊ทธ๋ฆผ์ ํ๋ ์ ๋ฒํผ์ ์ถ๋ ฅ
draw.to_frame(app, &frame).unwrap();
}