---- String-plucker ----
/* Plucked and vibrating string clamped at both ends * /
/* Copyright M. Gallant 4/14/96 */
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Event;
import java.awt.Image;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Label;
public class plucker extends java.applet.Applet implements Runnable {
Graphics offGr;
Image offIm;
static float pi= (float)Math.PI ;
static float twoPi = (float)(2*pi) ;
int wApp, hApp;
Thread runner;
int xPick, yPick; //location of pick.
int lxAnchor, lyAnchor, rxAnchor, ryAnchor, mMax;
boolean release, halted, multi;
Font myFont;
public void init() {
myFont = new Font("TimesRoman", Font.BOLD, 15) ;
setBackground(Color.white) ;
setForeground(Color.blue) ;
wApp = this.size().width;
hApp = this.size().height ;
hApp = hApp/2;
xPick = wApp/2 ;
yPick = 2 ;
mMax = 10; // # of Fourier components.
lxAnchor = 0; lyAnchor = hApp/2;
rxAnchor = wApp; ryAnchor = hApp/2;
release = false;
halted = false;
multi = false;
offIm = createImage(wApp, 2*hApp);
offGr = offIm.getGraphics();
offGr.setFont(myFont) ;
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(new Button("Stop/Go")) ;
add(new Button("MultiPlot"));
add(new Button("More")) ;
add(new Button("Less")) ;
setStart();
}
public Insets insets() {
return new Insets(hApp+2, 1,1,1) ;
}
public void start() {
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
public void stop() {
if (runner != null) {
runner.stop();
runner = null;
}
}
public void run() {
float amp, coef, q, tPhase, pq, k1, k2, lString, period, cAmp, ampj;
float vSound=255 ; // speed of transverse waves in string (m/sec).
lString = 0.65f ;
period = 2*lString/vSound; // time period of fundamental in sec.
int xOld, yOld, y ;
if (release) { // if mouse released to let string go ..
offGr.clearRect(0,0,wApp, 2*hApp) ; // clear both string plot and amplitudes.
q=(float)xPick/wApp ;
pq=pi*q ;
coef=(float)(2.0*(hApp/2-yPick)/pi/pi/(1-q)/q) ;
cAmp = 2/(pi*pi*(1-q)*q) ;
offGr.setColor(Color.black) ;
offGr.drawString( "Fourier amplitudes m=1 - "+mMax, wApp/2, hApp+50) ;
offGr.drawString( " xPick/StringLength = " +(float)xPick/wApp, wApp/2, hApp+70) ;
offGr.setColor(Color.red) ;
for (int j=1; j<=mMax; j++) {
ampj = (float)(cAmp*Math.sin(j*pi*q)/(j*j)) ;
offGr.fillRect(20*j-10, 2*hApp-(int)(hApp*ampj), 10, (int)(hApp*ampj));
if (ampj<0) // draw unfilled bar for negative values.
offGr.drawRect(20*j-10, 2*hApp+(int)(hApp*ampj), 10, -(int)(hApp*ampj));
}
for(float tim=0; tim<=twoPi ; tim +=twoPi/43) {
xOld=0; yOld=hApp/2;
if (!multi || (tim==0)) { // only erase previous if not multiPlot.
offGr.clearRect(0,0,wApp, hApp) ;
offGr.setColor(Color.black) ;
offGr.drawRect(0,0, wApp-1, hApp-1);
offGr.setColor(Color.gray) ;
offGr.drawLine(0, hApp/2, wApp, hApp/2) ; // string axis.
}
offGr.setColor(Color.blue) ;
for (int x=0; x<= wApp; x+=2) {
amp=0 ;
k1 = (pi*x)/wApp ;
for (int m=1; m<=mMax; m++) { // add Fourier components.
amp=amp+(float)(Math.cos(m*tim)*Math.sin(m*pq)/(m*m)*Math.sin(m*k1)) ;
}
amp *= coef;
y = (int)(hApp/2-amp) ;
offGr.drawLine(xOld, yOld, x, y);
xOld=x; yOld=y;
}
repaint() ;
pause(200);
} // end time.
release = false; // reset release flag.
halted = true; // end of time loop is equivalent to halted.
} // end release.
} // end run.
public void pause(int time) {
try { Thread.sleep(time);} // wait for time milliseconds.
catch (InterruptedException e) { }
}
public void update(Graphics g) {
paint(g) ;
}
public void paint(Graphics g) {
g.drawImage(offIm,0,0,this);
}
public boolean mouseDrag(Event evt, int x, int y) {
xPick=x;
yPick=y;
setStart();
return true;
}
public boolean mouseDown(Event evt, int x, int y) {
xPick=x;
yPick=y;
halted = false;
stop() ;
setStart();
return true;
}
public boolean mouseUp(Event evt, int x, int y) {
xPick=x;
yPick=y;
release = true;
setStart() ;
stop();
start();
return true;
}
void setStart() {
if (xPick<=0 || xPick >= wApp || yPick<=0 || yPick >= hApp) { // if outside proper region.
xPick = wApp/2; yPick = 1 ; // if outside, set to default inside point.
}
offGr.clearRect(0,0, wApp, hApp) ;
offGr.setColor(Color.black) ;
offGr.drawRect(0,0, wApp-1, hApp-1);
offGr.setColor(Color.blue) ;
offGr.drawLine(lxAnchor, lyAnchor, xPick, yPick);
offGr.drawLine(xPick, yPick, rxAnchor, ryAnchor);
offGr.drawString( " xPick/StringLength = " +(float)xPick/wApp, wApp/2, hApp-10) ;
repaint();
}
public boolean action(Event evt, Object arg) {
if(evt.target instanceof Button)
controls((String)arg);
return true;
}
void controls( String but) { //start+stop toggle.
if (but.equals("Stop/Go")){
stop() ;
if (halted) {
halted=!halted;
release=true ;
start() ;
}
else halted=!halted;
}
else if(but.equals("More")) {
mMax +=1; // add another Fourier component.
stop() ;
release = true;
halted = false;
start() ;
}
else if(but.equals("Less")) {
stop() ;
mMax -=1;
if (mMax==0) mMax=1; //set 1 as lower limit.
release = true;
halted = false;
start() ;
}
else if(but.equals("MultiPlot")) {
multi=!multi ; //toggle
}
}
public String getAppletInfo() {
return "Copyright 1996 Michel Gallant";
}
}