Metaballs
In this project, I wanted to make my implementation of Metaballs in TypeScript.
You can see it for yourself here.
What are those metaballs?
A metaball is an isoline shape that has an organic cell-like ability to merge with other metaballs.
How does it work?
Before we can calcluate anything, we need to create cricles that move around. Then we can calculate the value for every cell using a function that takes in the distance to each of the circles.
If the value is lower than 1 the cell is not in the metaball and if its above 1 its inside the metaball. We will only draw the contour of the metaball which is also called the isoline of our function.
The obvious solution is to change the colour of every pixel if it's value equals one and call it a day. Simple as that.
But if we do some quick maths and calculate how many pixels there are in Full HD resolution. 1920x1080 gives us 2 073 600 pixels. If we were to check every pixel on the screen, in a single frame and we would have to render 60 of those frames every second, that equals in 124 416 000 operations per second. Yeah, that's a little too much even for computers of today.
So, we need to optimize.
Marching squares
Enter the marching squares algorithm. What it does is - it takes the value of the metaball function of every corner of our cell and assigns it a state based on that. There are 16 possible states (2⁴). Based on a state we draw a predefined line for every cell.
We get some fast but very blocky metaballs. Of course, we can pump up the resolution, but that would result in only a little less blocky metaballs to some extent.
Linear interpolation
At the moment, for every state, we have a hardcoded line that goes from one corner to another. And if that line doesn't start or end on one of the corners, it does that from the middle of the cell. If there only was a way to calculate those positions on the go.
The linear interpolation algorithm comes to the rescue! By adding the interpolated output to the starting/ending points of the lines our cells no longer are confined to prefefined positions. The only thing left to do is decrease the cell size to 10px. And we get this!