/* * 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 $