/***** Harmonic oscillator          *****
 ***** @author: Christoph Federrath *****/

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

public class Oscillator extends Applet implements Runnable, ActionListener
{
    private static final int W = 400, H = 220; //graphic window
    private int px, py = H/2; //pixelcoordinates
    private double x; //position
    private double m = 1., D = 0.25; //mass, spring strength
    private double omega = Math.sqrt(2*D/m); //frequency
    private double amp = -0.8; //amplitude
    private int reib = 0; //friction
    private static final double dt = 0.05; //timestep
    private int step = 0;
    private int xPoints1[] = new int[10]; //pixelcoordinates for spring1
    private int yPoints1[] = new int[10]; //pixelcoordinates for spring1
    private int xPoints2[] = new int[10]; //pixelcoordinates for spring2
    private int yPoints2[] = new int[10]; //pixelcoordinates for spring2
    private boolean stop = false; //go/pause
    Button but_go, but_D_plus, but_D_min;
    Button but_reib_plus, but_reib_min;
    Thread animator;
    private Graphics g, dbg;
    private Image dbi;

    public void init()
    {
        setLayout(null);
        Canvas can = new Canvas();
        can.setBackground(Color.white);
        can.setBounds(1,1,W,H);
		can.addMouseListener(new ML());
		can.addMouseMotionListener(new ML());
        Panel pan = new Panel();
        pan.setLayout(null);
        pan.setBounds(W+1,1,100,H);
        pan.setBackground(new Color(0,200,100));
        pan.setFont(new Font("Verdana",Font.PLAIN,10));

	    but_D_plus = new Button("Spring +");
	    but_D_min = new Button("Spring -");
	    but_reib_plus = new Button("Friction ++");
	    but_reib_min = new Button("Friction --");
	    but_go = new Button("Go/Pause");
		but_D_plus.setBounds(10,25,80,25);
		but_D_min.setBounds(10,55,80,25);
		but_reib_plus.setBounds(10,95,80,25);
		but_reib_min.setBounds(10,125,80,25);
		but_go.setBounds(10,170,80,25);
		but_D_plus.addActionListener(this);
		but_D_min.addActionListener(this);
		but_reib_plus.addActionListener(this);
		but_reib_min.addActionListener(this);
		but_go.addActionListener(this);

		pan.add(but_go); pan.add(but_D_plus); pan.add(but_D_min);
		pan.add(but_reib_plus); pan.add(but_reib_min);
		add(pan); add(can);

        g = can.getGraphics();
        dbi = createImage(W,H);
        dbg = dbi.getGraphics();
    }

    public void run()
    {
       	try{Thread.sleep(200);} catch(InterruptedException exc){}
		while(Thread.currentThread() == animator)
		{
			while(stop){try{Thread.sleep(20);}catch(InterruptedException exc){}}

        	try{Thread.sleep(2);} catch(InterruptedException exc){}

			x = amp*Math.exp(-0.1*reib*step*dt)*Math.cos(omega*step*dt); //new x

			pixels(); //calculate pixelcoordinates
            paint(); //paint new frame
			step++;
	    }//while
    }//run

	public void startwerte()  //method for setting initial values
	{
            animator = null;
            stop = false;
            step = 0;
			omega = Math.sqrt(2*D/m);
	}//startwerte()

	public void pixels()  //method for calculating pixelcoords
	{
		int length, currentLength1, currentLength2; //spring lengths
		double d1, d2; //pixelcoordinates for springs

		length = W/2-10;
		px = (int)Math.round((double)W/2 + (length-40)*x); //mass position
		currentLength1 = px-20;
		currentLength2 = W-px-20;
		d1 = currentLength1/16.;
		d2 = currentLength2/16.;

		xPoints1[0] = 11;		       yPoints1[0] = H/2;
		xPoints1[1] = (int)(11+d1);    yPoints1[1] = H/2+7;
		xPoints1[2] = (int)(11+3*d1);  yPoints1[2] = H/2-7;
		xPoints1[3] = (int)(11+5*d1);  yPoints1[3] = H/2+7;
		xPoints1[4] = (int)(11+7*d1);  yPoints1[4] = H/2-7;
		xPoints1[5] = (int)(11+9*d1);  yPoints1[5] = H/2+7;
		xPoints1[6] = (int)(11+11*d1); yPoints1[6] = H/2-7;
		xPoints1[7] = (int)(11+13*d1); yPoints1[7] = H/2+7;
		xPoints1[8] = (int)(11+15*d1); yPoints1[8] = H/2-7;
		xPoints1[9] = px-10;		   yPoints1[9] = H/2;

		xPoints2[0] = W-12;		         yPoints2[0] = H/2;
		xPoints2[1] = (int)(W-11-d2);    yPoints2[1] = H/2+7;
		xPoints2[2] = (int)(W-11-3*d2);  yPoints2[2] = H/2-7;
		xPoints2[3] = (int)(W-11-5*d2);  yPoints2[3] = H/2+7;
		xPoints2[4] = (int)(W-11-7*d2);  yPoints2[4] = H/2-7;
		xPoints2[5] = (int)(W-11-9*d2);  yPoints2[5] = H/2+7;
		xPoints2[6] = (int)(W-11-11*d2); yPoints2[6] = H/2-7;
		xPoints2[7] = (int)(W-11-13*d2); yPoints2[7] = H/2+7;
		xPoints2[8] = (int)(W-11-15*d2); yPoints2[8] = H/2-7;
		xPoints2[9] = px+10;		     yPoints2[9] = H/2;
	}//pixels()

