

/*
Java Applet spring.java
Giugno 1997 - Mr. freeDom alias Domenico Luciani

This applet demonstrates the damped simple harmonic
motion of a mass on a spring with friction. The user can
click and drag the mass to an initial position, and then
release it to begin the motion. Various parameters can be
adjusted at any time.

The applet uses double buffering in an attempt to
eliminate (or reduce) the flicker in the animation.  
*/


import java.applet.Applet;
import java.awt.*;

public class spring extends Applet implements Runnable
{
   Dimension offDimension,d;  // Variables used to create an 'offscreen'
   Image offImage;            // image via the update() method, to help 
   Graphics offGraphics;      // reduce flicker.
   int X1=300, Y1=60;

   int SprOffSet = 175;
   double time0 = 0;             
   double time = 0;              
   
   double timePause = 0;         
   double mass=1;         
   double k=1;                
   double damp=0.1;              // Damping constant [1/sec].
   double F0 = 1;
   double freqF = 1;

   int set=0;
   int x=0;
   int mx=0;

   double x0 = 0;       //  Condizioni Iniziali
   double v0 = 0;	      //

   double omega; 
   double epsilon;
   double ni, amp, alfa;
   double ampP;
   double beta;

   Color ltblue = new Color(0, 150, 255);  // Define ltblue color
   Thread t;       
            
   Button buttonStart, buttonPause, buttonResume, buttonStop;
   TextField textFieldMass, textFieldK, textFieldDamp, 
             textFieldF0, textFieldFreqF, 
             textFieldX0, textFieldV0;
   Label labelOmega, labelEpsilon, labelAmp, labelAmpP, labelAlfa, labelBeta;
	                                   
   public void init()                     //  Initialize :
   {                                      //  Set up the user interface
      offDimension = new Dimension(1,1);
      d = new Dimension(1,1);
      
      setLayout(new BorderLayout(10,10));
      Panel pUp = new Panel();
      Panel p1 = new Panel();
      Panel p2 = new Panel();
      Panel pBottom = new Panel();
      pUp.setLayout(new GridLayout(2,1));
      p1.setLayout(new GridLayout(2,10));
      p2.setLayout(new GridLayout(2,8));
      pBottom.setLayout(new FlowLayout());

      textFieldMass = new TextField(String.valueOf(mass),8);
      textFieldK = new TextField(String.valueOf(k),8);
      textFieldDamp = new TextField(String.valueOf(damp),8);
      textFieldF0 = new TextField(String.valueOf(F0),8);
      textFieldFreqF = new TextField(String.valueOf(freqF),8);
      textFieldX0 = new TextField(String.valueOf(x0),8);
      textFieldV0 = new TextField(String.valueOf(v0),8);
      
      labelOmega = new Label(String.valueOf(omega));
      labelEpsilon = new Label(String.valueOf(epsilon));
      labelAmp = new Label(String.valueOf(amp));
      labelAmpP = new Label(String.valueOf(ampP));
      labelAlfa = new Label(String.valueOf(alfa));
      labelBeta = new Label(String.valueOf(beta));

      p1.add(new Label("m  = ", Label.RIGHT));
      p1.add(textFieldMass);
      p1.add(new Label("d  = ", Label.RIGHT));
      p1.add(textFieldDamp);
      p1.add(new Label("k  = ", Label.RIGHT));
      p1.add(textFieldK);
      p1.add(new Label("F0  = ", Label.RIGHT));
      p1.add(textFieldF0);
      p1.add(new Label("µ  = ", Label.RIGHT));
      p1.add(textFieldFreqF);
      p1.add(new Label("x0  = ", Label.RIGHT));
      p1.add(textFieldX0);
      p1.add(new Label("v0  = ", Label.RIGHT));
      p1.add(textFieldV0);
      for (int i=0; i<6; i++) p1.add(new Label(" "));

      p2.add(new Label("epsilon  = ", Label.RIGHT));      
      p2.add(labelEpsilon);
      p2.add(new Label("omega  = ", Label.RIGHT));
      p2.add(labelOmega);
      p2.add(new Label("A  = ", Label.RIGHT));      
      p2.add(labelAmp);
      p2.add(new Label("alfa  = ", Label.RIGHT)); 
      p2.add(labelAlfa);

      for (int i=0; i<4; i++) p2.add(new Label(" "));
      p2.add(new Label("B  = ", Label.RIGHT));
      p2.add(labelAmpP);
      p2.add(new Label("beta  = ", Label.RIGHT));
      p2.add(labelBeta);

      pUp.add(p1);
      pUp.add(p2);

      buttonStart = new Button("Restart");
      buttonPause = new Button("Pause");
      buttonResume = new Button("Resume");
      buttonStop = new Button("Stop");

      pBottom.add(buttonStart);
      pBottom.add(buttonPause);
      pBottom.add(buttonResume);
      pBottom.add(buttonStop);

      add("North",pUp);
      add("South", pBottom);

      t=new Thread(this);
      t.start();      
      
      set();
   }
                                         //
   public void paint(Graphics g)         //  The guts of the painting
   {                                     //  is done in the update()
      d=size();                          //  method below.
      update(g);                         //
   }

