What is a Quadtree?
Wikipedia says:
Quadtrees are most often used to partition a two-dimensional space by recursively subdividing it into four quadrants or regions. The data associated with a leaf cell varies by application, but the leaf cell represents a āunit of interesting spatial informationā.
These subdivisions reduce the amount of computation needed to query 2D data because instead of checking every single point, you can check only those within the same cell, or nearby cells. Itās useful for collision detection, image processing, or finding nearby landmarks on a map. Daniel Shiffman has an excellent series about quadtrees on The Coding Train.
How will they be used?
The design for this site was āheavily influencedā by Monica Dinculescuās, though Iāve added a dark mode, mobile menu, collapsible code blocks, project filtering, and style customisations. I enjoy the Trunchet tiles Monica uses on her site but thought I could do something to make it more me.
Making Tiles
As Iām not going to be querying any of my data, I can get away with plotting random points on a grid. Using quadtress could be considered a bit of an overengineered solution then, but I think itās still useful to explore a new algorithm. By changing the number of points and the capacity of each cell, I can generate infinite arrangements of cells and use the boundaries of those as tiles. The result is a structured grid with enough variation to make it interesting. I wanted to show some personality on this site so I started with a grid full of images related to my interests from places like Unsplash and Pexels. On the projects page I even experimented with using the current filter tags. The results ended up being too busy and took too long to load.
The images from Unsplash were already sized to fit each cell so it was hard to optimize them further, but fortunately every modern device comes preloaded with a standardised set of vector images - emoji! No matter the size theyāre displayed at, they are quick to render and take up a small number of bytes. Yes, they look different between devices but for how theyāre going to be used this really doesnāt matter. So that solved the issue with loading times, but what about the grid being too busy? Each cell has only a 20% chance of being filled by an emoji and I created functions for a number of shapes to fill in the rest. A small set of blocky shapes with a simple color palette produces a Bauhaus-esque aesthetic. The shapes are:
- Arc
- Circle
- Cross
- Grid
- Semicircle
- Stripes (straight or diagonal)
- Triangles (isosceles or right-angle)
By putting these inside a RenderShape class I could use the constructor to simplify the common parameters for every shape and reduce the overall size of the JavaScript file. I can add new shapes to the class without having to worry about how itās called either, as this is handled at runtime:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class RenderShape(){
constructor(ctx, originX, originY, width, count){
//...
//give each shape a side to stick to
this.position = count % 4;
}
arc();
circle();
cross();
grid();
semicircle();
stripes();
triangles();
}
//return every property that is a function, but not the constructor
let shapes = Object.getOwnPropertyNames(Object.getPrototypeOf(renderShape)).filter(item => {
return typeof renderShape[item] === 'function' && item != "constructor";
});
let index = Math.round(Math.random() * shapes.length) % shapes.length;
let shape = shapes[index];
Here you can change the capacity and point count to see the difference it makes:
Thatās all I have for now! Try coming back near Halloween or Christmas to see what the tiles look like then š
Thanks for reading! š