There are all sorts of resonances around us, in the world, in our culture, and in our technology. A tidal resonance causes the 55 foot tides in the Bay of Fundy. Mechanical and acoustical resonances and their control are at the center of practically every musical instrument that ever existed. Even our voices and speech are based on controlling the resonances in our throat and mouth. Technology is also a heavy user of resonance. All clocks, radios, televisions, and gps navigating systems use electronic resonators at their very core. Doctors use magnetic resonance imaging or MRI to sense the resonances in atomic nuclei to map the insides of their patients. In spite of the great diversity of resonators, they all share many common properties. In this blog, we will delve into their various aspects. It is hoped that this will serve both the students and professionals who would like to understand more about resonators. I hope all will enjoy the animations.

For a list of all topics discussed, scroll down to the very bottom of the blog, or click here.

Origins of Newton's laws of motion

Non-mathematical introduction to relativity

Three types of waves: traveling waves, standing waves and rotating waves new

History of mechanical clocks with animations
Understanding a mechanical clock with animations
includes pendulum, balance wheel, and quartz clocks

Water waves, Fourier analysis



Sunday, May 8, 2011

Ways to make waves in flash

Since my retirement from teaching in 2007, I have been writing a blog that includes flash animations on the physics of waves and resonances. As a physicist, I have programmed many different computers over the years in various languages. At the same time I am not a professional programmer. The last few months represent my first foray into Flash and Actionscript. Primarily because of cost, I have used Swish Max and found it to work well for my purposes. You can view my physics animations at http://resonanceswavesandfields.blogspot.com/. In the posting below, I will discuss a few ways I have discovered to animate wave motion in flash.

1. Dragging a long wave image repeatedly across the scene

In this first method, one simply draws a long banner-like object containing a number of complete cycles of the wave. The banner is somewhat wider than the scene (or wider than a mask if you wish to go that route). Then playing with the time line, one makes this wave slide in the desired direction, exactly one wavelength in the number of frames allotted. At the end of these frames, the wave is reset to its original position and the cycle is repeated, again and again. When it is played, the viewer sees a myopic view, and in this view, the wave seems to slide in the desired direction forever. The viewer is not aware that the wave really only slides one wavelength and is then abruptly restarted at its original position to repeat the process. Some tips on doing the above are:

  • To make the wave, first draw one wavelength only. Then set "snap to grid" and "show grid". This allows you to copy the one wavelength repeatedly and position the copies to line up perfectly. You can add anything you want in front of the wave or behind it. I show a mermaid and the sun.
  • A real challenge of this method is to make sure, at the last frame, the wave has moved exactly one wave length over and hasn't changed height. You will notice a slight flicker in my rendition due to my less-than-perfect alignment.
  • A real plus of this method is that you can make the wave as complex or artistic as you want.
  • Another feature is that this method uses no programming (or "script" as SwishMax calls it).

In the first animation below I show the wave without the required undersized scene1 boundaries, so you can see what is happening. In that, I show a rectangle representing the stage edges or mask size. The second animation shows the myopic effect with a properly undersized scene or mask. Mouse over the animations to see the action.

What the programmer sees:

What the viewer sees:



In order to allow the viewer to stop the action, I added the following script to scene1:

onSelfEvent(load){stop( )}
onSelfEvent(rollOver){gotoAndPlay(1);}
onSelfEvent(rollOut){stop( )}


Another example, this one showing a surface wave:

Carrying this idea to the next level, below we have a two dimensional wave done with this method. This might represent an aerial view of ocean waves, looking down at the ocean from an airplane, for example. To help with moving one exact wave length, I added two guide lines as seen in the first of the two animations below.

The actual waves were made, by first drawing a rectangle one half wavelength wide, using gradient fill to make it colored from blue to white. The outline was turned off. This rectangle was then copied, flipped, and positioned up beside the first one (having the snap on helped all these operations). Then the set of two rectangles was copied repeatedly and lined up to complete the long set of waves. The completed wave banner was then rotated. It was made to move one exact wavelength in 30 frames.

What the programmer sees:



What the viewer sees:


The above method is quite flexible. One could skew the wave field above and move it across the scene. One might also add ripples to the top of the waves. Or he could make the wave segments be trapezoidal and instead of sliding the wave field, he could rotate it about a somewhat removed center. This would give the illusion of a perspective view of the waves.



2. Waves by equation

Another way to create a wave in flash is by using an equation for it and drawing a curve to that equation. Of course this is a scientist's, engineer's, or mathematician's method of choice. Most of the waves in my blog are done using this method. And it certainly requires some programming, but not very involved programming.

