
๐ Rust Code
use nannou::prelude::*;
use std::path::Path;
struct Circle {
radius: f32,
angle: f32,
size: f32,
hue: f32,
speed_factor: f32,
}
impl Circle {
fn new(radius: f32, initial_angle: f32, hue: f32) -> Self {
let speed_factor = (300.0 - radius) / 300.0;
let size = map_range(radius, 0.0, 300.0, 15.0, 3.0);
Circle {
radius,
angle: initial_angle,
size,
hue,
speed_factor,
}
}
fn update(&mut self, base_speed: f32) {
self.angle += base_speed * self.speed_factor;
}
fn position(&self) -> Vec2 {
vec2(
self.radius * self.angle.cos(),
self.radius * self.angle.sin(),
)
}
}
struct Model {
circles: Vec<Circle>,
counter: u32,
base_rotation_speed: f32,
trail_alpha: f32,
}
fn main() {
nannou::app(model)
.update(update)
.event(event)
.run();
}
fn model(app: &App) -> Model {
app.new_window().size(800, 800).view(view).build().unwrap();
let mut circles = Vec::new();
for i in 0..100 {
let radius = random_range(20.0, 300.0);
let initial_angle = random_range(0.0, TAU);
let hue = random_range(0.0, 1.0);
circles.push(Circle::new(radius, initial_angle, hue));
}
Model {
circles,
counter: 1,
base_rotation_speed: 0.015,
trail_alpha: 0.05,
}
}
fn update(_app: &App, model: &mut Model, _update: Update) {
for circle in &mut model.circles {
circle.update(model.base_rotation_speed);
}
}
fn event(app: &App, model: &mut Model, event: Event) {
if let Event::WindowEvent { simple: Some(WindowEvent::KeyPressed(key)), .. } = event {
match key {
Key::P => {
save_frame(app, model, "png");
}
Key::J => {
save_frame(app, model, "jpg");
}
Key::C => {
reset_circles(model);
}
Key::Plus | Key::Equals => {
model.base_rotation_speed += 0.005;
println!("์๋ ์ฆ๊ฐ: {:.3}", model.base_rotation_speed);
}
Key::Minus => {
model.base_rotation_speed = (model.base_rotation_speed - 0.005).max(0.001);
println!("์๋ ๊ฐ์: {:.3}", model.base_rotation_speed);
}
Key::T => {
model.trail_alpha = if model.trail_alpha < 0.1 { 0.3 } else { 0.05 };
println!("๊ถค์ ํฌ๋ช
๋: {:.2}", model.trail_alpha);
}
_ => {}
}
}
}
fn reset_circles(model: &mut Model) {
model.circles.clear();
for _ in 0..100 {
let radius = random_range(20.0, 300.0);
let initial_angle = random_range(0.0, TAU);
let hue = random_range(0.0, 1.0);
model.circles.push(Circle::new(radius, initial_angle, hue));
}
println!("์๋ก์ด ํจํด ์์ฑ๋จ");
}
fn save_frame(app: &App, model: &mut Model, format: &str) {
let filename = format!("output_{}.{}", model.counter, format);
let path = Path::new(&filename);
let window = app.main_window();
window.capture_frame(path);
println!("ํ๋ ์ ์ ์ฅ: {}", filename);
model.counter += 1;
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.rect()
.w_h(800.0, 800.0)
.color(srgba(0.0, 0.0, 0.0, model.trail_alpha));
for circle in &model.circles {
let pos = circle.position();
let saturation = map_range(circle.radius, 20.0, 300.0, 1.0, 0.6);
let lightness = map_range(circle.speed_factor, 0.0, 1.0, 0.3, 0.8);
draw.ellipse()
.xy(pos)
.radius(circle.size)
.color(hsl(circle.hue, saturation, lightness))
.stroke_weight(0.5)
.stroke_color(hsla(circle.hue, saturation, lightness + 0.2, 0.8));
}
draw.ellipse()
.xy(vec2(0.0, 0.0))
.radius(2.0)
.color(WHITE);
draw.text("Radial Dreams")
.xy(vec2(0.0, 350.0))
.font_size(24)
.color(hsla(0.15, 0.8, 0.9, 0.8))
.center_justify();
let info_text = format!(
"Speed: {:.3} | Trail: {:.2}\nP: Save PNG, J: Save JPG, C: New Pattern\n+/-: Speed Control, T: Trail Toggle",
model.base_rotation_speed,
model.trail_alpha
);
draw.text(&info_text)
.xy(vec2(-380.0, 320.0))
.font_size(12)
.color(WHITE)
.left_justify();
draw.to_frame(app, &frame).unwrap();
}