From 92214054f9e471245d08331417406fe385bfa65f Mon Sep 17 00:00:00 2001 From: annieversary Date: Wed, 20 Oct 2021 18:52:16 +0100 Subject: [PATCH] add 22, 23, 24, --- Cargo.lock | 24 +++++++++ crates/subtitled22/Cargo.toml | 8 +++ crates/subtitled22/src/main.rs | 99 ++++++++++++++++++++++++++++++++++ crates/subtitled23/Cargo.toml | 8 +++ crates/subtitled23/src/main.rs | 68 +++++++++++++++++++++++ crates/subtitled24/Cargo.toml | 8 +++ crates/subtitled24/src/main.rs | 67 +++++++++++++++++++++++ crates/utils/src/lib.rs | 2 + crates/utils/src/lsystems.rs | 66 +++++++++++++++++++++++ crates/utils/src/turtle.rs | 59 ++++++++++++++++++++ 10 files changed, 409 insertions(+) create mode 100644 crates/subtitled22/Cargo.toml create mode 100644 crates/subtitled22/src/main.rs create mode 100644 crates/subtitled23/Cargo.toml create mode 100644 crates/subtitled23/src/main.rs create mode 100644 crates/subtitled24/Cargo.toml create mode 100644 crates/subtitled24/src/main.rs create mode 100644 crates/utils/src/lsystems.rs create mode 100644 crates/utils/src/turtle.rs diff --git a/Cargo.lock b/Cargo.lock index 0efea05..7cccd46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2470,6 +2470,30 @@ dependencies = [ "utils", ] +[[package]] +name = "subtitled22" +version = "0.1.0" +dependencies = [ + "nannou", + "utils", +] + +[[package]] +name = "subtitled23" +version = "0.1.0" +dependencies = [ + "nannou", + "utils", +] + +[[package]] +name = "subtitled24" +version = "0.1.0" +dependencies = [ + "nannou", + "utils", +] + [[package]] name = "subtitled3" version = "0.1.0" diff --git a/crates/subtitled22/Cargo.toml b/crates/subtitled22/Cargo.toml new file mode 100644 index 0000000..3d50df4 --- /dev/null +++ b/crates/subtitled22/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "subtitled22" +version = "0.1.0" +edition = "2018" + +[dependencies] +nannou = "0.17" +utils = { path = "../utils" } diff --git a/crates/subtitled22/src/main.rs b/crates/subtitled22/src/main.rs new file mode 100644 index 0000000..24fe577 --- /dev/null +++ b/crates/subtitled22/src/main.rs @@ -0,0 +1,99 @@ +use nannou::prelude::*; +use utils::{drawing::draw_soft_bg, *}; + +fn main() { + nannou::app(model).update(update).simple_window(view).run(); +} + +enum Type { + Square, + Circle, +} +struct Shape { + size: f32, + r: f32, + color: f32, + t: Type, +} + +struct Model { + shapes: Vec, +} + +fn model(_app: &App) -> Model { + Model { + shapes: (0..1000) + .map(|_| { + let size = random_range(7.0, 18.0); + let r = random_range(100.0, 300.0); + let color = (random_range(0.0, 80.0) / 360.0).fract(); + let t = if random::() > 0.3 { + Type::Circle + } else { + Type::Square + }; + Shape { size, r, color, t } + }) + .collect(), + } +} + +fn update(app: &App, _model: &mut Model, _update: Update) { + let _t = app.elapsed_frames() as f32 / 60.0; +} + +fn view(app: &App, model: &Model, frame: Frame) { + let t = frame.nth() as f32 / 60.0; + + let draw = app.draw(); + draw_soft_bg(&draw, &app, SNOW, 1.03); + + let count = model.shapes.len() as f32; + for (i, shape) in model.shapes.iter().enumerate() { + let angle = 360.0 * i as f32 / count; + let angle = angle + t * map_sin(angle.powi(4), 0.2, 0.7); + + let r = shape.r * map_sin(t + i as f32, 0.5, 1.3); + + let size = shape.size * map_sin(0.004 * t + 2.0 * i as f32, 0.5, 1.5); + + match shape.t { + Type::Circle => { + let color = (shape.color + 0.01 * t + angle * 0.0001).fract(); + let color = hsl(color, map_sin(i.pow(3) as f32, 0.4, 0.7), 0.5); + + let p = r * angle.sin_cos().to_vec2(); + + draw.ellipse().radius(size).xy(p).color(color); + } + Type::Square => { + let p = r * (-angle).sin_cos().to_vec2(); + let rotation = t + i as f32; + + let points = vec![ + vec2(1.0, 1.0), + vec2(-1.0, 1.0), + vec2(-1.0, -1.0), + vec2(1.0, -1.0), + ] + .into_iter() + .map(|v| { + vec2( + v.x * rotation.cos() - v.y * rotation.sin(), + v.x * rotation.sin() + v.y * rotation.cos(), + ) + }) + .map(|v| v * size + p) + .collect::>(); + + draw.polyline() + .stroke_weight(1.5) + .points_closed(points) + .color(SNOW); + } + }; + } + + draw.to_frame(app, &frame).unwrap(); + utils::record::record(app, &frame); +} diff --git a/crates/subtitled23/Cargo.toml b/crates/subtitled23/Cargo.toml new file mode 100644 index 0000000..b25d80d --- /dev/null +++ b/crates/subtitled23/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "subtitled23" +version = "0.1.0" +edition = "2018" + +[dependencies] +nannou = "0.17" +utils = { path = "../utils" } diff --git a/crates/subtitled23/src/main.rs b/crates/subtitled23/src/main.rs new file mode 100644 index 0000000..ce71225 --- /dev/null +++ b/crates/subtitled23/src/main.rs @@ -0,0 +1,68 @@ +use nannou::prelude::*; +use utils::*; + +fn main() { + nannou::app(model).update(update).simple_window(view).run(); +} + +struct Model { + points: Vec, + base_hue: f32, +} + +fn model(_app: &App) -> Model { + Model { + base_hue: random_range(0., 360.0), + points: vec![Vec2::splat(0.1); 10000], + } +} + +fn advance(point: Vec2, t: f32) -> Vec2 { + // pick a random point + let count = ((t * 0.125).trunc() as usize % 5) + 3; + let points = (0..count) + .map(|i| (TAU * i as f32 / count as f32).sin_cos().to_vec2()) + .collect::>(); + + let random = points[random_range(0, count)]; + + (random + point) * map_cos(t * PI * 0.25, 0.1, 1.0).powf(1.5) +} + +fn update(app: &App, model: &mut Model, _update: Update) { + let t = app.elapsed_frames() as f32 / 60.0; + + // advance all points in list + let mut last = model.points.last().unwrap().clone(); + for v in &mut model.points { + last = advance(last, t); + *v = last; + } + + // move base_hue + model.base_hue += 0.5; +} + +fn view(app: &App, model: &Model, frame: Frame) { + let _t = frame.nth() as f32 / 60.0; + + let draw = app.draw(); + + if frame.nth() == 0 { + draw.background().color(BLACK); + } else { + let win = app.window_rect(); + draw.rect().wh(win.wh()).color(srgba(0., 0.0, 0.0, 0.02)); + } + + for &p in &model.points { + let h = random_range(model.base_hue, model.base_hue + 60.0) / 360.0; + draw.ellipse() + .radius(0.2) + .xy(150.0 * p) + .color(hsla(h.fract(), 1.0, 0.5, 0.1)); + } + + draw.to_frame(app, &frame).unwrap(); + utils::record::record(app, &frame); +} diff --git a/crates/subtitled24/Cargo.toml b/crates/subtitled24/Cargo.toml new file mode 100644 index 0000000..aa5758a --- /dev/null +++ b/crates/subtitled24/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "subtitled24" +version = "0.1.0" +edition = "2018" + +[dependencies] +nannou = "0.17" +utils = { path = "../utils" } diff --git a/crates/subtitled24/src/main.rs b/crates/subtitled24/src/main.rs new file mode 100644 index 0000000..406c8cc --- /dev/null +++ b/crates/subtitled24/src/main.rs @@ -0,0 +1,67 @@ +use std::time::Duration; + +use nannou::prelude::*; +use utils::{ + lsystems::LSystem, + turtle::{Turtle, TurtleAlphabet}, +}; + +fn main() { + nannou::app(model) + .update(update) + .simple_window(view) + .size(600, 520) + .run(); +} + +struct Model {} + +fn model(_app: &App) -> Model { + Model {} +} + +fn update(app: &App, _model: &mut Model, _update: Update) { + let _t = app.elapsed_frames() as f32 / 60.0; +} + +fn view(app: &App, _model: &Model, frame: Frame) { + let t = frame.nth() as f32 / 60.0; + + let draw = app.draw(); + if frame.nth() == 0 { + draw.background().color(SNOW); + } + + if frame.nth() % 60 == 0 { + let iters = t.trunc() as usize % 10; + + use TurtleAlphabet::*; + let mut sys = LSystem::new( + vec![Line, Right, Right, Line, Right, Right, Line], + Box::new(|i| match i { + Line => vec![ + Line, Right, Right, Line, Right, Right, Line, Right, Right, Move, + ], + Move => vec![Move, Move], + a @ _ => vec![a], + }), + ); + let h = app.window_rect().h() / 2.0; + let w = app.window_rect().w() / 2.0; + + let mut turtle = Turtle::new(vec2(-w, -h), 2.85 * w * 0.5.powi(iters as i32), TAU / 3.0); + turtle.rotation = -PI / 4.0; + + let a = sys.nth(iters); + turtle.advance_many(&draw, &a); + // turtle.advance(&draw, TurtleAlphabet::Line); + // turtle.advance(&draw, TurtleAlphabet::Right); + // turtle.advance(&draw, TurtleAlphabet::Line); + } + + // cause otherwise it goes too fast when recording and it can't save the frames in time lmao + std::thread::sleep(Duration::from_millis(5)); + + 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 9e6305f..f365c79 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,8 +1,10 @@ pub mod color; pub mod curves; pub mod drawing; +pub mod lsystems; pub mod record; pub mod sequences; +pub mod turtle; use nannou::prelude::*; diff --git a/crates/utils/src/lsystems.rs b/crates/utils/src/lsystems.rs new file mode 100644 index 0000000..7c1fc12 --- /dev/null +++ b/crates/utils/src/lsystems.rs @@ -0,0 +1,66 @@ +use std::collections::HashMap; + +pub struct LSystem { + axiom: Vec, + rule: Box Vec>, + memo: HashMap>, +} + +impl LSystem { + pub fn new(axiom: Vec, rule: Box Vec>) -> Self { + Self { + axiom, + rule, + memo: Default::default(), + } + } + + pub fn nth(&mut self, i: usize) -> Vec { + if i == 0 { + return self.axiom.clone(); + } + if let Some(a) = self.memo.get(&i) { + return a.clone(); + } + + let last = self.nth(i - 1); + + let mut res = Vec::new(); + for letter in last { + res.extend((self.rule)(letter).into_iter()); + } + + self.memo.insert(i, res.clone()); + + res + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lsystems() { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + enum Test { + A, + B, + } + use Test::*; + + let mut sys = LSystem::new( + vec![A], + Box::new(|i| match i { + A => vec![A, B], + B => vec![A], + }), + ); + + assert_eq!(vec![A], sys.nth(0)); + assert_eq!(vec![A, B], sys.nth(1)); + assert_eq!(vec![A, B, A], sys.nth(2)); + assert_eq!(vec![A, B, A, A, B], sys.nth(3)); + assert_eq!(vec![A, B, A, A, B, A, B, A], sys.nth(4)); + } +} diff --git a/crates/utils/src/turtle.rs b/crates/utils/src/turtle.rs new file mode 100644 index 0000000..1f12ec9 --- /dev/null +++ b/crates/utils/src/turtle.rs @@ -0,0 +1,59 @@ +use nannou::prelude::*; + +#[derive(Clone, Copy, Debug)] +pub enum TurtleAlphabet { + Line, + Move, + Right, + Left, + Push, + Pop, +} + +pub struct Turtle { + position: Vec2, + pub rotation: f32, + len: f32, + theta: f32, + stack: Vec<(Vec2, f32)>, +} + +impl Turtle { + pub fn new(position: Vec2, len: f32, theta: f32) -> Self { + Self { + position, + rotation: 0.0, + len, + theta, + stack: Default::default(), + } + } + + pub fn advance(&mut self, draw: &Draw, l: TurtleAlphabet) { + match l { + TurtleAlphabet::Line | TurtleAlphabet::Move => { + let next = self.position + Vec2::ONE.rotate(self.rotation) * self.len; + draw.line().points(self.position, next); + self.position = next; + } + // TurtleAlphabet::Move => { + // self.position += Vec2::ONE.rotate(self.rotation) * self.len; + // } + TurtleAlphabet::Right => self.rotation = (self.rotation - self.theta).rem_euclid(TAU), + TurtleAlphabet::Left => self.rotation = (self.rotation + self.theta).rem_euclid(TAU), + TurtleAlphabet::Push => self.stack.push((self.position, self.rotation)), + TurtleAlphabet::Pop => { + if let Some((a, b)) = self.stack.pop() { + self.position = a; + self.rotation = b; + } + } + } + } + + pub fn advance_many(&mut self, draw: &Draw, l: &[TurtleAlphabet]) { + for &l in l { + self.advance(draw, l) + } + } +}