Compare commits
12 Commits
e7b0735bcf
...
b21d7d56b3
Author | SHA1 | Date |
---|---|---|
annieversary | b21d7d56b3 | |
annieversary | c1fef9993b | |
annieversary | e315d00bb3 | |
annieversary | 906e2a66c5 | |
annieversary | c39dec8f61 | |
annieversary | fd5c7fa77d | |
annieversary | c35df147be | |
annieversary | 99dddfde57 | |
annieversary | 5d0692e8e3 | |
annieversary | 4e695573d6 | |
annieversary | ca5e405a7e | |
annieversary | 6e73e65dfc |
|
@ -2348,6 +2348,22 @@ dependencies = [
|
||||||
"utils",
|
"utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtitled10"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nannou",
|
||||||
|
"utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtitled11"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nannou",
|
||||||
|
"utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtitled2"
|
name = "subtitled2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2388,6 +2404,30 @@ dependencies = [
|
||||||
"utils",
|
"utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtitled7"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nannou",
|
||||||
|
"utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtitled8"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nannou",
|
||||||
|
"utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtitled9"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nannou",
|
||||||
|
"utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.74"
|
version = "1.0.74"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "subtitled10"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nannou = "0.17"
|
||||||
|
utils = { path = "../utils" }
|
|
@ -0,0 +1,171 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://paulbourke.net/fractals/clifford/
|
||||||
|
// http://paulbourke.net/fractals/peterdejong/
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum Attractor {
|
||||||
|
Clifford,
|
||||||
|
DeJong,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Attractor {
|
||||||
|
fn random() -> Self {
|
||||||
|
match random_range(0, 2) {
|
||||||
|
0 => Self::Clifford,
|
||||||
|
_ => Self::DeJong,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct Params {
|
||||||
|
a: f32,
|
||||||
|
b: f32,
|
||||||
|
c: f32,
|
||||||
|
d: f32,
|
||||||
|
attractor: Attractor,
|
||||||
|
}
|
||||||
|
struct Model {
|
||||||
|
points: Vec<Vec2>,
|
||||||
|
base_hue: f32,
|
||||||
|
|
||||||
|
/// params for the attractor
|
||||||
|
params: Params,
|
||||||
|
|
||||||
|
// modulation values for each param
|
||||||
|
a_mul: f32,
|
||||||
|
a_add: f32,
|
||||||
|
b_mul: f32,
|
||||||
|
b_add: f32,
|
||||||
|
c_mul: f32,
|
||||||
|
c_add: f32,
|
||||||
|
d_mul: f32,
|
||||||
|
d_add: f32,
|
||||||
|
|
||||||
|
// max range for each param
|
||||||
|
// this would be a const, but acos isn't a const fn
|
||||||
|
min_a: f32,
|
||||||
|
max_a: f32,
|
||||||
|
min_b: f32,
|
||||||
|
max_b: f32,
|
||||||
|
min_c: f32,
|
||||||
|
max_c: f32,
|
||||||
|
min_d: f32,
|
||||||
|
max_d: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(_app: &App) -> Model {
|
||||||
|
Model {
|
||||||
|
params: Params {
|
||||||
|
a: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
c: 0.0,
|
||||||
|
d: 0.0,
|
||||||
|
attractor: Attractor::random(),
|
||||||
|
},
|
||||||
|
|
||||||
|
a_mul: random_range(0.3, 1.0) * random_range(-1.0, 1.0).signum(),
|
||||||
|
b_mul: random_range(0.3, 1.0) * random_range(-1.0, 1.0).signum(),
|
||||||
|
c_mul: random_range(0.3, 1.0) * random_range(-1.0, 1.0).signum(),
|
||||||
|
d_mul: random_range(0.3, 1.0) * random_range(-1.0, 1.0).signum(),
|
||||||
|
|
||||||
|
a_add: random_range(0.3, 2.0),
|
||||||
|
b_add: random_range(0.3, 2.0),
|
||||||
|
c_add: random_range(0.3, 2.0),
|
||||||
|
d_add: random_range(0.3, 2.0),
|
||||||
|
|
||||||
|
// magic numbers from here:
|
||||||
|
// http://paulbourke.net/fractals/clifford/paul_richards/main.cpp
|
||||||
|
min_a: f32::acos(1.6 / 2.0),
|
||||||
|
max_a: f32::acos(1.3 / 2.0),
|
||||||
|
min_b: f32::acos(-0.6 / 2.0),
|
||||||
|
max_b: f32::acos(1.7 / 2.0),
|
||||||
|
min_c: f32::acos(-1.2 / 2.0),
|
||||||
|
max_c: f32::acos(0.5 / 2.0),
|
||||||
|
min_d: f32::acos(1.6 / 2.0),
|
||||||
|
max_d: f32::acos(1.4 / 2.0),
|
||||||
|
|
||||||
|
base_hue: random_range(0., 360.0),
|
||||||
|
points: vec![Vec2::splat(0.1); 10000],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(
|
||||||
|
Params {
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d,
|
||||||
|
attractor,
|
||||||
|
}: Params,
|
||||||
|
point: Vec2,
|
||||||
|
) -> Vec2 {
|
||||||
|
let x = point.x;
|
||||||
|
let y = point.y;
|
||||||
|
|
||||||
|
// clifford attractor
|
||||||
|
// xn+1 = sin(a yn) + c cos(a xn)
|
||||||
|
// yn+1 = sin(b xn) + d cos(b yn)
|
||||||
|
|
||||||
|
// de Jong
|
||||||
|
// xn+1 = sin(a yn) - cos(b xn)
|
||||||
|
// yn+1 = sin(c xn) - cos(d yn)
|
||||||
|
|
||||||
|
match attractor {
|
||||||
|
Attractor::Clifford => vec2(
|
||||||
|
(a * y).sin() + (a * x).cos() * c,
|
||||||
|
(b * x).sin() + (b * y).cos() * d,
|
||||||
|
),
|
||||||
|
Attractor::DeJong => vec2((a * y).sin() - (b * x).cos(), (c * x).sin() - (d * y).cos()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(app: &App, model: &mut Model, _update: Update) {
|
||||||
|
let t = app.elapsed_frames() as f32 / 60.0;
|
||||||
|
|
||||||
|
// modulate params
|
||||||
|
model.params.a = map_sin(t * model.a_mul + model.a_add, model.min_a, model.max_a);
|
||||||
|
model.params.b = map_sin(t * model.b_mul + model.b_add, model.min_b, model.max_b);
|
||||||
|
model.params.c = map_sin(t * model.c_mul + model.c_add, model.min_c, model.max_c);
|
||||||
|
model.params.d = map_sin(t * model.d_mul + model.d_add, model.min_d, model.max_d);
|
||||||
|
|
||||||
|
// advance all points in list
|
||||||
|
let mut last = model.points.last().unwrap().clone();
|
||||||
|
for v in &mut model.points {
|
||||||
|
last = advance(model.params, last);
|
||||||
|
*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);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "subtitled11"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nannou = "0.17"
|
||||||
|
utils = { path = "../utils" }
|
|
@ -0,0 +1,159 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
automata: Automata,
|
||||||
|
row: usize,
|
||||||
|
cube: Vec<Vec4>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(_app: &App) -> Model {
|
||||||
|
// generate all the points in the cube
|
||||||
|
let mut cube = vec![];
|
||||||
|
let p = [-1.0, 1.0];
|
||||||
|
for &x in &p {
|
||||||
|
for &y in &p {
|
||||||
|
for &z in &p {
|
||||||
|
for &w in &p {
|
||||||
|
cube.push(vec4(x, y, z, w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Model {
|
||||||
|
automata: Automata::new(),
|
||||||
|
row: 0,
|
||||||
|
cube,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(app: &App, model: &mut Model, _update: Update) {
|
||||||
|
let _t = app.elapsed_frames() as f32 / 60.0;
|
||||||
|
|
||||||
|
if app.elapsed_frames() % 10 == 0 {
|
||||||
|
model.automata.advance();
|
||||||
|
model.row += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rot(angle: f32) -> Mat4 {
|
||||||
|
// 4d rotations https://math.stackexchange.com/questions/1402362/rotation-in-4d
|
||||||
|
let m = mat4(
|
||||||
|
vec4(angle.cos(), angle.sin(), 0.0, 0.0),
|
||||||
|
vec4(-angle.sin(), angle.cos(), 0.0, 0.0),
|
||||||
|
vec4(0.0, 0.0, 1.0, 0.0),
|
||||||
|
vec4(0.0, 0.0, 0.0, 1.0),
|
||||||
|
) * mat4(
|
||||||
|
vec4(1.0, 0.0, 0.0, 0.0),
|
||||||
|
vec4(0.0, angle.cos(), angle.sin(), 0.0),
|
||||||
|
vec4(0.0, -angle.sin(), angle.cos(), 0.0),
|
||||||
|
vec4(0.0, 0.0, 0.0, 1.0),
|
||||||
|
);
|
||||||
|
let angle = -angle * 2.0;
|
||||||
|
m * mat4(
|
||||||
|
vec4(1.0, 0.0, 0.0, 0.0),
|
||||||
|
vec4(0.0, 1.0, 0.0, 0.0),
|
||||||
|
vec4(0.0, 0.0, angle.cos(), angle.sin()),
|
||||||
|
vec4(0.0, 0.0, -angle.sin(), angle.cos()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(app: &App, model: &Model, frame: Frame) {
|
||||||
|
let t = frame.nth() as f32 / 60.0;
|
||||||
|
|
||||||
|
// background
|
||||||
|
|
||||||
|
let draw = app.draw();
|
||||||
|
if frame.nth() == 0 {
|
||||||
|
draw.background().color(SNOW);
|
||||||
|
} else {
|
||||||
|
let win = app.window_rect();
|
||||||
|
draw.rect()
|
||||||
|
.wh(win.wh())
|
||||||
|
.color(srgba(1.0, 250.0 / 255.0, 250.0 / 255.0, 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw automata
|
||||||
|
|
||||||
|
let (l, top, w, h) = app.window_rect().l_t_w_h();
|
||||||
|
|
||||||
|
const BALL_SIZE: f32 = 6.0;
|
||||||
|
let cols = w / BALL_SIZE;
|
||||||
|
let rows = h / BALL_SIZE;
|
||||||
|
|
||||||
|
for (i, &b) in model.automata.points.iter().enumerate() {
|
||||||
|
if b && i as f32 <= cols {
|
||||||
|
let x = l + (i as f32 % cols) * BALL_SIZE;
|
||||||
|
let y = top - (model.row as f32 % rows) * BALL_SIZE;
|
||||||
|
let p = vec2(x, y);
|
||||||
|
let h = model.row as f32 / 360.0;
|
||||||
|
draw.ellipse()
|
||||||
|
.radius(BALL_SIZE / 2.0)
|
||||||
|
.xy(p)
|
||||||
|
.color(hsl(h.fract(), 0.5, 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw center square
|
||||||
|
draw.rect().w_h(400.0, 400.0).color(SNOW);
|
||||||
|
|
||||||
|
// draw 4d cube
|
||||||
|
let p: Vec<_> = model
|
||||||
|
.cube
|
||||||
|
.iter()
|
||||||
|
.map(|&v| {
|
||||||
|
// rotate
|
||||||
|
let v = rot(t) * v;
|
||||||
|
// project onto xz plane cause it's the coolest one
|
||||||
|
100.0 * vec2(v.x, v.z)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
// make all pairs of points
|
||||||
|
let p = p
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(i, a)| p[i + 1..].iter().map(move |b| (a, b)));
|
||||||
|
for (&a, &b) in p {
|
||||||
|
draw.line().points(a, b).color(BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.to_frame(app, &frame).unwrap();
|
||||||
|
utils::record::record(app, &frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Automata {
|
||||||
|
points: Vec<bool>,
|
||||||
|
rule: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Automata {
|
||||||
|
fn new() -> Self {
|
||||||
|
let points = (0..200).map(|_| random::<bool>()).collect();
|
||||||
|
let rule = (0..8).map(|_| random::<bool>()).collect();
|
||||||
|
|
||||||
|
Self { points, rule }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self) {
|
||||||
|
for i in 0..self.points.len() {
|
||||||
|
let i1 = (i + 1) % self.points.len();
|
||||||
|
let im1 = if i == 0 { self.points.len() - 1 } else { i - 1 };
|
||||||
|
let v = (self.points[im1], self.points[i], self.points[i1]);
|
||||||
|
|
||||||
|
self.points[i] = match v {
|
||||||
|
(false, false, false) => self.rule[0],
|
||||||
|
(false, false, true) => self.rule[1],
|
||||||
|
(false, true, false) => self.rule[2],
|
||||||
|
(false, true, true) => self.rule[3],
|
||||||
|
(true, false, false) => self.rule[4],
|
||||||
|
(true, false, true) => self.rule[5],
|
||||||
|
(true, true, false) => self.rule[6],
|
||||||
|
(true, true, true) => self.rule[7],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ fn view(app: &App, _model: &Model, frame: Frame) {
|
||||||
|
|
||||||
let draw = app.draw();
|
let draw = app.draw();
|
||||||
|
|
||||||
if frame.nth() == 1 {
|
if frame.nth() == 0 {
|
||||||
draw.background().color(BG);
|
draw.background().color(BG);
|
||||||
} else {
|
} else {
|
||||||
let win = app.window_rect();
|
let win = app.window_rect();
|
||||||
|
|
|
@ -9,17 +9,25 @@ fn main() {
|
||||||
|
|
||||||
struct Model {
|
struct Model {
|
||||||
shape: Shapes,
|
shape: Shapes,
|
||||||
|
count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn model(_app: &App) -> Model {
|
fn model(_app: &App) -> Model {
|
||||||
Model {
|
Model {
|
||||||
shape: Shapes::new(),
|
shape: Shapes::new(0),
|
||||||
|
count: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(app: &App, model: &mut Model, _update: Update) {
|
fn update(app: &App, model: &mut Model, _update: Update) {
|
||||||
if app.elapsed_frames() % 60 == 0 {
|
if app.elapsed_frames() % 60 == 0 && app.elapsed_frames() > 1 {
|
||||||
model.shape = Shapes::new();
|
if model.count >= 20 {
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
model.shape = Shapes::new(random_range(1, 7));
|
||||||
|
|
||||||
|
model.count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +37,7 @@ fn view(app: &App, model: &Model, frame: Frame) {
|
||||||
let draw = app.draw();
|
let draw = app.draw();
|
||||||
if frame.nth() == 0 {
|
if frame.nth() == 0 {
|
||||||
draw.background().color(BLACK);
|
draw.background().color(BLACK);
|
||||||
} else if frame.nth() % 60 > 40 {
|
} else if frame.nth() % 60 > 30 {
|
||||||
let win = app.window_rect();
|
let win = app.window_rect();
|
||||||
draw.rect().wh(win.wh()).color(srgba(0.0, 0.0, 0.0, 0.2));
|
draw.rect().wh(win.wh()).color(srgba(0.0, 0.0, 0.0, 0.2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,41 +3,115 @@ use nannou::prelude::*;
|
||||||
pub struct Shapes(Vec<Shape>);
|
pub struct Shapes(Vec<Shape>);
|
||||||
impl Shapes {
|
impl Shapes {
|
||||||
pub fn draw(&self, draw: Draw) {
|
pub fn draw(&self, draw: Draw) {
|
||||||
|
let w = 1.0;
|
||||||
|
|
||||||
for shape in &self.0 {
|
for shape in &self.0 {
|
||||||
match shape {
|
match shape {
|
||||||
Shape::Triangle { center, radius } => {
|
&Shape::Triangle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
} => {
|
||||||
let points = vec![vec2(0., 1.), vec2(0.866, -0.5), vec2(-0.866, -0.5)]
|
let points = vec![vec2(0., 1.), vec2(0.866, -0.5), vec2(-0.866, -0.5)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| v * *radius + *center)
|
.map(|v| {
|
||||||
|
vec2(
|
||||||
|
v.x * rotation.cos() - v.y * rotation.sin(),
|
||||||
|
v.x * rotation.sin() + v.y * rotation.cos(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|v| v * radius + center)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
draw.polyline().weight(3.).points_closed(points).color(PINK);
|
draw.polyline().weight(w).points_closed(points).color(PINK);
|
||||||
}
|
}
|
||||||
Shape::Circle { center, radius } => {
|
Shape::Circle { center, radius, .. } => {
|
||||||
draw.ellipse()
|
draw.ellipse()
|
||||||
.radius(*radius)
|
.radius(*radius)
|
||||||
.no_fill()
|
.no_fill()
|
||||||
.stroke_weight(3.)
|
.stroke_weight(w)
|
||||||
.xy(*center)
|
.xy(*center)
|
||||||
.stroke_color(PINK);
|
.stroke_color(PINK);
|
||||||
}
|
}
|
||||||
Shape::Line { center, radius } => {}
|
&Shape::Line {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
} => {
|
||||||
|
let points = vec![vec2(-1.0, 0.), vec2(1.0, 0.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 * radius + center)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
draw.line()
|
||||||
|
.stroke_weight(w)
|
||||||
|
.start(points[0])
|
||||||
|
.end(points[1])
|
||||||
|
.color(PINK);
|
||||||
|
}
|
||||||
|
Shape::LongLine { center, radius, .. } => {
|
||||||
|
draw.line()
|
||||||
|
.stroke_weight(w)
|
||||||
|
.start(Vec2::ZERO)
|
||||||
|
.end(*center + center.normalize() * *radius)
|
||||||
|
.color(PINK);
|
||||||
|
}
|
||||||
|
&Shape::Square {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
} => {
|
||||||
|
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 * radius + center)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
draw.polyline()
|
||||||
|
.stroke_weight(w)
|
||||||
|
.points_closed(points)
|
||||||
|
.color(PINK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new(children: usize) -> Self {
|
||||||
let root = Shape::random(Vec2::ZERO, 100.0);
|
let root = Shape::random(Vec2::ZERO, 100.0);
|
||||||
|
|
||||||
let mut vec = vec![];
|
let mut vec = vec![];
|
||||||
|
|
||||||
for _ in 0..3 {
|
for _ in 0..children {
|
||||||
let (c, r) = root.get_random_point_distance();
|
let (c, r) = root.get_random_point_distance();
|
||||||
let s = Shape::random(c, r);
|
let s = Shape::random(c, r);
|
||||||
|
|
||||||
let children = random_range(0, 3);
|
let children = random_range(0, 5.min(children + 2));
|
||||||
for _ in 0..children {
|
for _ in 0..children {
|
||||||
let (c, r) = s.get_random_point_distance();
|
let (c, r) = s.get_random_point_distance();
|
||||||
let s = Shape::random(c, r);
|
let s = Shape::random(c, r);
|
||||||
|
|
||||||
|
let children = random_range(0, 3.min(children + 1));
|
||||||
|
for _ in 0..children {
|
||||||
|
let (c, r) = s.get_random_point_distance();
|
||||||
|
let s = Shape::random(c, r);
|
||||||
|
vec.push(s);
|
||||||
|
}
|
||||||
|
|
||||||
vec.push(s);
|
vec.push(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,58 +123,144 @@ impl Shapes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! sh {
|
||||||
|
( $($id:ident),* ) => {
|
||||||
pub enum Shape {
|
pub enum Shape {
|
||||||
Triangle { center: Vec2, radius: f32 },
|
$(
|
||||||
Circle { center: Vec2, radius: f32 },
|
$id { center: Vec2, radius: f32, rotation: f32 },
|
||||||
Line { center: Vec2, radius: f32 },
|
)*
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sh!(Triangle, Circle, Line, LongLine, Square);
|
||||||
|
|
||||||
|
fn random_sq_rot() -> f32 {
|
||||||
|
[0.0, PI / 4.0][random_range(0, 2)]
|
||||||
|
}
|
||||||
|
fn random_tri_rot() -> f32 {
|
||||||
|
[0.0, PI / 3.0][random_range(0, 2)]
|
||||||
|
}
|
||||||
|
fn random_line_rot() -> f32 {
|
||||||
|
[0.0, PI / 3.0, PI / 2.0, PI / 4.0, PI][random_range(0, 4)]
|
||||||
|
}
|
||||||
|
|
||||||
impl Shape {
|
impl Shape {
|
||||||
fn random(center: Vec2, radius: f32) -> Self {
|
fn random(center: Vec2, radius: f32) -> Self {
|
||||||
match random_range(0, 3) {
|
match random_range(0, 5) {
|
||||||
0 => Self::tri(center, radius),
|
0 => Self::tri(center, radius, random_tri_rot()),
|
||||||
1 => Self::line(center, radius),
|
1 => Self::line(center, radius, random_line_rot()),
|
||||||
_ => Self::circ(center, radius),
|
2 if center.distance(Vec2::ZERO) > 0.01 => Self::long_line(center, radius, 0.0),
|
||||||
|
3 => Self::square(center, radius, random_sq_rot()),
|
||||||
|
_ => Self::circ(center, radius, 0.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tri(center: Vec2, radius: f32) -> Self {
|
fn tri(center: Vec2, radius: f32, rotation: f32) -> Self {
|
||||||
Self::Triangle { center, radius }
|
Self::Triangle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
}
|
}
|
||||||
fn circ(center: Vec2, radius: f32) -> Self {
|
|
||||||
Self::Circle { center, radius }
|
|
||||||
}
|
}
|
||||||
fn line(center: Vec2, radius: f32) -> Self {
|
fn circ(center: Vec2, radius: f32, rotation: f32) -> Self {
|
||||||
Self::Line { center, radius }
|
Self::Circle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn line(center: Vec2, radius: f32, rotation: f32) -> Self {
|
||||||
|
Self::Line {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn long_line(center: Vec2, radius: f32, rotation: f32) -> Self {
|
||||||
|
Self::LongLine {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn square(center: Vec2, radius: f32, rotation: f32) -> Self {
|
||||||
|
Self::Square {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_random_point_distance(&self) -> (Vec2, f32) {
|
fn get_random_point_distance(&self) -> (Vec2, f32) {
|
||||||
match self {
|
match self {
|
||||||
Shape::Triangle { center, radius } => {
|
&Shape::Triangle {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
} => {
|
||||||
let points = vec![vec2(0., 1.), vec2(0.866, -0.5), vec2(-0.866, -0.5)]
|
let points = vec![vec2(0., 1.), vec2(0.866, -0.5), vec2(-0.866, -0.5)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| v * *radius + *center)
|
.map(|v| {
|
||||||
|
vec2(
|
||||||
|
v.x * rotation.cos() - v.y * rotation.sin(),
|
||||||
|
v.x * rotation.sin() + v.y * rotation.cos(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|v| v * radius + center)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let denom = random_range(1.0, 5.0).floor();
|
let denom = random_range(1.0, 4.0).floor();
|
||||||
(points[random_range(0, points.len())], radius / denom)
|
(points[random_range(0, points.len())], radius / denom)
|
||||||
}
|
}
|
||||||
Shape::Circle { center, radius } => {
|
&Shape::Circle { center, radius, .. } => {
|
||||||
let point = match random_range(0, 4) {
|
let point = match random_range(0, 4) {
|
||||||
0 => *center + Vec2::X * *radius,
|
0 => center + Vec2::X * radius,
|
||||||
1 => *center - Vec2::X * *radius,
|
1 => center - Vec2::X * radius,
|
||||||
2 => *center + Vec2::Y * *radius,
|
2 => center + Vec2::Y * radius,
|
||||||
_ => *center - Vec2::Y * *radius,
|
_ => center - Vec2::Y * radius,
|
||||||
};
|
};
|
||||||
|
|
||||||
let denom = random_range(1.0, 5.0).floor();
|
let denom = random_range(1.0, 4.0).floor();
|
||||||
(point, radius / denom)
|
(point, radius / denom)
|
||||||
}
|
}
|
||||||
Shape::Line { center, radius } => {
|
&Shape::Line { center, radius, .. } => {
|
||||||
let point = todo!("one of the two vertices");
|
let point = match random_range(0, 2) {
|
||||||
|
0 => center - center.normalize() * radius,
|
||||||
|
_ => center + center.normalize() * radius,
|
||||||
|
};
|
||||||
|
|
||||||
let denom = random_range(1.0, 5.0).floor();
|
let denom = random_range(1.0, 4.0).floor();
|
||||||
(point, radius / denom)
|
(point, radius / denom)
|
||||||
}
|
}
|
||||||
|
&Shape::LongLine { center, radius, .. } => {
|
||||||
|
let denom = random_range(1.0, 4.0).floor();
|
||||||
|
(center + center.normalize() * radius, radius / denom)
|
||||||
|
}
|
||||||
|
&Shape::Square {
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
rotation,
|
||||||
|
} => {
|
||||||
|
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 * radius + center)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let denom = random_range(1.0, 4.0).floor();
|
||||||
|
(points[random_range(0, 4)], radius / denom)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "subtitled7"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nannou = "0.17"
|
||||||
|
utils = { path = "../utils" }
|
|
@ -0,0 +1,151 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
points: VecDeque<(Vec2, (f32, f32))>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 color() -> (f32, f32) {
|
||||||
|
(
|
||||||
|
random_range(345.0, 355.0) / 360.0,
|
||||||
|
random_range(75.0, 93.0) / 100.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(_app: &App) -> Model {
|
||||||
|
Model {
|
||||||
|
points: (0..2000).map(point).map(|a| (a * 10.0, color())).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::<Vec<_>>();
|
||||||
|
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::<Vec<_>>();
|
||||||
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
(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)), color()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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].0, 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(BLACK);
|
||||||
|
} else {
|
||||||
|
let win = app.window_rect();
|
||||||
|
draw.rect().wh(win.wh()).color(srgba(0., 0.0, 0.0, 0.005));
|
||||||
|
}
|
||||||
|
|
||||||
|
for &(p, (h, l)) in &model.points {
|
||||||
|
draw.ellipse().color(hsl(h, 1.0, l)).xy(p).radius(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.to_frame(app, &frame).unwrap();
|
||||||
|
|
||||||
|
utils::record::record(app, &frame);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "subtitled8"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nannou = "0.17"
|
||||||
|
utils = { path = "../utils" }
|
|
@ -0,0 +1,133 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
const POINTS: usize = 10;
|
||||||
|
const NUM: usize = 200;
|
||||||
|
|
||||||
|
struct Model {
|
||||||
|
points: Vec<Vec2>,
|
||||||
|
offsets: Vec<Vec2>,
|
||||||
|
|
||||||
|
bg: Vec<(Vec2, f32)>,
|
||||||
|
}
|
||||||
|
impl Model {
|
||||||
|
fn points(&self) -> Vec<Vec2> {
|
||||||
|
self.points
|
||||||
|
.windows(2)
|
||||||
|
.flat_map(|p| {
|
||||||
|
let a = p[0];
|
||||||
|
let b = p[1];
|
||||||
|
(0..NUM).map(move |i| {
|
||||||
|
let i = i as f32 / NUM as f32;
|
||||||
|
(1.0 - i) * a + i * b
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.zip(self.offsets.iter())
|
||||||
|
.map(|(a, &b)| a + b)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn model(_app: &App) -> Model {
|
||||||
|
let points = (0..POINTS)
|
||||||
|
.map(|_| vec2(random_range(-1., 1.), random_range(-1., 1.)) * 200.0)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let offsets = vec![Vec2::ZERO; NUM * POINTS];
|
||||||
|
|
||||||
|
let bg = sequences::Halton::points(2.0, 3.0)
|
||||||
|
.take(10000)
|
||||||
|
.map(|v| (v - Vec2::splat(0.5)) * 1100.0)
|
||||||
|
.filter(|v| v.x.abs() > 220.0 || v.y.abs() > 220.0)
|
||||||
|
.map(|v| (v, 0.5))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Model {
|
||||||
|
points,
|
||||||
|
offsets,
|
||||||
|
bg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(app: &App, model: &mut Model, _update: Update) {
|
||||||
|
let t = app.elapsed_frames() as f32 / 60.0;
|
||||||
|
|
||||||
|
for p in &mut model.offsets {
|
||||||
|
*p *= 0.95;
|
||||||
|
*p += 0.3 * vec2(random_range(-1.0, 1.0), random_range(-1.0, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, p) in model.points.iter_mut().enumerate() {
|
||||||
|
let a = i as f32 * 0.1;
|
||||||
|
p.x = (t * a + i as f32).sin();
|
||||||
|
p.y = (t * 1.3 * a + i as f32).cos();
|
||||||
|
*p *= 200.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, p) in model.bg.iter_mut().enumerate() {
|
||||||
|
p.1 = (t * 2.0 + i as f32).sin() + 1.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);
|
||||||
|
} else {
|
||||||
|
// we only want fading on the inner box,
|
||||||
|
// outside of it we want a hard reset
|
||||||
|
|
||||||
|
let win = app.window_rect();
|
||||||
|
draw.rect()
|
||||||
|
.wh(win.wh())
|
||||||
|
.color(srgba(1.0, 250.0 / 255.0, 250.0 / 255.0, 0.001));
|
||||||
|
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(220.0, -1000.0),
|
||||||
|
vec2(220.0, 1000.0),
|
||||||
|
vec2(1000.0, 1000.0),
|
||||||
|
vec2(1000.0, -1000.0),
|
||||||
|
)
|
||||||
|
.color(SNOW);
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-220.0, -1000.0),
|
||||||
|
vec2(-220.0, 1000.0),
|
||||||
|
vec2(-1000.0, 1000.0),
|
||||||
|
vec2(-1000.0, -1000.0),
|
||||||
|
)
|
||||||
|
.color(SNOW);
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-1000.0, 220.0),
|
||||||
|
vec2(-1000.0, 1000.0),
|
||||||
|
vec2(1000.0, 1000.0),
|
||||||
|
vec2(1000.0, 220.0),
|
||||||
|
)
|
||||||
|
.color(SNOW);
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-1000.0, -220.0),
|
||||||
|
vec2(-1000.0, -1000.0),
|
||||||
|
vec2(1000.0, -1000.0),
|
||||||
|
vec2(1000.0, -220.0),
|
||||||
|
)
|
||||||
|
.color(SNOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.polyline().points(model.points()).color(BLACK);
|
||||||
|
|
||||||
|
for &(p, r) in &model.bg {
|
||||||
|
draw.ellipse().xy(p).radius(r).color(BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.to_frame(app, &frame).unwrap();
|
||||||
|
utils::record::record(app, &frame);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "subtitled9"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nannou = "0.17"
|
||||||
|
utils = { path = "../utils" }
|
|
@ -0,0 +1,82 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Model {}
|
||||||
|
|
||||||
|
fn model(_app: &App) -> Model {
|
||||||
|
Model {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(_app: &App, _model: &mut Model, _update: Update) {}
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
// we only want fading on the inner box,
|
||||||
|
// outside of it we want a hard reset
|
||||||
|
|
||||||
|
let win = app.window_rect();
|
||||||
|
draw.rect()
|
||||||
|
.wh(win.wh())
|
||||||
|
.color(srgba(1.0, 250.0 / 255.0, 250.0 / 255.0, 0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay so this is a bit of a hack
|
||||||
|
// we draw the ones inside and outside the box in two separate passes
|
||||||
|
// on the first one, we draw the ones inside the box, with a box of size 220
|
||||||
|
// then we draw the blank stuff outside of a 200 box
|
||||||
|
// then we draw the points outside the 200 box
|
||||||
|
|
||||||
|
// this is cause otherwise either the colored lines go outside the box,
|
||||||
|
// or the colored lines finish too early
|
||||||
|
// this works and it's still real time so
|
||||||
|
// don't judge me uwu
|
||||||
|
|
||||||
|
// inner draw
|
||||||
|
for i in 0..50 {
|
||||||
|
let r = 1.5;
|
||||||
|
let r2 = r * (50 + i) as f32 / 50.0;
|
||||||
|
let v = vec2((t * r + i as f32).sin(), (t * r2 + (2 * i) as f32).sin()) * 300.0;
|
||||||
|
|
||||||
|
// while inside the box, draw a line to the last point
|
||||||
|
if v.x.abs() < 220.0 && v.y.abs() < 220.0 {
|
||||||
|
let t = t - 1.0 / 60.0;
|
||||||
|
let v_prev = vec2((t * r + i as f32).sin(), (t * r2 + (2 * i) as f32).sin()) * 300.0;
|
||||||
|
|
||||||
|
let h_prev = map_sin((v_prev.x + v_prev.y) / 400.0 + i as f32, 0.0, 1.0);
|
||||||
|
|
||||||
|
draw.line()
|
||||||
|
.stroke_weight(4.0)
|
||||||
|
.points(v, v_prev)
|
||||||
|
.color(hsl(h_prev, 0.5, 0.5));
|
||||||
|
|
||||||
|
let h = map_sin((v.x + v.y) / 400.0 + i as f32, 0.0, 1.0);
|
||||||
|
draw.ellipse().radius(2.0).xy(v).color(hsl(h, 0.5, 0.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw exterior
|
||||||
|
drawing::draw_exterior(&draw, 200.0, SNOW);
|
||||||
|
|
||||||
|
// outer draw
|
||||||
|
for i in 0..50 {
|
||||||
|
let r = 1.5;
|
||||||
|
let r2 = r * (50 + i) as f32 / 50.0;
|
||||||
|
let v = vec2((t * r + i as f32).sin(), (t * r2 + (2 * i) as f32).sin()) * 300.0;
|
||||||
|
|
||||||
|
if v.x.abs() > 201.0 || v.y.abs() > 201.0 {
|
||||||
|
draw.ellipse().radius(2.0).xy(v).color(BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw.to_frame(app, &frame).unwrap();
|
||||||
|
utils::record::record(app, &frame);
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use nannou::prelude::*;
|
use nannou::prelude::*;
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
nannou::app(model).update(update).simple_window(view).run();
|
nannou::app(model).update(update).simple_window(view).run();
|
||||||
|
@ -10,13 +11,18 @@ fn model(_app: &App) -> Model {
|
||||||
Model {}
|
Model {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(_app: &App, _model: &mut Model, _update: Update) {}
|
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) {
|
fn view(app: &App, _model: &Model, frame: Frame) {
|
||||||
let _t = frame.nth() as f32 / 60.0;
|
let _t = frame.nth() as f32 / 60.0;
|
||||||
|
|
||||||
let draw = app.draw();
|
let draw = app.draw();
|
||||||
draw.background().color(PLUM);
|
draw.background().color(PLUM);
|
||||||
|
|
||||||
draw.ellipse().color(STEELBLUE);
|
draw.ellipse().color(STEELBLUE);
|
||||||
|
|
||||||
draw.to_frame(app, &frame).unwrap();
|
draw.to_frame(app, &frame).unwrap();
|
||||||
|
utils::record::record(app, &frame);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
use nannou::{color::IntoLinSrgba, draw::properties::ColorScalar, prelude::*};
|
||||||
|
|
||||||
|
/// Draws the opposite of a box
|
||||||
|
pub fn draw_exterior(draw: &Draw, size: f32, color: impl IntoLinSrgba<ColorScalar> + Clone) {
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(size, -1000.0),
|
||||||
|
vec2(size, 1000.0),
|
||||||
|
vec2(1000.0, 1000.0),
|
||||||
|
vec2(1000.0, -1000.0),
|
||||||
|
)
|
||||||
|
.color(color.clone());
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-size, -1000.0),
|
||||||
|
vec2(-size, 1000.0),
|
||||||
|
vec2(-1000.0, 1000.0),
|
||||||
|
vec2(-1000.0, -1000.0),
|
||||||
|
)
|
||||||
|
.color(color.clone());
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-1000.0, size),
|
||||||
|
vec2(-1000.0, 1000.0),
|
||||||
|
vec2(1000.0, 1000.0),
|
||||||
|
vec2(1000.0, size),
|
||||||
|
)
|
||||||
|
.color(color.clone());
|
||||||
|
draw.quad()
|
||||||
|
.points(
|
||||||
|
vec2(-1000.0, -size),
|
||||||
|
vec2(-1000.0, -1000.0),
|
||||||
|
vec2(1000.0, -1000.0),
|
||||||
|
vec2(1000.0, -size),
|
||||||
|
)
|
||||||
|
.color(color);
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
pub mod drawing;
|
||||||
pub mod record;
|
pub mod record;
|
||||||
|
pub mod sequences;
|
||||||
|
|
||||||
use nannou::prelude::*;
|
use nannou::prelude::*;
|
||||||
|
|
||||||
|
@ -14,9 +16,29 @@ pub fn map_cos(v: f32, out_min: f32, out_max: f32) -> f32 {
|
||||||
|
|
||||||
pub trait Vec2Extension {
|
pub trait Vec2Extension {
|
||||||
fn atan2(self) -> f32;
|
fn atan2(self) -> f32;
|
||||||
|
fn yy(self) -> Self;
|
||||||
|
fn yx(self) -> Self;
|
||||||
|
fn xx(self) -> Self;
|
||||||
}
|
}
|
||||||
impl Vec2Extension for Vec2 {
|
impl Vec2Extension for Vec2 {
|
||||||
fn atan2(self) -> f32 {
|
fn atan2(self) -> f32 {
|
||||||
self.x.atan2(self.y)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait Tup2Extension {
|
||||||
|
fn to_vec2(self) -> Vec2;
|
||||||
|
}
|
||||||
|
impl Tup2Extension for (f32, f32) {
|
||||||
|
fn to_vec2(self) -> Vec2 {
|
||||||
|
self.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub fn record(app: &App, frame: &Frame) {
|
||||||
// Capture all frames to a directory called `/<path_to_nannou>/nannou/simple_capture`.
|
// Capture all frames to a directory called `/<path_to_nannou>/nannou/simple_capture`.
|
||||||
.join("recordings")
|
.join("recordings")
|
||||||
.join(app.exe_name().unwrap())
|
.join(app.exe_name().unwrap())
|
||||||
|
.join("frames")
|
||||||
// Name each file after the number of the frame.
|
// Name each file after the number of the frame.
|
||||||
.join(format!("{:03}", frame.nth()))
|
.join(format!("{:03}", frame.nth()))
|
||||||
// The extension will be PNG. We also support tiff, bmp, gif, jpeg, webp and some others.
|
// The extension will be PNG. We also support tiff, bmp, gif, jpeg, webp and some others.
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
use nannou::prelude::*;
|
||||||
|
|
||||||
|
pub struct Halton {
|
||||||
|
i: usize,
|
||||||
|
base: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Halton {
|
||||||
|
pub fn new(base: f32) -> Self {
|
||||||
|
Self { i: 0, base }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn points(base1: f32, base2: f32) -> impl Iterator<Item = Vec2> {
|
||||||
|
Self::new(base1)
|
||||||
|
.zip(Self::new(base2))
|
||||||
|
.map(crate::Tup2Extension::to_vec2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for Halton {
|
||||||
|
type Item = f32;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut f = 1.0;
|
||||||
|
let mut r = 0.0;
|
||||||
|
let mut index = self.i as f32;
|
||||||
|
|
||||||
|
while index > 0.0 {
|
||||||
|
f /= self.base;
|
||||||
|
r += f * (index % self.base);
|
||||||
|
index = (index / self.base).floor();
|
||||||
|
}
|
||||||
|
self.i += 1;
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VanDerCorput {
|
||||||
|
i: usize,
|
||||||
|
base: f32,
|
||||||
|
}
|
||||||
|
impl VanDerCorput {
|
||||||
|
pub fn new(base: f32) -> Self {
|
||||||
|
Self { i: 0, base }
|
||||||
|
}
|
||||||
|
pub fn points(base1: f32, base2: f32) -> impl Iterator<Item = Vec2> {
|
||||||
|
Self::new(base1)
|
||||||
|
.zip(Self::new(base2))
|
||||||
|
.map(crate::Tup2Extension::to_vec2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Iterator for VanDerCorput {
|
||||||
|
type Item = f32;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut q = 0.0;
|
||||||
|
let mut bk = 1.0 / self.base;
|
||||||
|
let mut i = self.i as f32;
|
||||||
|
|
||||||
|
while i > 0.0 {
|
||||||
|
q += (i % self.base) * bk;
|
||||||
|
i /= self.base;
|
||||||
|
bk /= self.base;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(q)
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,9 @@ if [[ -z $1 ]]; then
|
||||||
echo "example:"
|
echo "example:"
|
||||||
echo -e "\t$0 packagename"
|
echo -e "\t$0 packagename"
|
||||||
else
|
else
|
||||||
rm -rf "recordings/$1"
|
rm -rf "recordings/$1/frames"
|
||||||
cargo run --release --package $1 -- -record
|
cargo run --release --package $1 -- -record
|
||||||
ffmpeg -framerate 60 -i "recordings/$1/%03d.png" -pix_fmt yuv420p "recordings/$1.mp4"
|
filename="video$(( $(find recordings/$1/videos -type f -exec basename -s .mp4 {} \; | sed 's/^video//' | sort -n | tail -n1) + 1)).mp4"
|
||||||
|
ffmpeg -framerate 60 -i "recordings/$1/frames/%03d.png" -pix_fmt yuv420p "recordings/$1/videos/$filename"
|
||||||
echo "done"
|
echo "done"
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Reference in New Issue