
๐ Rust Code
use nannou::prelude::*;
use nannou::noise::{NoiseFn, Perlin};
const WIN_W: u32 = 2000;
const WIN_H: u32 = 2000;
const NUM_BRANCHES: usize = 20_000;
const SEGMENTS_PER_BRANCH: usize = 80;
const SEGMENT_LENGTH: f32 = 8.0;
const FINAL_RADIUS: f32 = 1_000.0;
const NOISE_SCALE: f64 = 0.15;
const NOISE_AMPLITUDE: f32 = 1.0;
const BRANCH_WIDTH_START: f32 = 2.0;
const BRANCH_WIDTH_END: f32 = 0.0;
fn main() {
nannou::app(model).view(view).run();
}
struct BranchPoint {
position: Vec2,
}
struct Model {
branches: Vec<Vec<BranchPoint>>,
}
fn model(app: &App) -> Model {
app.new_window()
.size(WIN_W, WIN_H)
.view(view)
.build()
.unwrap();
let perlin = Perlin::new();
let mut branches = Vec::new();
for b in 0..NUM_BRANCHES {
let base_angle = (b as f32 / NUM_BRANCHES as f32) * TAU;
let mut branch = Vec::new();
let mut prev_pos = vec2(0.0, 0.0);
for s in 0..SEGMENTS_PER_BRANCH {
let noise_val = perlin.get([
(s as f64) * NOISE_SCALE,
(b as f64) * 0.3,
0.0,
]) as f32;
let angle_offset = noise_val * NOISE_AMPLITUDE;
let new_angle = base_angle + angle_offset;
let new_pos = prev_pos + vec2(
new_angle.cos() * SEGMENT_LENGTH,
new_angle.sin() * SEGMENT_LENGTH,
);
branch.push(BranchPoint { position: new_pos });
prev_pos = new_pos;
}
branches.push(branch);
}
Model { branches }
}
fn view(app: &App, model: &Model, frame: Frame) {
let draw = app.draw();
draw.background().color(hsla(0.0, 0.0, 0.01, 1.0));
let full_length = SEGMENTS_PER_BRANCH as f32 * SEGMENT_LENGTH;
let scale = FINAL_RADIUS / full_length;
for branch in &model.branches {
for s in 1..SEGMENTS_PER_BRANCH {
let p1 = branch[s - 1].position * scale;
let p2 = branch[s].position * scale;
let progress = s as f32 / SEGMENTS_PER_BRANCH as f32;
let width = map_range(progress, 0.0, 1.0, BRANCH_WIDTH_START, BRANCH_WIDTH_END);
draw.line()
.start(p1)
.end(p2)
.weight(width)
.color(rgba(1.0, 1.0, 1.0, 0.2));
}
}
draw.ellipse()
.xy(pt2(0.0, 0.0))
.radius(100.0)
.color(BLACK);
draw.to_frame(app, &frame).unwrap();
}