

๐ Rust Code
use nannou::prelude::*;
use nannou::noise::*;
struct Model {
angle: f32,
spiral_params: SpiralParams,
noise: Perlin,
time: f32,
}
struct SpiralParams {
turns: f32,
points_count: usize,
spacing: f32,
max_radius: f32,
noise_strength: f32,
noise_speed: f32,
}
impl Default for SpiralParams {
fn default() -> Self {
Self {
turns: 35.0,
points_count: 5000,
spacing: 3.0,
max_radius: 500.0,
noise_strength: 20.0,
noise_speed: 2.0,
}
}
}
fn main() {
nannou::app(model).update(update).run();
}
fn model(app: &App) -> Model {
let _window = app
.new_window()
.title("ํ๋ค๋ฆฌ๋ ๋์ feat Catmull-Rom Spline")
.size(800, 800)
.view(view)
.build()
.unwrap();
Model {
angle: 0.0,
spiral_params: SpiralParams::default(),
noise: Perlin::new(),
time: 0.0,
}
}
fn update(_app: &App, model: &mut Model, _update: Update) {
model.angle -= 0.01;
model.time += 0.01;
}
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;
let common_time = model.time * params.noise_speed;
let points: Vec<Point2> = (0..params.points_count)
.filter_map(|i| {
let t = (i as f32 / params.points_count as f32) * max_t;
let base_r = params.spacing * t;
if base_r > params.max_radius {
return None;
}
let noise_val = model.noise.get([
(i as f32 * 0.05) as f64,
common_time as f64
]) as f32;
let r = base_r + noise_val * params.noise_strength;
Some(pt2(r * t.cos(), r * t.sin()))
})
.collect();
if points.len() > 3 {
for i in 1..points.len() - 2 {
let progress = i as f32 / points.len() 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);
let p0 = points[i - 1];
let p1 = points[i];
let p2 = points[i + 1];
let p3 = points[i + 2];
for j in 0..5 {
let t = j as f32 / 4.0;
let t2 = t * t;
let t3 = t2 * t;
let x = 0.5 * ((2.0 * p1.x) +
(-p0.x + p2.x) * t +
(2.0 * p0.x - 5.0 * p1.x + 4.0 * p2.x - p3.x) * t2 +
(-p0.x + 3.0 * p1.x - 3.0 * p2.x + p3.x) * t3);
let y = 0.5 * ((2.0 * p1.y) +
(-p0.y + p2.y) * t +
(2.0 * p0.y - 5.0 * p1.y + 4.0 * p2.y - p3.y) * t2 +
(-p0.y + 3.0 * p1.y - 3.0 * p2.y + p3.y) * t3);
let current_point = pt2(x, y);
if j > 0 {
let prev_point = {
let prev_t = (j - 1) as f32 / 4.0;
let prev_t2 = prev_t * prev_t;
let prev_t3 = prev_t2 * prev_t;
let prev_x = 0.5 * ((2.0 * p1.x) +
(-p0.x + p2.x) * prev_t +
(2.0 * p0.x - 5.0 * p1.x + 4.0 * p2.x - p3.x) * prev_t2 +
(-p0.x + 3.0 * p1.x - 3.0 * p2.x + p3.x) * prev_t3);
let prev_y = 0.5 * ((2.0 * p1.y) +
(-p0.y + p2.y) * prev_t +
(2.0 * p0.y - 5.0 * p1.y + 4.0 * p2.y - p3.y) * prev_t2 +
(-p0.y + 3.0 * p1.y - 3.0 * p2.y + p3.y) * prev_t3);
pt2(prev_x, prev_y)
};
draw.line()
.start(prev_point)
.end(current_point)
.weight(weight * 0.8)
.color(hsla(hue, saturation, lightness, alpha * 0.8));
}
}
}
}
draw.ellipse()
.radius(3.0)
.color(hsla(0.6, 0.8, 0.9, 0.8));
draw.to_frame(app, &frame).unwrap();
}