	public void paint()
	{
	    dbg.setColor(Color.white);
        dbg.fillRect(0,0,W,H);
        dbg.setColor(Color.black);
        dbg.drawLine(10,H/2-30,10,H/2+30);
        dbg.drawLine(W-11,H/2-30,W-11,H/2+30);
        dbg.drawPolyline(xPoints1,yPoints1,10);
        dbg.drawPolyline(xPoints2,yPoints2,10);
        dbg.fillOval(px-10,py-10,21,21);
		print();
        g.drawImage(dbi,0,0,this);
	}//paint()

	public void print()
	{
	    dbg.setColor(Color.blue);
		dbg.setFont(new Font("Verdana",Font.BOLD,14));
	    dbg.drawString("Drag and drop mass ...",W/2-90,28);
		dbg.setFont(new Font("Verdana",Font.BOLD,10));
	    dbg.drawString("Spring force:  " + D,80,H-40);
	    dbg.drawString("Friction:  " + reib,250,H-40);
	}//print()

    public void actionPerformed(ActionEvent evt)
    {
		if(evt.getActionCommand() == but_go.getActionCommand())
        { stop = !stop; }

		if(evt.getActionCommand() == but_D_plus.getActionCommand())
        {
        	D = D*2; startwerte(); paint(); start();
        }
		if(evt.getActionCommand() == but_D_min.getActionCommand())
        {
        	D = D/2; startwerte(); paint(); start();
        }
		if(evt.getActionCommand() == but_reib_plus.getActionCommand())
        {
        	reib++; startwerte(); paint(); start();
        }
		if(evt.getActionCommand() == but_reib_min.getActionCommand())
        {
        	if(reib > 0)
        	{ reib--; startwerte(); paint(); start(); }
        }
    }//actionPerformed()

    public void start()
    {
		if(animator == null)
	    {
            animator = new Thread(this);
            animator.start();
	    }
    }//start()

    public void stop()
    {
		animator = null;
    }//stop()

class ML extends MouseAdapter implements MouseMotionListener  //overwrite mouse
{
	public void mousePressed (MouseEvent evt)
	{
		animator = null;
		stop = false;
		step = 0;
		//new amplitude
		if(evt.getX() < 40) {amp = (double)(40-W/2)/(double)(W/2-50);}
		else if(evt.getX() > W-40) {amp = (double)(W-40-W/2)/(double)(W/2-50);}
		else {amp = (double)(evt.getX()-W/2)/(double)(W/2-50);}
		x = amp; //to calculate pixelcoordinates
		pixels(); //pixelcoordinates
		paint();
	}//mousePressed

	public void mouseDragged (MouseEvent evt)
	{
		if(evt.getX() < 40) {amp = (double)(40-W/2)/(double)(W/2-50);}
		else if(evt.getX() > W-40) {amp = (double)(W-40-W/2)/(double)(W/2-50);}
		else {amp = (double)(evt.getX()-W/2)/(double)(W/2-50);}
        x = amp; //to calculate pixelcoordinates
        pixels(); //pixelcoordinates
        paint();
	}//mouseDragged

	public void mouseReleased (MouseEvent evt)
	{
		start();
	}//mouseReleased

	public void mouseMoved (MouseEvent evt)
	{  }//mouseMoved (simply implemented for Interface MouseMotionListener)

}//ML
}