The basics parts to the program are:

  • We need to draw the scene: a foreground, a blank invisible rectangle as a "container" for the wave layer (check the container as a "target"), and a background. If you need only the wave, forget this step. On the other hand, having a container makes getting the layering (or "depth") easy so that you can have other objects in the scene in front of and behind the waves. Be sure not to stretch the container or all coordinates in it will be stretched. I set the container's origin to equal scene 1's origin to keep the coordinates simple.
  • Similar to the previous program, I have the animation set to only operate if the viewer mouses over the animation and it stops when the mouse leaves the animation. This avoids having the animation run when the viewer is not looking at that page. To achieve this, on load, the first graph is drawn, then in Frame 1 there is a stop. When the viewer mouses over the animation, the program starts on Frame 2, where it draws the second graph, proceeds to Frame 3 which adds to theta and returns the program to Frame 2 to redraw the graph with the larger theta. It continues on this cycle until the user mouses off the animation. The ever increasing theta makes the waves progress with time, provided we use the correct equation.
  • If you have questions on the specific steps, go to the Swish Max user's guide and search on the instruction of interest. The steps for producing the filled wave shape are shown under the "beginFill" instruction.
  • Basically, this program redraws the wave every cycle through the program. It uses the standard equation for a traveling wave of y = A cos (kxωt) . If you would like to read more about the equation you might read my blog on the subject or other sources like Wikipedia.
  • The advantages of this method is that it can graph equations and produce waves as specified by equations. Thus, in some sense, it is very flexible. Any wave that has a nice mathematical formula can be animated with this method. For example, with a little augmentation of the program below, we can produce waves on a mesh surface. These can include both linearly and radially propagating waves. Examples of these are at http://resonanceswavesandfields.blogspot.com/.
  • The disadvantage of this method is that it requires programming, it is hard to create "artistic" waves, and while the flash program size is tiny (no bit maps) it can be very computationally intensive and slow on slow computers.
  • The script and the actual animation are shown below.

The animation:

Mouse over it to see the action.

The script:


onSelfEvent(load){
    var theta:Number=0; // initial value of omega x time.
    const xmax:Number=Stage.width;
    const N:Number=xmax/dx;
    const y1:Number=Stage.height;
    const y0:Number=220;
    drawGraph();
 }// end of load
        
onSelfEvent(rollOver){gotoAndPlay(2);}

onSelfEvent(rollOut){stop()}

function drawGraph(){
    container.clear();
    container.lineStyle(1,0x000000,100);
    container.beginFill(0x0000FF,100);
    container.moveTo(0,y1);
    var x:Number=0;
    for(var i:Number=0;i<N;i++){
        var y:Number=y0-15*Math.cosdeg(1.1*x-theta);
        container.lineTo(x,y);           
        x+=15;        
        }// end of i loop
    container.lineTo(x-dx,y1);
    container.lineTo(x,y1);
    container.endFill();           
} // end of function drawGraph

onFrame(1){  stop()  }

onFrame(2){ drawGraph();  }
    
onFrame(3){ theta+=15;  gotoAndPlay(2)  }

onFrame(4){  stop()  }


3. Waves by shifting values through an array

This method is similar to the last one, in that we use an equation to produce the wave height, and we redraw the wave each cycle, but to reduce computation time, we save the values from previous computations and shift them through an array. Each time through Frame 2, the program calculates a single new value to push into the array. It then, in one loop (in the drawGraph function), simultaneously plots the points in the array and shifts the array values over by one.

The animation:

Mouse over it to see the action.

The script:


onSelfEvent(load){
    y=new Array;  // array to store y values of the wave
    
    var theta:Number=0; // initial value of omega x time in units of degrees
    const dtheta:Number=20;
    const dx:Number=6;  // the spacing of plotted points in pixels
    const k:Number=dtheta/dx;  // the wavenumber in degrees per pixel 
       
    const xmax:Number=Stage.width;
    const N:Number=xmax/dx;
    const y1:Number=Stage.height; // base of the filled area in pixels
    const y0:Number=220; // average height of the wave in pixels
    
    fillArray(theta);
    drawGraph();
    }// end of load
        
onSelfEvent(rollOver){gotoAndPlay(2);}

onSelfEvent(rollOut){stop()}

function waveValue(x,theta){ return y0-15*Math.cosdeg(k*x-theta);  }

function fillArray(theta){
    for(var i:Number=0;i<N;i++){
       y[i]=waveValue(i*dx,theta); 
    }// end of i loop
  }// end of function fill Array

