xxxxxxxxxx
179
const dim = [600, 600];
const mid = [dim[0] * 0.5, dim[1] * 0.5];
const col = {
bg: [255,253,237],
fg: [[252,231,98],
[255,177,122],
[79,71,137],
[32,19,53]].reverse(),
fade: 10
}
//https://coolors.co/fce762-fffded-ffb17a-4f4789-201335
class DotString {
constructor(n, r, ix=0, iy=0) {
this.xs = Array.from({ length: n }, () => [ix, iy]);
this.old = this.xs;
this.r = r;
this.n = n;
}
snap(s, t) {
const dx = s[0] - t[0];
const dy = s[1] - t[1];
const d2 = dx*dx + dy*dy;
if(d2 > this.r * this.r) {
const a = Math.atan2(dy, dx);
let x = -Math.cos(a) * this.r + s[0];
let y = -Math.sin(a) * this.r + s[1];
return [x,y];
}
return t;
}
drawline(g,x1,y1,x2,y2,i) {
const ii = i * (col.fg.length - 1);
const ind = Math.floor(ii);
i = ii - ind;
const c = lerpColor(color(col.fg[ind]),color(col.fg[ind+1]),i);
g.stroke(c);
g.line(x1,y1,x2,y2);
}
move(x,y) {
this.old = this.xs.map(p => p.map(x => x));
let prev;
this.xs = this.xs.map((c,i) => {
prev = i == 0 ? [x,y] : this.snap(prev, c);
return prev;
});
}
trail(g,s=0) {
this.xs.map((p,i) => [this.old[i], p]).reverse().map(
([[x1,y1], [x2,y2]], i) =>
i % s == 0 ? this.drawline(g,x1,y1,x2,y2,i/this.n) : null);
}
outline(g) {
this.xs.reduce(([x1,y1], [x2,y2], i) => {
this.drawline(g,x1,y1,x2,y2,1-i/this.n);
return [x2,y2];});
}
}
class ViewTrails {
}
class ControllerRandom {
constructor() {
this.cx = mid[0];
this.cy = mid[1];
this.randTarget();
}
randTarget() {
this.tx = Math.random() * dim[0];
this.ty = Math.random() * dim[1];
this.t = Math.floor(Math.random() * 12 + 6);
}
update() {
if(this.t == 0) this.randTarget();
else this.t--;
this.cx += (this.tx - this.cx)/25;
this.cy += (this.ty - this.cy)/25;
}
get() {
return [this.cx, this.cy];
}
}
class ControllerMouse {
constructor() {
this.cx = mid[0];
this.cy = mid[1];
this.t = 0;
}
update() {
this.cx = mouseX + Math.sin(this.t)*15;
this.cy = mouseY + Math.cos(this.t)*15;
this.t += PI / 15;
}
get() {
return [this.cx, this.cy];
}
}
class ControllerWave {
constructor() {
this.cx = 0;
this.cy = mid[1];
}
update() {
this.cx += 0.5;
const a = this.cx / dim[0] * TWO_PI * 3;
this.cy = mid[1] + Math.sin(a) * 200 + Math.sin(a*1.5) * 100 + Math.sin(a*10) * 50;
}
get() {
return [this.cx, this.cy];
}
}
let s, m;
let g1, g2;
function setup() {
createCanvas(dim);
g1 = createGraphics(dim);
g2 = createGraphics(dim);
g1.background(col.bg);
g2.clear();
m = new ControllerRandom();
s = new DotString(100, 2, m.get());
}
function draw() {
m.update();
s.move(m.get());
// update model
g1.background(col.bg, col.fade);
s.trail(g1,1);
// render trails
g2.clear();
s.outline(g2);
// render outline
image(g1,0,0);
image(g2,0,0);
}
/*
Model
create : Nodes -> Radius -> DotString
constrain : DotString -> DotString
View
createCol : bg -> [fg] -> ColState
Trails
setup : ColState -> Width -> Height -> Fade -> Skip -> ViewTrailState
draw : ViewTrailState -> DotString -> ()
Controller
bounds : x1 -> y1 -> x2 -> y2 -> Bounds
Random
setup : Bounds -> ControllerRandomState
update : ControllerRandomState -> DotString -> (ControllerRandomState, DotString)
Mouse
setup : () -> ControllerMouseState
update : ControllerMouseState -> DotString -> (ControllerMouseState, DotString)
Wave
setup : xinit -> [(Amplitudes,Frequencies,Phases)] -> ControllerWaveState
update : ControllerWaveState -> DotString -> (ControllerWaveState, DotString)
===
Orchestrator
-- needs to know when and how to spawn new DotStrings
-- has references to created DotStrings
-- has interface for setup and draw
-- some controllers can use orchestrator states
===
-- global constructs orchestrator
setup -- call orch setup
draw -- call orch draw
*/