

๐งฉ Pseudo-code ๊ตฌ์กฐ ์ค๊ณ
- NUM_RINGS: ๋์ฌ์์ ๊ฐ์.
- ELLIPSE_RATIO: ์ด์ฌ๋ฅ ์ ํตํด ์ํ โ ํ์ํ ๋ณํ ์ ์ด.
- DEPTH_SCALE: ์์ด ์์ชฝ์ผ๋ก ๊ฐ์๋ก ์ ์ ์์์ง๋ ๋น์จ.
- COLOR_VARIATION: ๊ฐ ์์ hue๋ฅผ ๋๋คํ๊ฒ ์กฐ์ .
- LINE_WEIGHT: ๋๊ป ์กฐ์ ์ผ๋ก ๊น์ด๊ฐ ๋ณด๊ฐ.
- Ring: ๊ฐ ์์ ๋ฐ์ง๋ฆ, ์์, ์ด์ฌ๋ฅ ์ ๋ณด๋ฅผ ๊ฐ์ง.
- Model: ์ ์ฒด ring ์งํฉ์ ๊ด๋ฆฌํ๊ณ view์์ ๋ฐ๋ณต ๋ ๋๋ง.
- OUTER_WEIGHT โ INNER_WEIGHT
: ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ ์์ ๋๊ป๊ณ , ์์ชฝ์ผ๋ก ๊ฐ์๋ก ์ ์ด ์์์ง.
๐ Rust Code
use nannou::prelude::*;
const NUM_RINGS: usize = 100;
const ELLIPSE_RATIO: f32 = 1.0;
const COLOR_VARIATION: f32 = 0.2;
const DEPTH_SCALE: f32 = 0.98;
const BASE_RADIUS: f32 = 800.0;
const HUE_BASE: f32 = 0.5;
const FOLLOW_SPEED: f32 = 0.45;
const OUTER_WEIGHT: f32 = 15.0;
const INNER_WEIGHT: f32 = 1.0;
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
struct Model {
rings: Vec<Ring>,
}
struct Ring {
radius: f32,
color: Hsla,
eccentricity: f32,
position: Vec2,
weight: f32,
}
impl Ring {
fn new(radius: f32, color: Hsla, eccentricity: f32, position: Vec2, weight: f32) -> Self {
Self {
radius,
color,
eccentricity,
position,
weight,
}
}
fn follow(&mut self, target: Vec2, lerp_amount: f32) {
self.position = self.position + (target - self.position) * lerp_amount;
}
}
impl Model {
fn new(app: &App) -> Self {
app.new_window()
.size(1024, 1024)
.title("Concentric Color Ellipses Follow Mouse")
.build()
.unwrap();
let mut rings = Vec::new();
let denom = if NUM_RINGS > 1 { (NUM_RINGS - 1) as f32 } else { 1.0 };
for i in 0..NUM_RINGS {
let t = (i as f32) / denom;
let weight = lerp(OUTER_WEIGHT, INNER_WEIGHT, t);
let radius = BASE_RADIUS * DEPTH_SCALE.powf(i as f32);
let hue = (HUE_BASE + random_f32() * COLOR_VARIATION) % 1.0;
let color = hsla(hue, 0.8, 0.5, 1.0);
rings.push(Ring::new(radius, color, ELLIPSE_RATIO, vec2(0.0, 0.0), weight));
}
Self { rings }
}
fn update(&mut self, app: &App) {
let mouse = app.mouse.position();
if let Some(last) = self.rings.last_mut() {
last.follow(mouse, FOLLOW_SPEED);
}
if self.rings.len() >= 2 {
for i in (0..self.rings.len() - 1).rev() {
let target_pos = self.rings[i + 1].position;
let lerp_amt = FOLLOW_SPEED * 0.8;
self.rings[i].follow(target_pos, lerp_amt);
}
}
}
fn view(&self, app: &App, frame: Frame) {
let draw = app.draw();
draw.background().color(hsla(0.0, 0.0, 0.02, 1.0));
for ring in &self.rings {
draw.ellipse()
.x_y(ring.position.x, ring.position.y)
.w(ring.radius)
.h(ring.radius * ring.eccentricity)
.no_fill()
.stroke(ring.color)
.stroke_weight(ring.weight);
}
draw.to_frame(app, &frame).unwrap();
}
}
fn main() {
nannou::app(model).update(update).view(view).run();
}
fn model(app: &App) -> Model {
Model::new(app)
}
fn update(app: &App, model: &mut Model, _update: Update) {
model.update(app);
}
fn view(app: &App, model: &Model, frame: Frame) {
model.view(app, frame);
}