xxxxxxxxxx
166
// Submission for @Sableraph's Birb's Nest weekly challenge
// CAW CAW CAW HONK
// Discord: https://discord.com/invite/hzsn3UHxbw
// Theme: Tangent
const MAX_TRIES = 25;
const START_COUNT = 3;
const MIN_SIZE = 0.0025;
const MAX_SIZE = 0.025;
const SIZE_POW = 1;
const BORDER = 0.05;
const ANGLE_RAND = 0.5;
const GROW_TIME = 10;
const WAGGLE = 0.125;
const COS_COLORS = [0, 0.1, 0.66];
const COLOR_RATE = 0.001;
const RESET_ON_DONE = false;
const SCREENSHOT_ON_DONE = false;
function setup() {
createCanvas(400, 400);
windowResized();
colorMode(RGB, 1);
reset();
}
function reset() {
frameCount = 0;
balls = [
{x: 0.5, y: 0.5, tx:0.5, ty: 0.5, tr: 0.1, r: 0.1, born: t, dir: -1}
];
let count = 0;
for (let i=0; i<START_COUNT; i++) {
const f = i/START_COUNT;
addBall(balls[0], f, MAX_SIZE * 1.5)
}
}
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 size = 1024 * 4;
resizeCanvas(size, size);
strokeWeight(4);
drawBalls();
save();
windowResized();
pop();
}
let t = 0;
function draw() {
t = frameCount / GROW_TIME;
noFill();
strokeWeight(1);
updateBalls();
drawBalls();
if (!growingBalls.length && RESET_ON_DONE) {
if (SCREENSHOT_ON_DONE)
screenshot();
reset();
}
}
function drawBalls() {
background(0);
balls.forEach(b => {
const v = (t * 0.00001 + b.born * COLOR_RATE) * GROW_TIME;
stroke(
cosn(v + COS_COLORS[0]),
cosn(v + COS_COLORS[1]),
cosn(v + COS_COLORS[2])
);
circle(b.x * width, b.y * height, b.r * width * 2);
});
}
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 (balls.some(b => {
return dist(b.tx, b.ty, x, y) < r + b.tr - 0.0001
})) {
return false;
}
const newBall = {parent, a, tx:x, ty:y, tr: r, r: 0,born:t, dir: -parent.dir};
balls.push(newBall);
growingBalls.push(newBall);
return true;
}
const randSize = () => pow(random(), SIZE_POW) * (MAX_SIZE - MIN_SIZE) + MIN_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++) {
if (addBall(b,
b.a + randAngle(),
randSize()
))
break;
if (i == MAX_TRIES-1) {
let nb = b.parent;
if (nb && nb.parent && nb.lt == 1) {
nb.born = t;
growingBalls.push(nb);
break;
}
}
}
}
});
growingBalls = growingBalls.filter(b => t - b.born < 1);
}
function cosn(v) {
return 0.5 + 0.5 * cos(v * TAU);
}