Skip to content

Commit

Permalink
Merge pull request #6 from NicEastvillage/main
Browse files Browse the repository at this point in the history
fritfit
  • Loading branch information
falkecarlsen authored Oct 10, 2023
2 parents 0c642e3 + 464baed commit ac93416
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 1 deletion.
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<script src="bouncing_dvd.js"></script>
<script src="rain.js"></script>
<script src="rotating_bubbles.js"></script>
<script src="quadtree.js"></script>
<script src="main.js"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const animations = [
new BouncingDvd(),
new Rain(),
new RotatingBubbles(),
new Quadtree(),
]
let needsReset = true;
let curAnim = null;
Expand All @@ -24,7 +25,6 @@ function resizeCanvas() {
function repickAnimation() {
needsReset = true;
curAnim = animations[Math.floor(Math.random() * animations.length)];
canvas.clearRect(0, 0, canvas.width, canvas.height);
}

// ======= Loop =======
Expand Down
152 changes: 152 additions & 0 deletions quadtree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
class QuadTreeNode {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.children = [];
this.oldX = x;
this.oldY = y;
this.oldSize = size;
this.oldChildren = [];
this.isSettled = true;
this.settleTime = 0;
this.settleTimeMax = 0.3;
}

animTo(x, y, size, time) {
this.x = x;
this.y = y;
this.size = size;
this.settleTimeMax = time;
this.settleTime = this.settleTimeMax;
this.isSettled = false;
return this;
}

attemptSplit() {
if (this.children.length === 0 && this.isSettled) {
const t = 0.24;
this.children = [
new QuadTreeNode(this.x, this.y, this.size).animTo(this.x, this.y, this.size / 2, t),
new QuadTreeNode(this.x, this.y, this.size).animTo(this.x + this.size / 2, this.y, this.size / 2, t),
new QuadTreeNode(this.x, this.y, this.size).animTo(this.x, this.y + this.size / 2, this.size / 2, t),
new QuadTreeNode(this.x, this.y, this.size).animTo(this.x + this.size / 2, this.y + this.size / 2, this.size / 2, t),
].filter(child => child.x <= canvas.width && child.y <= canvas.height);
return true;
}
return false;
}

attemptMerge() {
if (this.children.length !== 0) {
for (const child of this.children) {
if (child.children.length !== 0 || child.oldChildren.length !== 0) {
return false;
}
}
for (const child of this.children) {
child.animTo(this.x, this.y, this.size, 0.24);
}
this.oldChildren = this.children;
this.children = [];
return true;
}
return false;
}

draw(ctx, img) {
if (this.oldChildren.length !== 0) {
// Old children are animating
for (const child of this.oldChildren) {
child.draw(ctx, img);
}
if (this.oldChildren[0].settleTime <= 0) {
this.oldChildren = [];
}
} else if (!this.isSettled) {
// This node is animating
this.settleTime -= 0.016;
let t = 1 - this.settleTime / this.settleTimeMax;
t = easeInOutCubic(t)
let x = lerp(this.oldX, this.x, t);
let y = lerp(this.oldY, this.y, t);
let s = lerp(this.oldSize, this.size, t);
ctx.drawImage(img, img.width * x, img.height * y, img.width * s, img.height * s);
if (this.settleTime <= 0) {
this.isSettled = true;
this.oldX = this.x;
this.oldY = this.y;
this.oldSize = this.size;
}
} else if (this.children.length === 0) {
// This not is a leaf
ctx.drawImage(img, img.width * this.x, img.height * this.y, img.width * this.size, img.height * this.size);
} else {
// This node is a branch
for (const child of this.children) {
child.draw(ctx, img);
}
}
}
}

class Quadtree {
reset() {
this.tree = new QuadTreeNode(0, 0, Math.ceil(canvas.height / img.height));
this.ticks = 0;
this.splits = 0;
this.direction = "split";
}

draw(ctx, img) {
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
this.tree.draw(ctx, img);
if (this.ticks >= 4) {
this.ticks = 0;
if (this.direction === "split") {
this.splitRandom(this.tree);
} else {
this.mergeRandom(this.tree);
}
if (this.splits >= 150) {
this.direction = "merge";
} else if (this.splits <= 0) {
this.direction = "split";
}
}
this.ticks++;
}

splitRandom(tree) {
// Use random depth-first search to find a leaf node to split
if (tree.attemptSplit()) {
this.splits++;
return true;
} else {
let rand = Math.floor(Math.random() * tree.children.length);
for (let i = 0; i < tree.children.length; i++) {
if (this.splitRandom(tree.children[(rand + i) % tree.children.length])) {
return true;
}
}
return false;
}
}

mergeRandom(tree) {
// Use random depth-first search to find a node to merge
if (tree.attemptMerge()) {
this.splits--;
return true;
} else {
let rand = Math.floor(Math.random() * tree.children.length);
for (let i = 0; i < tree.children.length; i++) {
if (this.mergeRandom(tree.children[(rand + i) % tree.children.length])) {
return true;
}
}
return false;
}
}
}
8 changes: 8 additions & 0 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@ function tinted(img, color) {
bx.drawImage(img, 0, 0);

return buffer
}

function lerp(a, b, t) {
return a + (b - a) * t;
}

function easeInOutCubic(x) {
return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
}

0 comments on commit ac93416

Please sign in to comment.