   public boolean mouseDrag(Event e, int mDx, int mDy)
   {
      if(mDy > Y1+50 && mDy <Y1+100)
      {
         set=0;

         mDx -= X1;
         if (mDx > SprOffSet) { x=SprOffSet; }
         else if (mDx < -SprOffSet) { x=-SprOffSet; }
         else { x=mDx; }
         
         mx=x;
         x0=x;
         textFieldX0.setText(String.valueOf(x0));
         
         repaint();
         return true;
      }
      else
      {
         return false;
      }
   }

   public boolean mouseUp(Event e, int mDx, int mDy)
   {
      set();                           //  This insures that the mass
      return true;                     //  is not released into motion
   }                                   //  until the mouse button is up.

   public boolean action(Event e, Object o)
   {
      if (o.equals("Restart"))
      {
         if (t.isAlive()) {
         mass = Double.valueOf(textFieldMass.getText()).doubleValue();
         k = Double.valueOf(textFieldK.getText()).doubleValue();
         damp = Double.valueOf(textFieldDamp.getText()).doubleValue();
         F0 = Double.valueOf(textFieldF0.getText()).doubleValue();
         freqF = Double.valueOf(textFieldFreqF.getText()).doubleValue();
         x0 = Double.valueOf(textFieldX0.getText()).doubleValue();     
         v0 = Double.valueOf(textFieldV0.getText()).doubleValue();

         set();
         }
      } else if (o.equals("Pause"))
      {
         if (t.isAlive()) 
         {
            t.suspend();
            timePause = System.currentTimeMillis();
         }
      }
      else if (o.equals("Resume"))
      {
         if (t.isAlive()) 
         {   
            t.resume();
            time0 += (System.currentTimeMillis()-timePause);
         }
      }
      else if (o.equals("Stop"))
      {
         t.stop();
      } 

      return true;
   }

   private void set()
   {
      set = 1;

      time0 = System.currentTimeMillis();
      time=0;
      timePause = System.currentTimeMillis();

      omega = Math.sqrt(k/mass);              // Standard formula for frequency 
      epsilon = damp/(2*mass);
      ampP = F0/(mass*Math.sqrt(Math.pow(omega*omega-freqF*freqF,2)+4*epsilon*epsilon*freqF*freqF));
      beta = Math.atan(2*epsilon*freqF/(omega*omega-freqF*freqF));

      if (omega > epsilon)
      {
         ni = Math.sqrt(omega*omega-epsilon*epsilon); 
         double k1 = x0 - ampP*Math.cos(beta);
         double k2 = (epsilon*k1 - ampP*freqF*Math.sin(beta) + v0)/ni;
         amp = Math.sqrt(k1*k1+k2*k2);
         alfa = Math.atan(k1/k2);
         if ((amp*Math.sin(alfa)*k1) < 0) amp *= -1;
      }

      labelOmega.setText(String.valueOf(omega));
      labelEpsilon.setText(String.valueOf(epsilon));
      labelAmp.setText(String.valueOf(amp));
      labelAmpP.setText(String.valueOf(ampP));
      labelAlfa.setText(String.valueOf(alfa));
      labelBeta.setText(String.valueOf(beta));
   }

