xxxxxxxxxx
199
const MAX_TRIES = 2;
const START_COUNT = 2;
const MIN_SIZE = 0.0025;
const MAX_SIZE = 0.0025;
const SIZE_POW = 15;
const BORDER = 0.15;
// const ANGLE_RAND = 10;
const ANGLE_RAND = 0.02;
const JUMP_ANGLE_RAND = 0.21;
const GROW_TIME = 1;
const WAGGLE = 0.15;
const COS_COLORS = [0, 0.1, 0.66];
const COLOR_RATE = 0.0003;
const RESET_ON_DONE = true;
const SCREENSHOT_ON_DONE = true;
const DIST = MIN_SIZE * 1 - 0.001;
const ROOT_LEN = 200;
const BASE_THICKNESS = 10;
function setup() {
createCanvas(400, 400);
windowResized();
colorMode(RGB, 1);
reset();
}
let maxDepth;
function reset() {
console.log("reset!");
maxDepth = 0;
frameCount = 0;
balls = [
{x: 0.5, y: 0.5, tx:0.5, ty: 0.5, tr: 0.01, r: 0.01, born: 0, dir: -1, depth: 0, angleRand: ANGLE_RAND}
];
let count = 0;
for (let i=0; i<START_COUNT; i++) {
const f = i/START_COUNT;
addBall(balls[0], f, balls[0].tr * 0.3)
}
background(0);
}
function windowResized() {
const size = min(windowWidth, windowHeight)
resizeCanvas(size, size);
}
// press 1 for 4k screenshot
function keyPressed() {
if (keyCode === 49) {
screenshot();
}
}
function screenshot() {
push();
const scale = 4;
const size = 1024 * scale;
resizeCanvas(size, size);
strokeWeight(scale);
drawBalls(4);
save();
windowResized();
pop();
}
let t = 0;
function draw() {
t = frameCount / GROW_TIME;
// t = 10 * mouseX / width;
noFill();
strokeWeight(1);
updateBalls();
drawBalls(1);
growingBalls = growingBalls.filter(b => t - b.born < 1);
if (!growingBalls.length && RESET_ON_DONE) {
if (SCREENSHOT_ON_DONE)
screenshot();
reset();
}
}
function drawBalls(scale) {
background(0);
balls.forEach(b => {
// const v = (t * 0.00001 + b.born * COLOR_RATE) * GROW_TIME;
// const v = b.born / t;
const v = 1 * b.depth / maxDepth;
stroke(
cosn(v + COS_COLORS[0]),
cosn(v + COS_COLORS[1]),
cosn(v + COS_COLORS[2])
);
strokeWeight(max(1, BASE_THICKNESS * (1 - b.depth / ROOT_LEN)) * scale);
if (b.parent)
line(b.x * width, b.y * height, b.parent.x * width, b.parent.y * height);
// stroke(1);
// strokeWeight(1);
// circle(b.x * width, b.y * height, b.r * 2 * width);
});
}
let growingBalls = [];
let balls;
function addBall(parent, a, r) {
const x = parent.x + cos(a * TAU) * (r + parent.r);
const y = parent.y + sin(a * TAU) * (r + parent.r);
// if (x-r <= BORDER || x+r > 1-BORDER || y-r <= BORDER || y+r > 1-BORDER)
// return false;
if (dist(x, y, 0.5, 0.5) > 0.5 - BORDER || r < 0.00001)
return false;
if (balls.some(b => {
return b != parent && dist(b.tx, b.ty, x, y) < r + b.tr + DIST
})) {
return false;
}
const newBall = {parent, a, tx:x, ty:y, tr: r, r: 0,born:t, dir: -parent.dir, depth: parent.depth + 1, angleRand: ANGLE_RAND};
balls.push(newBall);
growingBalls.push(newBall);
maxDepth = max(maxDepth, newBall.depth);
return true;
}
const randSize = () => pow(random(), SIZE_POW) * MAX_SIZE;
const randAngle = () => random(-ANGLE_RAND, ANGLE_RAND);
function updateBalls() {
growingBalls.forEach(b => {
const lt = min(1, t - b.born);
b.r = b.tr * lt;
b.lt = lt;
const r = b.parent.r + b.r;
const a = b.a + (1 - lt) * b.dir * WAGGLE;
b.x = b.parent.x + cos(a * TAU) * r;
b.y = b.parent.y + sin(a * TAU) * r;
if (lt == 1) {
for (let i=0; i<MAX_TRIES; i++) {
const distVal = (1 - dist(b.x, b.y, 0.5, 0.5) * 2);
const depthVal = max(1, 2 - 1 * b.depth / ROOT_LEN);
b.dir *= -1;
if (addBall(b,
// b.a + random(-b.angleRand, b.angleRand), // * b.r,
// b.a + Math.sign(random(-1, 1)) * b.angleRand, // * b.r,
b.a + b.dir * b.angleRand,
// constrain(randSize(), MIN_SIZE * distVal, MAX_SIZE)
// constrain(randSize() , MIN_SIZE, MAX_SIZE) * distVal
constrain(randSize(), MIN_SIZE * depthVal, MAX_SIZE * depthVal)
))
break;
if (i == MAX_TRIES-1) {
let nb = b.parent;
// while (!(nb && nb.parent && nb.lt == 1))
// nb = balls[floor(random(balls.length))];
if (nb && nb.parent && nb.lt == 1) {
nb.born = t;
nb.angleRand = JUMP_ANGLE_RAND;
growingBalls.push(nb);
}
}
}
}
});
}
function cosn(v) {
return 0.5 + 0.5 * cos(v * TAU);
}