/*========================================================
 Beats: by Eduardo Sanchez Velasco. 05/10/97
 A Java Applet that shows beats.
 This applet uses antiflikering tricks in method update(). 
 Like any applet with Runnable threads, it implements:
  init()
  start()
  run() -> repaint() -> update() -> myPaint()
  stop()
  
 Call the applet from a HTML file with the line:
 <applet code="rwalk.class" width=XXX height=YYY></applet>
 A width XXX = 301 and height YYY = 161 works well.
 If the background color is set to:
 <BODY BGCOLOR="#EAFFFF"> then the applet is transparent
 (if you have a 24-bit color display).
==========================================================*/

import java.util.*;
import java.awt.*;
import java.applet.*;

public class beats extends Applet implements Runnable
{
   Thread timer = null;
   int Width, Height;
   final Color Bgd    = new Color(0xEA,0xFF,0xFF);
   
   int time=0;                // Current time step
   int tPoints[], yPoints[];  // Array storing the wave
   int yEnv1[], yEnv2[];      // Arrays for the envelope
   boolean envelope = false;  // To display the envelope
   boolean MouseClick = false;// flag is true if click
   int x0Label, y0Label;      // Location of "time" label
      
   // Private variables used in update() to avoid flikering
   private Image     offScreenImage;
   private Graphics  offScreenGraphics;
   
   // ----------------- METHODS ----------------------------
    
   public void init()
   { 
     double K1,K2,A,Zero,sk1,sk2;
     Font Fnt = new Font("Helvetica", Font.PLAIN, 14);
     FontMetrics  Fmetric = getFontMetrics(Fnt);
    
     // Set background color 
     setBackground(Bgd);
          
     Width  = size().width; 
     Height = size().height; 
     
     // Location of the "time" label
     x0Label = Width-Fmetric.stringWidth("time")-4;
     y0Label = Height/2+Fmetric.getAscent()+3; 
     
     
     // Compute the angular freqencies & parameters
     K1 = (1.5*2*Math.PI)/Width;  // 1.5 Periods
     K2 = (20.0*2*Math.PI)/Width; // 20 Periods
     Zero  = (Height-1.0)/2.0;    // Location of origin  
     A = (Height-1.0)/2.0;        // Amplitude of wave
      
     // Define and compute the wave 
     tPoints = new int[Width];
     yPoints = new int[Width];
     yEnv1   = new int[Width];
     yEnv2   = new int[Width];
     for(int t=0; t<Width; t++) 
     {
         sk1 = Math.sin(K1*t);  // Modulating wave
         sk2 = Math.sin(K2*t);  // Carrier wave
         tPoints[t] = t;  
         yPoints[t] = (int)Math.round(Zero+A*sk1*sk2); 
         yEnv1[t] = (int)Math.round(Zero+A*sk1);
         yEnv2[t] = (int)Math.round(Zero-A*sk1);                 
     }   
     
     // Create and init the off screen image 
     offScreenImage = createImage(Width, Height);
     offScreenGraphics = offScreenImage.getGraphics();
     offScreenGraphics.setFont(Fnt);
           
   } //--------- End of init() -------------- 
   
   public void start() 
   {
     if(timer == null)
     {
        timer = new Thread(this);
        timer.start();
      }
      
   } //--------- End of start() ------------ 
    
   // In run() we set a minimum delay between frames. 
   // In most systems the delay will be much larger 
   // (it takes time to paint!). It is also browser
   // dependent. In my tests ME is faster than Netscape! 
   public void run() 
   {
      while (timer != null) 
      {
         try {Thread.sleep(15);}          // 15 ms delay
         catch (InterruptedException e){}
         
         repaint(); 
      }
      timer = null;
      
   } //--------- End of run() -------------- 

   // Override update() with tricks to avoid flikering 
   public final synchronized void update(Graphics g)
   {  
      myPaint(offScreenGraphics);
      g.drawImage(offScreenImage, 0, 0, null);
              
   } //--------- End of update() --------------  

   public void stop() 
   { 
      if( timer != null )
      {  
         timer.stop();
         timer = null; 
      }
   
   } //--------- End of stop() -------------- 
   
   // myPaint() is where most things happen
   public void myPaint(Graphics g) 
   {  
      int n,n0;
       
      if( time == 0 || MouseClick)        
      {
        // Clear board (do not use g.clearRect!)
        g.setColor(Bgd);
        g.fillRect(0,0,Width,Height);
      
        // Draw time axis and label in blue
        g.setColor(Color.blue);
        g.drawLine(0,Height/2,Width-1,Height/2); 
        g.drawLine(Width-1,Height/2,Width-6, Height/2-4); 
        g.drawLine(Width-1,Height/2,Width-6, Height/2+4); 
        g.drawString("time", x0Label, y0Label);
        
        if(time==0) time=2; // plot the first two points 
      } 
      
      if(MouseClick) { n0=1; MouseClick=false;}
      else           { n0=time-1;             }
 
      //Draw the envelope
      if(envelope)
      { 
        g.setColor(Color.magenta);
        for(n=n0; n<time ; n++)
        {
           g.drawLine(tPoints[n-1],yEnv1[n-1],
                      tPoints[n],yEnv1[n]);      
           g.drawLine(tPoints[n-1],yEnv2[n-1],
                      tPoints[n],yEnv2[n]);
        }                   
      } 
      
      
      // Draw the wave in red
      g.setColor(Color.red);
      for(n=n0; n<time ; n++)
      {
         g.drawLine(tPoints[n-1],yPoints[n-1],
                    tPoints[n],yPoints[n]);
      }             
      
      // Reset time at the end
      time++;
      if(time == Width) time=0;
      
   } //--------- End of paint() -------------- 
   
   // Toggle envelope display if mouse is clicked
   public boolean mouseDown(Event click, int x, int y) 
   {
      envelope = !envelope;   
      MouseClick=true;   
      return true;
      
   } //--------- End of mouseDown() ---------- 
   
}
