---- 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";
  } 



}