   public void run()      // Run an infinite loop where the M vector
   {                      // position is calculated as a function of
      while (true)        // time.
         if (set!=0)
         {
            time = (System.currentTimeMillis()-time0)/1000;

            int a = 0;
            if (omega > epsilon)
            {
               a = (int) (amp * (Math.exp(time*epsilon*(-1))) * (Math.sin(ni*time+alfa)) + ampP*Math.cos(freqF*time-beta));
            } else if (omega == epsilon)
            {
               //a = (int) (((x0-SprLen) + epsilon*(x0-SprLen)*time) * (Math.exp(time*epsilon*(-1))));
            } else if (omega < epsilon)
            {
               //double ni = Math.sqrt(epsilon*epsilon-omega*omega); 
               //double amp = (x0-SprLen) * Math.sqrt(1-(epsilon*epsilon)/(ni*ni));
               //double alfa = Math.tan(epsilon/ni*3.14);  // atanh approssimata
               //a = (int) (amp * (Math.exp(time*epsilon*(-1))) * cosh(ni*time + alfa));
            }
               
            mx= a;

            repaint();       //  begin until mouse button is released.     
         }
   }

   private double sinh(double alfa)
   {
      return (Math.exp(alfa) - Math.exp(-alfa))/2;
   }

   private double cosh(double alfa)
   {
      return (Math.exp(alfa) + Math.exp(-alfa))/2;
   }
   
