Summer 2009: Slashdotted!
Xmas 2013: I added a Javascript port of my wave simulation.

I want to "lure" my nephews and nieces towards science and engineering, and one of the things I've done towards that goal is to code some real-time physics simulators.

Modelling waves...
 
All the code I wrote is available below, and compiles under Linux, Free/Net/OpenBSD, Mac OS/x and Windows (with GCC/MinGW), with the usual...
bash$ ./configure
bash$ make
I have also made Win32 binaries available (cross-compiled with GCC-MinGW32) for the people that don't know how to compile the code, but would like to use it anyway...

For the specifics of each "demo", check the README inside each source package.

Download

Explosion/fireworks simulator

Download source code, and Win32 binary (compressed with 7-zip).

Click and hold-down the left mouse button to create a never ending supply of "debris".

One-dimensional wave simulator

Download source code, and Win32 binary (compressed with 7-zip).

Hit SPACE to pull down the string at a distance of 1/4 its full length (from the left). See if you can hit SPACE at the proper frequency (natural structural frequency) to maximize the wave amplitude... Can you make it reach the right side?

Two-dimensional waves simulator - a.k.a. "water"

2D waves
Waves... (they look much better
when they move :‑)
Download source code, and Win32 binary (compressed with 7-zip).

Click (and hold) the left mouse button to "pull" the water at the point where you click. Draging the mouse makes for nice waves that reflect around the borders of the "pool"...

Snow "generator"

Download the Win32 binary, the code was too embarassing to share :‑)

And yes, what you see there getting covered in snow is my... name... in Greek :‑) Hey, my nephews loved it! :‑)

Theory

The "snow" and "fireworks" simulations are easy to implement: we just add random horizontal noise to the falling snow flakes, and we make sure every firework particle is falling with a constant vertical acceleration.

The theory behind real-time wave simulation is a little more complex.

Assume we have a wave, $ f_n(i)$ , where $ i$ refers to the horizontal screen axis (i.e. the X-pixel coordinate). $ f_n(i)$ provides the pixel $ y$ -coordinate at frame $ n$ ; we want to find what the values will change to, at frame $ n+1$ .

Consider the individual pixels to represent the water molecules, and assume that each molecule is influenced only by its two neighbours (left and right), as if they are connected to it with springs. That is, assume that a given pixel's value, $ f_n(i)$ , is only influenced by the attractive force of its two neighbours, the left pixel, $ f_{nL} = f_n(i-1)$ , and the right pixel, $ f_{nR} = f_n(i+1)$ .

With $ f$ the position, $ v$ the velocity and $ a$ the acceleration, the laws governing motion tell us the following (using dt=1):

First: the pixel's position in the new frame is equal to the position in this frame, plus the vertical velocity:

$\displaystyle f_{n+1} = f_n + v_n$ (1)

The velocity is equal to the old velocity plus the acceleration:

$\displaystyle v_n = v_{n-1} + a_{n-1}\nonumber\\
$
And since springs cause acceleration that is linear to the "pulled" distance:

$\displaystyle a_{n-1} = k (f_{nL} - f_{n-2}) + k (f_{nR} - f_{n-2}) \nonumber\\
$
(where $ k$ is the spring's coefficient - the larger it is, the more acceleration is caused by the spring).

These equations can be used to perform the simulation, and they would work fine. There is one problem, though: the simulation would be slow, since there's quite a lot of calculations going on per pixel...

Instead, we will use the acceleration at time $ n+1$ (instead of $ n-1$ ), which basically means that we will approximate the current acceleration rate with the one we would have in two frames' time. We will also set $ k$ to 1, to further simplify the calculation.

Replacing $ v_n$ in 1 leads to the following:

$\displaystyle f_{n+1}$ $\displaystyle = f_n + v_{n-1} + a_{n-1}\nonumber$    
  $\displaystyle = f_n + (f_n - f_{n-1}) + a_{n+1}\nonumber$    
  $\displaystyle = 2 f_n - f_{n-1} + f_{nL} - f_n + f_{nR} - f_n\nonumber$    
  $\displaystyle = f_{nL} + f_{nR} - f_{n-1}$    

So the end result is amazingly simple: we add the $ y$ -coordinates of the two neighbouring pixels, and subtract the old value we used to have! Since the springs are losing power with each iteration, we will finally scale the output of the last equation so that it diminishes over time (e.g. multiply by 0.99):
 
$\displaystyle \boxed{ f_{n+1}(i) = 0.99 (f_n(i-1) + f_n(i+1) - f_{n-1}(i)) }$ (2)

And that is the only equation we need, to simulate one-dimensional waves in real-time.

For the corresponding two-dimensional problem, we just average the effects of the X- and Y- coordinate waves:

$\displaystyle f_{n+1}(i,j) = \frac{1}{2} ($ $\displaystyle f_{n+1,HorizontalWave} + f_{n+1,VerticalWave})\nonumber$    
$\displaystyle = \frac{1}{2} ($ $\displaystyle 0.99 (f_n(i-1,j) + f_n(i+1,j) - f_{n-1}(i,j)) + \nonumber$    
  $\displaystyle 0.99 (f_n(i,j-1) + f_n(i,j+1) - f_{n-1}(i,j)))\nonumber$    

So, for real-time simulation of two-dimensional waves, we only need this equation:
 
$\displaystyle \boxed{ f_{n+1}(i,j) = 0.99 ( \frac{f_n(i-1,j) + f_n(i+1,j) + f_n(i,j-1) + f_n(i,j+1)}{2} - f_{n-1}(i,j) ) }$ (3)

Hmm, Coding "semi-scientific" models of natural processes can quickly become addictive... I better stop now... :‑)

profile for ttsiodras at Stack Overflow, Q&A for professional and enthusiast programmers
GitHub member ttsiodras
 
Index
 
 
CV
 
 
Updated: Sat Oct 8 12:33:59 2022