/* * Author: William Brooks with Gary M. Epstein, inspired by * TrueBASIC sources provided by Dr. Gary M. Epstein * Creation: Sun Jun 22 12:06:04 PDT 1997 * Compiler: JDK(tm) 1.1.5 * Filename: $Source: /home/wbrooks/public_html/math/RCS/NormalModes.java,v $ * Version: $Revision: 1.17 $ * Modified: $Date: 1998/04/07 03:46:32 $ */ //package Edu.calpoly.math.gepstein.NormalModes import java.applet.Applet; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; /** * This applet shows the first eight contributing normal modes of string with * fixed ends. This is equivalent to two identical traveling waves moving * in opposite directions. * Wave Eq: Uxx - (1/c^2)*Utt = 0 * Boundary Conditions: U(0,t) = 0 & U(L,t) = 0 * Initial Condition: U(x,0) = triangular pulse at center of string. * Initial Condition: Ut(x,0) = 0 (released from rest.) * Originally Written in TRUE Basic for Macintosh by Dr. G.M. Epstein * Copyright (C) 1997, 1998 by Dr. G.M. Epstein *
gepstein@calpoly.edu * @author Gary M. Epstein */ public class NormalModes extends Applet implements Runnable { private double time = -0.02; private int midpoint; private int delay; private Thread animatorThread; private boolean frozen = false; private Dimension offDimension; private Image offImage; private Graphics offGraphics; private static final int WIDTH = 330; private static final int HEIGHT = 450; private static final int PANEL_COUNT = 9; private static final String caption = "th Harmonic Fourier Coefficient = "; private Dimension[] sizeList; /** * This method sets the frame refresh rate for the animation, the * background color, the amount of space the animation occupies in the * the WWW browser and how much space each individual contributing * wave will occupy in the total area. * * @see java.applet.Applet#init() */ public void init() { int fps = 8; setFont(new Font("Serif",Font.BOLD,10)); setBackground(Color.white); //Set the delay on redrawing intervals delay = (fps > 0) ? (1000 / fps) : 100; resize(WIDTH,HEIGHT); sizeList = new Dimension[PANEL_COUNT]; for (int i=0; i < PANEL_COUNT; i++) { sizeList[i] = new Dimension(WIDTH, (int)Math.round((HEIGHT/PANEL_COUNT)*(i+1))); } addMouseListener(new NormalModesListener() ); } /** * If the user has not requested that we stop the animation with a mouse * click, this method begins animation following initial program loading, * the user revisting the WWW page or de-iconifying the browser. If the * user did request that we stop the motion, this method does * nothing. * * @see java.applet.Applet#start() */ public void start() { if (frozen) { // Do nothing. The user has requested that we // stop changing the image. } else { // Start animating! if (animatorThread == null) { animatorThread = new Thread(this); } animatorThread.start(); } } /** * The Java Virtual Machine calls this method just after completing the *start() method. This method begins another thread running
* concurrently, the thread handling the animation. Calls the
* paint() method on regular intervals in order to continue
* the animation.
*
* @see java.lang.Thread#run()
*/
public void run() {
//Just to be nice, lower this thread's priority
//so it can't interfere with other processing going on.
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
//Remember the starting time.
long startTime = System.currentTimeMillis();
//This is the animation loop.
while (Thread.currentThread() == animatorThread) {
//Advance the animation frame.
time += 0.02;
// System.out.print("time is: \t" + time);
//Display it.
repaint();
//Delay depending on how far we are behind.
try {
startTime += delay;
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}
/**
* This method simply passes the current graphics context to the
* update() method.
*
* @see java.awt.Container.html#paint(java.awt.Graphics)
*/
public void paint(Graphics g) {
update(g);
}
/**
* This method does the computation and displays the graphical results
* on the screen.
*
* @see paint
* @see java.awt.Container.html#update(java.awt.Graphics)
*/
public void update(Graphics g) {
int top, bottom;
double yVal;
final int offset = 4;
Dimension currentSize;
//Create a offscreen graphics context, if we don't
//have a suitable one already
currentSize = getSize();
if ( (offGraphics == null)
|| (currentSize.width != offDimension.width)
|| (currentSize.height != offDimension.height) ) {
offDimension = currentSize;
offImage = createImage(currentSize.width,currentSize.height);
offGraphics = offImage.getGraphics();
for (int i=0; i < PANEL_COUNT; i++) {
sizeList[i] = new Dimension(WIDTH,
(int)Math.round((currentSize.height/PANEL_COUNT)*(i+1)));
}
}
//Erase the previous image.
offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0,currentSize.width,currentSize.height);
for (int i=0; i < PANEL_COUNT; i++) {
//Draw the axes on the off-screen drawing context
offGraphics.setColor(Color.black);
top = sizeList[i].height - (currentSize.height/PANEL_COUNT);
bottom = sizeList[i].height;
midpoint = (bottom-top)/2 + top;
offGraphics.drawLine(offset,midpoint,100+offset,midpoint);
offGraphics.drawLine(offset, top, offset, bottom);
}
//top = currentSize.height/PANEL_COUNT;
for (int i=0; i < PANEL_COUNT; i++) {
top = sizeList[i].height - (currentSize.height/PANEL_COUNT);
bottom = sizeList[i].height;
midpoint = (bottom-top)/2 + top;
offGraphics.setColor(Color.black);
if ( i < PANEL_COUNT-1 ) {
switch (i) { //Draw in the label based on the iteration
case 0:
offGraphics.drawString( "1st Harmonic " +
"Fourier Coefficient = " +
new Float(aTerms(2*i+1)).toString(), 110,midpoint);
break;
case 1:
offGraphics.drawString( "3rd Harmonic " +
"Fourier Coefficient = " +
new Float(aTerms(2*i+1)).toString(), 110,midpoint);
break;
default:
offGraphics.drawString( (2*i+1) +
caption + new Float(aTerms(2*i+1)).toString(),
110,midpoint);
}
//Draw in the waveform
offGraphics.setColor(Color.blue);
int xPrev = offset;
int yPrev = midpoint;
for (double xVal=0; xVal < 1; xVal += 0.02) {
yVal = fourier(2*i+1,xVal,time);
offGraphics.drawLine(xPrev, yPrev,
(int)(100*xVal+offset),
linearTransform( ((currentSize.height/PANEL_COUNT)/2)*yVal,
midpoint)
);
xPrev = (int)(100*xVal+offset);
yPrev = linearTransform(
((currentSize.height/PANEL_COUNT)/2)*yVal,
midpoint);
}
//Finish off drawing the last segment
double xVal = 100+offset;
yVal = fourier(2*i+1,xVal,time);
offGraphics.drawLine(xPrev, yPrev,(int)xVal,midpoint);
}
}
offGraphics.setColor(Color.black);
//Draw in the label
offGraphics.drawString("Partial Sum No. 15", 110, midpoint);
yVal = 0;
int xPrev = offset;
int yPrev = midpoint;
//Draw in the 15th partial sum waveform
offGraphics.setColor(Color.red);
for (double xVal=0 ;xVal < 1; xVal += 0.02) {
for (int k = 1; k <=15; k++)
yVal = yVal + (double)aTerms(k)*fourier(k,xVal,time);
yVal = .5 * yVal;
offGraphics.drawLine(xPrev, yPrev,
(int)(100*xVal+offset),
linearTransform( ((currentSize.height/PANEL_COUNT)/2)*yVal,
midpoint)
);
xPrev = (int)(100*xVal+offset);
yPrev = linearTransform(
((currentSize.height/PANEL_COUNT)/2)*yVal,
midpoint);
}
//Finish off drawing the last segment
double xVal = 100+offset;
yVal = yVal + (double)aTerms(15)*fourier(15,xVal,time);
yVal = .5 * yVal;
offGraphics.drawLine(xPrev, yPrev,(int)xVal,midpoint);
//Paint the image onto the screen
g.drawImage(offImage, 0, 0, this);
}
/**
* Stops the animation. In the case of the user leaving the web page or
* minimizing the browser, the JVM calls this method so that the animation
* won't continue running and taking CPU time without anyone to see it.
*
* @see java.applet.Applet#start()
*/
public void stop() {
//Stop the animating thread
animatorThread = null;
//Rid ourselves of the objects that we set up for double buffering
offGraphics = null;
offImage = null;
}
/**
* Calculates the coefficients for the Fourier series.
*
* @param n the number of the harmonic.
* @return the value of the n-th coefficient.
*/
public float aTerms(int n) {
final int L = 1;
float results;
results = (float) (32.0*(0.1)*L*Math.sin(n*(Math.PI/2))*
Math.pow(Math.sin(n*Math.PI*0.1/(4*L)),2.0) /
Math.pow((n*Math.PI*0.1),2.0) );
return results;
}
/**
* Calculates the value of a harmonic in the equation:
* for different times t and different locations x.
*
* @param n the number of the harmonic
* @param x the current location on the x-axis
* @param t the elapsed time since the start of the animation
* @return the value of the n-th harmonic.
*/
public double fourier(int n, double x, double t) {
double results;
final int velocity = 1;
final int L = 1;
final double h = 0.1;
results = Math.sin(n*Math.PI*x/L)*Math.cos(n*Math.PI*velocity*t/L);
return results;
}
/**
* Takes a value from the conventional cartesian coordinate system and
* translates it to the coordinate system in a single panel on our
* display
*
* @param yValue vertical value in the canonical cartesian coordinate system
* @param mid value halfway between 0 and the maximum vertical coordinate
* @return the value of yValue in the AWT coordinate system
*/
public int linearTransform(double yValue, int mid) {
return ((int)Math.round(yValue + mid));
}
class NormalModesListener extends MouseAdapter {
/**
* This method lets the user stop the action by
* depressing the mouse button on the graph
*/
public void mouseClicked(MouseEvent event) {
if ( frozen ) {
frozen = !frozen;
start();
} else {
frozen = !frozen;
animatorThread = null;
}
}
}
}// $Id: NormalModes.java,v 1.17 1998/04/07 03:46:32 wbrooks Exp wbrooks $