function drawGraph(){
    container.clear();
    container.lineStyle(1,0x000000,100);
    container.beginFill(0x0099FF,80);
    container.moveTo(0,y1);
    var x:Number=0;
    var ynow:Number=waveValue(0,theta);
    for(var i:Number=0;i<=N;i++){
        container.lineTo(x,ynow);
        var ytemp:Number=ynow;
        ynow=y[i]; 
        y[i]=ytemp;               
        x+=dx;        
        }// end of i loop
     container.lineTo(x-dx,y1);
     container.lineTo(x,y1);
     container.endFill();           
  }// end of function drawGraph

onFrame(1){stop()}

onFrame(2){ theta+=dtheta; drawGraph(); }
    
onFrame(3){ gotoAndPlay(2) }

onFrame(4){ stop() }


4. Waves by difference equation

While the previous three methods assume that waves are repeating functions that move at a constant speed, this last method uses a finite difference approach to actually solve the wave equation

,

where c is the speed of the wave. This method operates on an array of x and y values. The x values are not actually stored as an array, but instead are calculated as needed, assuming they occur at regular intervals, i.e. xi+1 = xi + dx, where dx is constant. In contrast, the y are stored in an array, similar to that used in the previous example. The x and y values are used to draw the wave each cycle in the program.

In order to model a wave that is changing with time, we need an equation to tell us how much each y changes each cycle. This can be gotten from the wave equation above. We can rearrange the equation to solve for the time derivate term, the second term above, as

,

where we have also shown alternate styles of writing the time derivate term. Now, changing from a continuous derivative to a difference notation (required for actual computation), we rewrite this equation as:

.

In the right most term, we have changed the x derivate into its difference counterpart, i.e.:

.

This equation requires the knowledge of y values on either side of a particular array element to calculate the next value of that element. It works for all the elements, except for the two end point y values that do not have element on both their sides. Thus we need to treat the end values specially. I decided to let the left end point be driven in a sinusoidal pattern, assuming that this is the source of the waves. The right most point is harder. Unless it is treated properly the waves will reflect and mix with the oncoming waves. (Yes, these equations embody the physics of waves and will demonstrate reflections if so allowed.) Avoiding this requires that the motion of the right end point is damped with damping coefficient D. I experimented with various values of D until the reflections were absent. The equation for this right most element is

.

These equations have be incorporated in the following animation.

The animation:

Mouse over it to see the action.

The script:


onSelfEvent(load){
    y=new Array;  // array to store y values of the wave
    vy=new Array;  // to store velocities of y values
    
    var theta:Number=0; // initial value of omega x time in units of degrees
    const dtheta:Number=20;
    const dx:Number=12;  // the spacing of plotted points in pixels
    const invM:Number=1;  // inverse mass in 1/(framesSquared).  Program unstable if this is > 1.0 .
    const damp:Number=1.0 // damping coefficient of the right most element
       
    const xmax:Number=Stage.width;
    const N:Number=Math.floor(xmax/dx);
    const y1:Number=Stage.height; // base of the filled area in pixels
    const y0:Number=220; // average height of the wave in pixels
    
    fillArray(); // call of the function defined below
    drawGraph();  // call of the function defined below
    }// end of load
        
onSelfEvent(rollOver){gotoAndPlay(2);}

onSelfEvent(rollOut){stop()}

function fillArray(){
    for(var i:Number=0;i<=N;i++){
       y[i]=y0;
       vy[i]=0; 
      }  // end of i loop
   }  // end of function fill Array

function drawGraph(){
    container.clear();
    container.lineStyle(1,0x000000,100);
    container.beginFill(0x0099FF,80);
    container.moveTo(0,y1);
    var x:Number=0; 
    for(var i:Number=0;i<=N;i++){ container.lineTo(x,y[i]); x+=dx;}      
    container.lineTo(x-dx,y1);
    container.lineTo(x,y1);
    container.endFill();           
  }  // end of function drawGraph

function updateArray(){
    y[0]=y0-15*Math.sindeg(theta);  // the left element is driven
    for(var i:Number=1;i<N;i++){   // update the velocities of the elements   
        vy[i]+=invM*(y[i+1]+y[i-1] -2*y[i]);            
        }// end of i loop       
    vy[N]+=invM*(y[N-1]-y[N])-damp*vy[N];
    for(i=1;i<=N;i++){   // now update the positions   
        y[i]+=vy[i];            
        }// end of i loop           
}// end of function updateArray

onFrame(1){stop()}
onFrame(2){
    theta+=dtheta; 
    drawGraph(); 
    updateArray(); 
    }// end of onFrame1
    
onFrame(3){   gotoAndPlay(2)}
onFrame(4){stop()}