From 4e695573d64a942d061b1381c329346a57a8e159 Mon Sep 17 00:00:00 2001 From: annieversary Date: Sun, 22 Aug 2021 16:40:06 +0200 Subject: [PATCH] add subtitled #7 --- Cargo.lock | 8 ++ crates/subtitled7/Cargo.toml | 8 ++ crates/subtitled7/src/main.rs | 145 ++++++++++++++++++++++++++++++++++ crates/utils/src/lib.rs | 12 +++ 4 files changed, 173 insertions(+) create mode 100644 crates/subtitled7/Cargo.toml create mode 100644 crates/subtitled7/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 751b288..9b67520 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2388,6 +2388,14 @@ dependencies = [ "utils", ] +[[package]] +name = "subtitled7" +version = "0.1.0" +dependencies = [ + "nannou", + "utils", +] + [[package]] name = "syn" version = "1.0.74" diff --git a/crates/subtitled7/Cargo.toml b/crates/subtitled7/Cargo.toml new file mode 100644 index 0000000..f099407 --- /dev/null +++ b/crates/subtitled7/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "subtitled7" +version = "0.1.0" +edition = "2018" + +[dependencies] +nannou = "0.17" +utils = { path = "../utils" } diff --git a/crates/subtitled7/src/main.rs b/crates/subtitled7/src/main.rs new file mode 100644 index 0000000..9ae256f --- /dev/null +++ b/crates/subtitled7/src/main.rs @@ -0,0 +1,145 @@ +use nannou::{color::Mix, prelude::*}; +use std::collections::VecDeque; +use utils::*; + +fn main() { + nannou::app(model).update(update).simple_window(view).run(); +} + +struct Model { + points: VecDeque, +} + +const GOLDEN: f32 = 0.618033988; + +fn point(i: usize) -> Vec2 { + let theta = i as f32 * TAU * GOLDEN; + let r = theta.sqrt(); + let x = r * theta.cos(); + let y = r * theta.sin(); + vec2(x, y) +} + +fn model(_app: &App) -> Model { + Model { + points: (0..2000).map(point).map(|a| a * 10.0).collect(), + } +} + +fn circ(p: Vec2, center: Vec2, r: f32) -> f32 { + (p - center).length() - r +} + +fn spinny_circles(p: Vec2, t: f32) -> f32 { + let mut c = vec![(Vec2::ZERO, 1.0)]; + + let mut ext = (0..7) + .map(|i| { + let v: Vec2 = (t + TAU * i as f32 / 7.0).sin_cos().into(); + (v * 100.0, 10.0) + }) + .collect::>(); + c.append(&mut ext); + let mut ext = (0..7) + .map(|i| { + let v: Vec2 = (-t + TAU * i as f32 / 7.0).sin_cos().into(); + (v * 250.0, 10.0) + }) + .collect::>(); + c.append(&mut ext); + + c.iter() + .fold(f32::MAX, |acc, &(c, r)| acc.min(circ(p, c, r))) +} + +fn segment(p: Vec2, a: Vec2, b: Vec2) -> f32 { + let pa = p - a; + let ba = b - a; + let h = (pa.dot(ba) / ba.length_squared()).clamp(0.0, 1.0); + return (pa - ba * h).length(); +} +fn pentagram(p: Vec2, center: Vec2, t: f32) -> f32 { + let points = (0..5) + .map(|i| { + let v: Vec2 = (-t + TAU * i as f32 / 5.0).sin_cos().into(); + v * 100.0 - center + }) + .collect::>(); + + (0..5) + .map(|i| (points[i], points[(i + 2) % 5])) + .fold(f32::MAX, |acc, (a, b)| acc.min(segment(p, a, b))) +} +fn pentagrams(p: Vec2, t: f32) -> f32 { + (0..5) + .map(|i| { + let v: Vec2 = (t / 10.0 + TAU * i as f32 / 5.0).sin_cos().into(); + let v = v * 330.0; + pentagram(p, v, t) + }) + .fold(f32::MAX, |acc, a| acc.min(a)) +} + +fn f(p: Vec2, t: f32) -> f32 { + spinny_circles(p, t).min(pentagrams(p, t)) +} + +fn norm_f(p: Vec2, t: f32) -> Vec2 { + const H: f32 = 0.0001; + let k = vec2(1.0, -1.0); + return (k * f(p + k * H, t) + + k.yy() * f(p + k.yy() * H, t) + + k.yx() * f(p + k.yx() * H, t) + + k.xx() * f(p + k.xx() * H, t)) + .normalize(); +} + +fn update(app: &App, model: &mut Model, _update: Update) { + let t = app.elapsed_frames() as f32 / 120.0; + for p in &mut model.points { + *p -= norm_f(*p, t) * f(*p, t).signum(); + } + + if app.elapsed_frames() > 20 { + for _ in 0..10 { + model.points.push_back(6.0 * point(random_range(0, 1000))); + } + } + + // remove a bunch of them every once in a while + // since we're already not running real time, who cares if we pause for a bit rigth? + if app.elapsed_frames() % 200 == 0 { + // drain_filter but i'm too lazy for nightly + let mut i = 0; + while i < model.points.len() { + if f(model.points[i], t) < 0.0 { + model.points.remove(i); + } else { + i += 1; + } + } + } +} + +fn view(app: &App, model: &Model, frame: Frame) { + let t = frame.nth() as f32 / 120.0; + + let draw = app.draw(); + if frame.nth() == 0 { + draw.background() + .color(srgb(1., 250.0 / 255.0, 250.0 / 255.0)); + } else { + let win = app.window_rect(); + draw.rect() + .wh(win.wh()) + .color(srgba(1., 250.0 / 255.0, 250.0 / 255.0, 0.005)); + } + + for &p in &model.points { + draw.ellipse().color(BLACK).xy(p).radius(1.0); + } + + draw.to_frame(app, &frame).unwrap(); + + utils::record::record(app, &frame); +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index 642dc4a..f8f8b37 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -14,9 +14,21 @@ pub fn map_cos(v: f32, out_min: f32, out_max: f32) -> f32 { pub trait Vec2Extension { fn atan2(self) -> f32; + fn yy(self) -> Self; + fn yx(self) -> Self; + fn xx(self) -> Self; } impl Vec2Extension for Vec2 { fn atan2(self) -> f32 { self.x.atan2(self.y) } + fn yy(self) -> Self { + vec2(self.y, self.y) + } + fn yx(self) -> Self { + vec2(self.y, self.x) + } + fn xx(self) -> Self { + vec2(self.x, self.x) + } }