I'm going to make a Delphi version using FireMonkey.
Basically, we need a simple, two-dimensional particle system. An array of "particles", each of which has a randomly assigned position and velocity. For every frame of the animation, each particle's position is updated based on its velocity and particles that are close enough together are connected with lines, giving that signature constellation effect.
Include uParticles in your uses clause, declare a TParticles field and instantiate it in the form's constructor, passing in the control you want to draw on as a parameter. Any control with a TCanvas will work. So basically, all of them. I used a TPaintBox.
A TTimer repaints the TPaintBox every 16ms (around 60 frames per second) and calls FParticles.Update and FParticles.Paint in the TPaintBox's OnPaint event.
TParticles has a reference to the parent control, so it handles any mouse events and knows the control's size, even if it's resized. We don't have to pass it any additional information
And since so little code is required in the form, I couldn't resist...
All of the interesting stuff is in uParticles.pas. For flexibility, several of the properties are configurable instead of being hard coded.
TParticles maintains an array of TParticle.
As I wrote earlier, TParticle doesn't keep track of much. Just the current position and velocity.
Internally, a vector is stored as just two values, so you could get away with using a point (and lots of people do), but explicitly declaring Velocity as a TVector implies that it has magnitude (speed) and direction and makes the intent more obvious to anyone reading the code.
Annoyingly, the implicit conversion from TVector to TPointF and the explicit ToPointF function are deprecated and you need to explicitly cast the vector when adding to or multiplying by a TPointF type.
As the name suggests, this is the number of particles being drawn. If the count goes down, the array is truncated. If the count goes up, new particles are added and initialised with a random starting point and velocity.
A line is drawn between each particle and any other particles that are within this distance.
BackgroundColor and LineColor
These seem pretty self-explanatory.
Particle libraries commonly have some kind of interactivity with the mouse. In our case, particles can ignore the mouse (default), be repelled by it or attracted to it.
When particles interact with the mouse, it's within this radius of the mouse.
This updates the state of TParticles so the next frame of the animation can be painted.
First, each particle's position is updated by adding its velocity.
Next, if particles are reacting to the mouse and the mouse is hovering over the control, a mouse force is calculated for each particle and used to update its position again. This force is calculated based on the inverse square of the particle's distance from the mouse, which means it's weaker the further away it is. Like gravity.
Finally, if any particles have moved outside of the bounds of the parent control, they are wrapped around to the opposite side.
This draws a circle for each particle on the parent control's canvas and draws a line between any particles that are within a certain distance of each other (LineDistance).
It's a little jarring when lines pop in and out of existence at full intensity. To avoid this, each line's opacity is adjusted based on the distance between particles so that longer lines are dimmer and shorter lines are brighter. Lines fade in and out, giving the animation a smoother appearance.
It took some playing around to come up with reasonable and interesting combinations of settings, so I made a test harness to make this easier. It was also really useful for testing and tweaking the functionality.
If anyone adds some features, I'd love to see what they come up with. I have some suggestions:
- Add particles on mouse click (or tap?).
- Image or gradient background.
- Multiple line colours based on some rule.
- Dynamically calculate line distance based on the control size instead of using a fixed value.
- Random particle sizes.
- Have particles bounce off edges instead of wrapping around.
- Configurable particle speed.