Goal of this project was to create a web application rendering dots (ants) which move avoiding the cursor and each other.
This project was created with TypeScript React. Trigonometric functions have tests created using Jest.
Ants is available at rasmusmerzin.github.io/ants.
Table of contents
Setup
I used Node.js 14.0.0 and Yarn 1.22.4 while developing this project.
Note: I found out that with these versions of Node.js and Yarn and with node-sass versions 4.13.1 and 4.14.0 using CSS variables
var(--
var)
incalc()
functions compiles in development mode but throws an error duringreact-scripts build
. In such cases I used Sass variables.
Module versions and scripts are available in package.json.
Downloading and installing
Clone the git repository with git clone https://github.com/rasmusmerzin/ants.git
.
Change working directory to ants with cd ants
.
When Node.js and yarn or npm are installed one can install needed node modules with yarn
or npm install
.
Running the app
When node modules are installed it is possible to run the app in development mode with yarn start
or npm start
.
Open http://localhost:3000 to view it in the browser.
More about development mode at create-react-app.dev.
Running tests
To run tests yarn test
or npm test
.
More about running tests at create-react-app.dev.
GitHub Pages
To deploy the app to GitHub Pages yarn deploy
or npm run deploy
.
Deploy script first creates the production build with react-scripts build
with project root as rasmusmerzin.github.io/ants and removes source map files.
Then the build directory is uploaded to branch gh-pages
at github.com/rasmusmerzin/ants which is then served by github.
More about react-app build
at create-react-app.dev.
More about gh-pages
at npmjs.com.
User interface
When the web page is opened 30 ants are drawn and updated 60 times per second.
Clicking and holding on the screen creates a highlight which ants are trying to avoid. It also shows current distancing range.
In the top left corner there is hamburger icon which opens a menu to configure simulation settings.
Algorithm
When the app component is mounted 30 objects of class Ant are randomly placed in the window space.
Additionally a job is initialized which calculates a velocity and the new position depending on that velocity for each Ant object with an interval depending on the render rate (state.renderRate).
For each Ant object a React component Dot is mapped. They are kept in sync by React runtime.
Mathematics
A velocity for each ant is calculated every frame depending on the count of peers in range.
One peer in range
When there is another ant in distancing range R
the a distancing push p
will be calculated.
Distancing push is a vector with a direction opposite to the direction of the neighbouring ant and a length inversely proportional to the distance d
of the same neighbouring ant.
When there is only one neighbour in range the velocity of the given ant is equal to it's neighbour's push vector.
R
‒ distancing range
k
‒ distancing factor
d
‒ neighbour distance
p
‒ distancing push
Multiple peers in range
When there are multiple neighbours in range the velocity of the given ant is equal to vector sum of it's neighbours' push vectors.
n
‒ number of neighbours in range
pᵢ
‒ i
neighbour's push
P
‒ push sum
No peers in range
When there are no peers in range ant's velocity will be turned at random to left or right by angle between 0 and ant's agility.
l
‒ previous frame's velocity
α
‒ amount to change velocity direction based on ants agility
(Math.random() -.5) *2 *
agility
Summary
It took about eight hours to work out the trigonometry and to implement it in TypeScript React. I wrote automatic tests for the trigonometric functions because I figured these are fundamental and more difficult to debug later on.
It took about 16 hours more to implement control dock to change settings in real time, ants avoiding the cursor and to add a CSS styling and a few animations.