Visualizing wave interference using FireMonkey and C++Builder XE2

By: Anders Ohlsson

Abstract: This article discusses how you can generate your own dynamic 3-dimensional mesh for visualizing wave interference using C++Builder XE2 and FireMonkey.

    Prerequisites!

    The wave function

The wave function we'll use in this article is:

         f(x,y) = A*sin(1/L*r-v*t)

where:

  • (x,y) = observation point
  • A = amplitude
  • L = wave length
  • r = distance between wave center and observation point
  • v = velocity of wave propagation
  • t = time

In C++Builder it simply becomes:

double f(double x, double y)
{
  return Amplitude*Sin(1/Length*Sqrt(Sqr(x-PosX)+Sqr(y-PosY))-Speed*t);
}

Note: It should be noted that this function simply gives us the state of equilibrium. We're completely ignoring starting scenarios and the fact that waves die out over time and distance.

The screen shot below shows one wave:

    Hide image
Click to see full-sized image

Two waves interfering with each other:

Hide image
Click to see full-sized image

And 4 waves while we're at it:

Hide image
Click to see full-sized image

    Generating the mesh

In order to generate the mesh, we borrow the code from my previous article, and modify it slightly to give it a time parameter:

double f(double x, double y)
{
  int          i;
  double  Result = 0;

  for (i = 0; i < 4; i++) {
    if (Wave[i].Enabled)
      Result += Wave[i].Amplitude*Sin(1/Wave[i].Length*Sqrt(Sqr(x-Wave[i].PosX)+Sqr(y-Wave[i].PosY))-Wave[i].Speed*t);
  }

  return Result;
}

void __fastcall TForm3D2::GenerateWave(double t)
{
  int          MaxX = 30;
  int          MaxZ = 30;
  double  u, v;
  double  px[4], py[4], pz[4];
  double  d = 0.5;
  int          NP = 0;
  int          NI = 0;
  TBitmap *BMP;
  int          k;

  // We have to set these up front. The buffers are CLEARED every time Length is set!!!
  Mesh1->Data->VertexBuffer->Length = 2*MaxX*2*MaxZ/d/d*4;
  Mesh1->Data->IndexBuffer->Length = 2*MaxX*2*MaxZ/d/d*6;

  BMP = new TBitmap(1,360);
  for (k = 0; k < 360; k++)
    BMP->Pixels[0][k] = CorrectColor(HSLtoRGB(k/360.0,0.75,0.5));

  u = -MaxX;
  while (u < MaxX) {
    v = -MaxZ;
    while (v < MaxZ) {
      px[0] = u;
      pz[0] = v;
      py[0] = f(px[0],pz[0]);

      px[1] = u+d;
      pz[1] = v;
      py[1] = f(px[1],pz[1]);

      px[2] = u+d;
      pz[2] = v+d;
      py[2] = f(px[2],pz[2]);

      px[3] = u;
      pz[3] = v+d;
      py[3] = f(px[3],pz[3]);

      // Set the points
      Mesh1->Data->VertexBuffer->Vertices[NP+0] = Point3D(px[0],py[0],pz[0]);
      Mesh1->Data->VertexBuffer->Vertices[NP+1] = Point3D(px[1],py[1],pz[1]);
      Mesh1->Data->VertexBuffer->Vertices[NP+2] = Point3D(px[2],py[2],pz[2]);
      Mesh1->Data->VertexBuffer->Vertices[NP+3] = Point3D(px[3],py[3],pz[3]);

      // Map the colors
      Mesh1->Data->VertexBuffer->TexCoord0[NP+0] = PointF(0,(py[0]+35)/45);
      Mesh1->Data->VertexBuffer->TexCoord0[NP+1] = PointF(0,(py[1]+35)/45);
      Mesh1->Data->VertexBuffer->TexCoord0[NP+2] = PointF(0,(py[2]+35)/45);
      Mesh1->Data->VertexBuffer->TexCoord0[NP+3] = PointF(0,(py[3]+35)/45);

      // Map the triangles
      Mesh1->Data->IndexBuffer->Indices[NI+0] = NP+1;
      Mesh1->Data->IndexBuffer->Indices[NI+1] = NP+2;
      Mesh1->Data->IndexBuffer->Indices[NI+2] = NP+3;
      Mesh1->Data->IndexBuffer->Indices[NI+3] = NP+3;
      Mesh1->Data->IndexBuffer->Indices[NI+4] = NP+0;
      Mesh1->Data->IndexBuffer->Indices[NI+5] = NP+1;

      NP = NP+4;
      NI = NI+6;
      v = v+d;
    }
    u = u+d;
  }

  Mesh1->Data->CalcNormals();
  Mesh1->Material->Texture = BMP;
}

    Animating the mesh

The above code generates a "snap shot" of the wave interaction between 4 waves at any time t.

Animating the wave is simply a matter of using a timer to increment time and re-generating the mesh over and over again:

void __fastcall TForm3D2::Timer1Timer(TObject *Sender)
{
  GenerateWave(t);
  t = t+0.1;
}

The waves are represented by this struct:

struct TWave {
    bool Enabled;
    double     Amplitude;
    double     Length;
    double     PosX;
    double     PosY;
    double     Speed;
};

In the demo project that accompanies this article, I have declared 4 starting waves like so:

TWave       Wave[4] = {{False, 1, 1, -20, -20, 1},
                {False, 1, 1, +20, -20, 1},
               {False, 1, 1, +20, +20, 1},
               {False, 1, 1, -20, +20, 1}};

Note that all 4 waves have the same properties, except that their origins are spread across the coordinate system. Specifically they're located in (-20,-20), (+20,-20), (+20,+20) and (-20,+20).

    Demo application

You can find my demo application in CodeCentral.

    Contact

Please feel free to email me with feedback to aohlsson at embarcadero dot com.

Server Response from: ETNASC03