    public void update(Graphics g)
    {  
       if((offGraphics == null)                // Setup an off-screen image
        ||(d.width != offDimension.width)      // via the update() method.
        ||(d.height != offDimension.height))   //
       {
          offDimension=d;
          offImage=createImage(d.width, d.height);
          offGraphics=offImage.getGraphics();
       }
       
       offGraphics.setColor(getBackground());
       offGraphics.fillRect(0,0, d.width, d.height);
       
       // Draw the wall and floor

       offGraphics.setColor(Color.black);
       offGraphics.drawLine(100, Y1+100, 500, Y1+100);
       offGraphics.drawLine(100, Y1+100, 90, Y1+90);
       offGraphics.drawLine(90, Y1+90, 490, Y1+90);
       offGraphics.drawLine(500, Y1+100, 490, Y1+90);

       // "Erase" the black lines in background as necessary

       offGraphics.setColor(getBackground());
       offGraphics.drawLine(X1-25+mx-10, Y1+90, X1-25+mx+50, Y1+90);

       if ((X1-25+mx+50>489) && (X1-25+mx+50<551)) offGraphics.drawLine(490, Y1+90, X1-25+mx+50, Y1+90+X1-25+mx+50-490);
       
       // Draw the mass

       offGraphics.setColor(Color.red);
       offGraphics.drawRect(X1-25+mx, Y1+50, 50, 50);
       offGraphics.drawLine(X1-25+mx, Y1+100, X1-25+mx-10, Y1+90);
       offGraphics.drawLine(X1-25+mx-10, Y1+90, X1-25+mx-10, Y1+40);
       offGraphics.drawLine(X1-25+mx-10, Y1+40, X1-25+mx, Y1+50);
       offGraphics.drawLine(X1-25+mx-10, Y1+40, X1-25+mx+40, Y1+40);
       offGraphics.drawLine(X1-25+mx+40, Y1+40, X1-25+mx+50, Y1+50);

       // Draw the spring

       //space= (int)((mx-25)/11);
       //int spr1=X1+10, spr2=X1+9, spr3=X1+11;

       float space= (float)mx/11;
       int spr1=X1, spr2=spr1-1, spr3=spr1+1;

       // Light blue parts:

       offGraphics.setColor(ltblue);

       offGraphics.drawLine((int)(spr1+space), Y1+80, (int)(spr1+2*space), Y1+60);
       offGraphics.drawLine((int)(spr2+space), Y1+80, (int)(spr2+2*space), Y1+60);
       offGraphics.drawLine((int)(spr3+space), Y1+80, (int)(spr3+2*space), Y1+60);

       offGraphics.drawLine((int)(spr1+3*space), Y1+80, (int)(spr1+4*space), Y1+60);
       offGraphics.drawLine((int)(spr2+3*space), Y1+80, (int)(spr2+4*space), Y1+60);
       offGraphics.drawLine((int)(spr3+3*space), Y1+80, (int)(spr3+4*space), Y1+60);

       offGraphics.drawLine((int)(spr1+5*space), Y1+80, (int)(spr1+6*space), Y1+60);
       offGraphics.drawLine((int)(spr3+5*space), Y1+80, (int)(spr3+6*space), Y1+60);
       offGraphics.drawLine((int)(spr2+5*space), Y1+80, (int)(spr2+6*space), Y1+60);

       offGraphics.drawLine((int)(spr1+7*space), Y1+80, (int)(spr1+8*space), Y1+60);
       offGraphics.drawLine((int)(spr3+7*space), Y1+80, (int)(spr3+8*space), Y1+60);
       offGraphics.drawLine((int)(spr2+7*space), Y1+80, (int)(spr2+8*space), Y1+60);

       offGraphics.drawLine((int)(spr1+9*space), Y1+80, (int)(spr1+10*space), Y1+60);
       offGraphics.drawLine((int)(spr3+9*space), Y1+80, (int)(spr3+10*space), Y1+60);
       offGraphics.drawLine((int)(spr2+9*space), Y1+80, (int)(spr2+10*space), Y1+60);

       // Dark blue parts:

       offGraphics.setColor(Color.blue);

       offGraphics.drawLine(spr1, Y1+60, spr1, Y1+100);
       offGraphics.drawLine(spr2, Y1+60, spr2, Y1+100);
       offGraphics.drawLine(spr3, Y1+60, spr3, Y1+100);


       offGraphics.drawLine((int)(spr1+mx), Y1+60, (int)(spr1+mx), Y1+80);
       offGraphics.drawLine((int)(spr2+mx), Y1+60, (int)(spr2+mx), Y1+80);
       offGraphics.drawLine((int)(spr3+mx), Y1+60, (int)(spr3+mx), Y1+80);

       offGraphics.drawLine(spr1, Y1+70, (int)(spr1+space), Y1+80);
       offGraphics.drawLine(spr1, Y1+71, (int)(spr2+space), Y1+80);
       offGraphics.drawLine(spr1, Y1+69, (int)(spr3+space), Y1+80);

       offGraphics.drawLine((int)(spr1+2*space), Y1+60, (int)(spr1+3*space), Y1+80);
       offGraphics.drawLine((int)(spr2+2*space), Y1+60, (int)(spr2+3*space), Y1+80);
       offGraphics.drawLine((int)(spr3+2*space), Y1+60, (int)(spr3+3*space), Y1+80);

       offGraphics.drawLine((int)(spr1+4*space), Y1+60, (int)(spr1+5*space), Y1+80);
       offGraphics.drawLine((int)(spr2+4*space), Y1+60, (int)(spr2+5*space), Y1+80);
       offGraphics.drawLine((int)(spr3+4*space), Y1+60, (int)(spr3+5*space), Y1+80);

       offGraphics.drawLine((int)(spr1+6*space), Y1+60, (int)(spr1+7*space), Y1+80);
       offGraphics.drawLine((int)(spr2+6*space), Y1+60, (int)(spr2+7*space), Y1+80);
       offGraphics.drawLine((int)(spr3+6*space), Y1+60, (int)(spr3+7*space), Y1+80);

       offGraphics.drawLine((int)(spr1+8*space), Y1+60, (int)(spr1+9*space), Y1+80);
       offGraphics.drawLine((int)(spr2+8*space), Y1+60, (int)(spr2+9*space), Y1+80);
       offGraphics.drawLine((int)(spr3+8*space), Y1+60, (int)(spr3+9*space), Y1+80);

       offGraphics.drawLine((int)(spr1+10*space), Y1+60, (int)(spr1+mx), Y1+70);
       offGraphics.drawLine((int)(spr3+10*space), Y1+60, (int)(spr1+mx), Y1+69);
       offGraphics.drawLine((int)(spr2+10*space), Y1+60, (int)(spr1+mx), Y1+71);

       g.drawImage(offImage, 0, 0, this);
    }
}
