import java.awt.*;
import java.util.*;
import java.awt.event.*;
import java.applet.Applet;

public class springs extends java.applet.Applet 
{
    DrawPanel panel;
    Controls control;
	Label staten;
	Label status;

    public void init() {		
		GridBagLayout gridbag = new GridBagLayout();
		GridBagConstraints c = new GridBagConstraints();
		setFont(new Font("Helvetica", Font.PLAIN, 14));
		setLayout(gridbag);
		c.fill = GridBagConstraints.BOTH;
		c.insets=new Insets (5,5,5,5);
        c.weightx = 1.0;
        c.weighty = 1.0;
 		c.gridheight = 1;
 		c.gridwidth = GridBagConstraints.RELATIVE;	   	 
		staten=new Label ("E=N/A               ");
        panel = new DrawPanel(staten);
		status=new Label ("Draw object in the right panel.         ");
        c.weightx = 2.0;
		c.anchor =GridBagConstraints.NORTHEAST;
 		c.gridwidth = GridBagConstraints.REMAINDER ;	   	 
		gridbag.setConstraints(panel, c);
		add(panel);
		control = new Controls(panel,status);
		c.weighty = 0.0;
		c.gridwidth =1;
 		c.fill =  GridBagConstraints.VERTICAL ;	 
        c.weightx = 0.0;
		gridbag.setConstraints(staten, c);
		add(staten);
		gridbag.setConstraints(status, c);
		add(status);
// 		c.gridwidth = GridBagConstraints.REMAINDER ;	   	 
		gridbag.setConstraints(control, c);
		add(control);
    }

    public void destroy() {
        remove(panel);
        remove(control);
    }

    public static void main(String args[]) {
        Frame f = new Frame("proj1");
        springs drawTest = new springs();
        drawTest.init();
        f.add("Center", drawTest);
        f.setSize(700, 300);
        f.show();
    }
    public String getAppletInfo() {
        return "Particles connected by springs.By Adrian Barbu.";
    }
}
class Objectxy{
	double	m,k,h,c;					// mass and spring const.,step size, viscosity coeff
    Vector T = new Vector(); //the vertices
    Vector v = new Vector(); //the speeds
	Vector D = new Vector();	// initial distances between tokens.

	public Objectxy(){
		m=1;k=0.1;h=0.1;c=0.1;};
	
	public double	Ec(){
		double sum=0;
		int i;
		point P;
		for (i=0;i<v.size();i++){
			P=(point)v.elementAt(i);
			sum+=P.x*P.x+P.y*P.y;
		}
		return m*sum/2;
	}
	public double	Ep(){
		double d1,sum=0;
		int i,j;
		point P=new point(0,0);
		Point3d q;
		for (i=0;i<D.size();i++){
			q=(Point3d)D.elementAt (i);
			if (q.z>0){
				P=((point)T.elementAt(q.x)).minus((point)T.elementAt(q.y));
				d1=Math.sqrt(P.x*P.x+P.y*P.y)-q.z;
				sum+=d1*d1;
			}
		}
		return k*sum/2;
	};

	public point getAvg(int i, Vector T1){
		int j;
		double d1;
		point P,sum;
		Point3d q;
		sum=((point)v.elementAt(i)).times(c);// point (0,0);
		P=new point (0,0);
		for (j=0;j<D.size();j++){
			q=(Point3d)D.elementAt (j);
			if (((q.x==i)||(q.y==i))&&(q.z>0)){
				if (q.x==i){
					P=((point)T1.elementAt(q.x)).minus((point)T1.elementAt(q.y));
				}
				else{
					P=((point)T1.elementAt(q.y)).minus((point)T1.elementAt(q.x));
				}
				d1=Math.sqrt(P.x*P.x+P.y*P.y);
				if (d1>0){
					sum=sum.plus(P.times((d1-q.z)/d1));
				}
			}
		}
		return sum.times(2*k/m);
	};
	public void Propagate(){
		int i;
		point p;
		Vector	T1=new Vector();			
		Vector	v1=new Vector();			
		for (i=0;i<T.size ();i++){
			p=((point)T.elementAt(i)).plus(((point)v.elementAt(i)).times (h));
			T1.addElement (p);
			p=((point)v.elementAt(i)).minus(getAvg(i,T).times (h));
			v1.addElement (p);
		}
		for (i=0;i<T.size ();i++){
			T.setElementAt (T1.elementAt (i),i);
			v.setElementAt (v1.elementAt (i),i);
		}
	};
	public void PropagateRK(){
		int i;
		point p;
		Vector	T1=new Vector();			
		Vector	T2=new Vector();			
		Vector	v1=new Vector();			

		for (i=0;i<T.size ();i++){
			p=((point)T.elementAt(i)).plus(((point)v.elementAt(i)).times (h/2));
			T2.addElement (p);
		}
		for (i=0;i<T.size ();i++){
			p=((point)T.elementAt(i)).plus(((point)v.elementAt(i)).times (h));
			p=p.minus(getAvg(i,T).times(h*h/2.));
			T1.addElement (p);
			p=((point)v.elementAt(i)).minus(getAvg(i,T2).times(h));
			v1.addElement (p);
		}
		for (i=0;i<T.size();i++){
			T.setElementAt (T1.elementAt (i),i);
			v.setElementAt (v1.elementAt (i),i);
		}
	};
	
};
class point{
	double x;
	double y; 
	public point(){x=0;y=0;};
	public point(double x1,double y1){x=x1;y=y1;};
	public point set(point p){
		if(this!=p){
			x=p.x;
			y=p.y;
		}
		return this;
	};
	public point plus(point p){
		point q=new point (0,0);
		q.x=x+p.x;q.y=y+p.y;
		return q;
	};
	public point minus( point p){
		point q=new point (0,0);
		q.x=x-p.x;q.y=y-p.y;
		return q;
	};
	public point times(double d){
		point q=new point (0,0);
		q.x=x*d;q.y=y*d;
		return q;
	};
	public point over(double d){
		point q=new point (0,0);
		q.x=x/d;q.y=y/d;
		return q;
	};
};
class Point3d extends Point {
	double z;
	Point3d(Point3d p){
		super(p.x,p.y);
		this.z =p.z;
	}
	Point3d(Point p,int z){
		super(p);
		this.z =z;
	}
	Point3d(int x,int y,double z){
		super(x,y);
		this.z =z;
	}
	public void setLocation(int x, int y, double z){
		this.setLocation (x,y);
		this.z=z;
	}
	public void setLocation(Point3d p){
		int x=p.x,y=p.y; double z=p.z;
		this.x=x;
		this.y=y;
		this.z=z;
	}
}
class DrawPanel extends Panel implements MouseListener, MouseMotionListener, Runnable{
    int midx;
	int speed;
	int mode =3;	//0=move,1=delete,2=symulate,3=construct
	point pi,pf;
	Objectxy O;
	Thread t;
	Label len;
	
    public DrawPanel(Label staten) {
        setBackground(Color.white);
        addMouseMotionListener(this);
        addMouseListener(this);
		pi=new point (-1,-1);
		pf=pi;
		len=staten;
		O=new Objectxy();
    }
	public void run(){
		int i;
		double en;
		String s=new String("");
		O.v.setElementAt(new point (2,0),0);
		while (true){
			if (t==Thread.currentThread())
			try {
			    t.sleep(30);
			} catch (InterruptedException e) {}
			for (i=0;i<10;i++){
				if (midx>-1){
					O.T.setElementAt(pf,midx);
					O.v.setElementAt (new point(0,0),midx);
				}
				O.PropagateRK();
				en=O.Ec()+O.Ep();
				len.setText("E="+s.valueOf(en));
			}
			repaint();
		}
	}
	public point findclose(Point p0){
	//returns a point from the Vertex list if it's
	//close enough to p0, else returns p0
		boolean found=false;
		int np=O.T.size ();
		int i,j;j=-1;
		for (i=0;i<np;i++){
			point p= (point)O.T.elementAt (i);
			if ((p0.x-p.x)*(p0.x-p.x)+(p0.y-p.y)*(p0.y-p.y)<100){ 
				found=true;
				j=i;
				i=np;
			}
		}
		if (j<0) {return new point (p0.x,p0.y);}
		else {return (point)O.T.elementAt (j);}
	}
	public boolean colinear (point a,point b,point c){
		double r,rx,ry;
		r=a.y*(c.x-b.x)-a.x*(c.y-b.y)-b.y*c.x+c.y*b.x;
		rx=(b.x-a.x)*(b.x-c.x);	
		ry=(b.y-a.y)*(b.y-c.y);
		if ((r>-400)&&(r<400)&&(rx<10)&&(ry<10)) {return true;}
		else return false; 
	}
	public void deletepoint(point p){
	//remove all occurences of p
		int i,j,k;
		Point3d q;
		j=O.T.indexOf (p);
		if (O.T.removeElement(p)){
			O.v.removeElementAt(j);
			for (i=0;i<O.D.size ();){
				q=(Point3d)O.D.elementAt (i);
				if ((j==q.x)||(j==q.y)){
					O.D.removeElementAt(i);
				}
				else i++;
			}
			for(i=0;i<O.D.size ();i++){
				q=(Point3d)O.D.elementAt (i);
				if (q.x>j)	q.x--;
				if (q.y>j)	q.y--;
			}
		}
		else{
			for (i=0;i<O.D.size ();i++){
				q=(Point3d)O.D.elementAt (i);
				if (colinear((point)O.T.elementAt (q.x),p,(point)O.T.elementAt (q.y))){
					O.D.removeElementAt(i);
					break;
				}
			}			
		}
	}	
    public void setDrawMode(int mode) {
    }
    public void mousePressed(MouseEvent e) {
        e.consume();
		pi=findclose(e.getPoint ());
		switch(mode){
		case 0:	//delete
			deletepoint(pi);
			break;
		case 1:	//move
		case 2:	//symulate
			midx=O.T.indexOf(pi);
			pf=pi;
			break;	
		default://construct
			if (O.T.indexOf (pi)==-1){
				 O.T.addElement (pi);
				 O.v.addElement (new point(0,0));
			}
			pf=pi;
		}
		repaint();
    }
    public void mouseDragged(MouseEvent e) {
		Point p1;
        e.consume();
		switch(mode){
		case 0:	//delete
			break;
		case 1:	//move
		case 2:	//symulate
			p1=e.getPoint ();
			pf=new point (p1.x,p1.y);
			if (midx>-1) 
				O.T.setElementAt (pf,midx);
			break;
		default://construct
			pf=findclose(e.getPoint ());
		}
        repaint();	
    }
    public void mouseReleased(MouseEvent e) {
		Point p1;
		point q;
        e.consume();
		switch(mode){
		case 0:	//delete
			break;
		case 1:	//move
			p1=e.getPoint ();
			pf=new point(p1.x,p1.y);
			if (midx>-1) O.T.setElementAt (pf,midx);
			pi=pf;
			break;
		case 2:	//symulate
			midx=-1;
			break;	
		default://construct
			pf=findclose(e.getPoint ());
			if(pi!=pf){
				if (O.T.indexOf (pf)==-1){ 
					O.T.addElement (pf);
					O.v.addElement (new point(0,0));
				}
				q=pi.minus(pf);
				O.D.addElement(new Point3d(O.T.indexOf (pi),O.T.indexOf (pf),
										   Math.sqrt(q.x*q.x+q.y*q.y)));
			}
			pi=pf;
		}
		repaint();		
    }
    public void mouseMoved(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }
    public void mouseClicked(MouseEvent e) {
    }
    public void paint(Graphics g) {
        int np = O.T.size();
        int ne = O.D.size();
        /* draw the current lines and points */
        g.setColor(Color.black);
        g.setPaintMode();
		point p,p0;
		Point3d q;
        for (int i=0; i < np; i++) {
            p = (point)O.T.elementAt(i);
			g.fillRect ((int)p.x-3,(int)p.y-3,6,6);
			p0=p;
        } 
		for (int i=0;i<ne;i++) {
			q=(Point3d)O.D.elementAt(i);
			p0=(point)O.T.elementAt (q.x);
			p=(point)O.T.elementAt(q.y);
		    g.drawLine((int)p0.x,(int) p0.y, (int)p.x,(int) p.y);
		}
		if (mode>2){
		    g.drawLine((int)pi.x,(int) pi.y, (int)pf.x,(int) pf.y);
		}
    }
}
class Controls extends Panel implements ActionListener {
    DrawPanel target;
	Button bClear,bDelete,bSym,bConstr,bMove;
	Label status;
    public Controls(DrawPanel target, Label status) {
        this.target = target;
		this .status =status;
        setLayout(new GridLayout(1,5));
		setBackground(Color.lightGray);
		target.setForeground(Color.black );		bClear = new Button("Clear");		add(bClear);
		bClear.addActionListener (this);
		bClear.setForeground(Color.black);
		bDelete = new Button("Delete");		add(bDelete);
		bDelete.addActionListener (this);
		bDelete.setForeground(Color.black);
		bMove = new Button("Move");		add(bMove);
		bMove.addActionListener (this);
		bMove.setForeground(Color.black);
		bSym = new Button("Simulate");		add(bSym);
		bSym.addActionListener (this);
		bSym.setForeground(Color.black);
		bConstr = new Button("Construct");		add(bConstr);
		bConstr.addActionListener (this);
		bConstr.setForeground(Color.black);
    }

    public void paint(Graphics g) {
        Rectangle r = getBounds();
		g.setColor(Color.lightGray);
        g.draw3DRect(0, 0, r.width-1, r.height-1, true);
    }
public void actionPerformed(ActionEvent e) {
	Object src = e.getSource();
	if (src == bClear) {
		target.O.T.removeAllElements();
		target.O.v.removeAllElements();
		target.O.D.removeAllElements();
		target.pi=new point(-1,-1);		target.pf=target.pi;				target.mode=3;		
		bMove.setLabel("Move");
		bDelete.setLabel("Delete");
		status.setText ("Draw object in the right panel.  ");
		target.repaint();	}  
	if (src == bDelete) {		if (target.mode!=0){
			target.mode=0;
//			bDelete.setLabel("Delete");
			status.setText ("Delete elements from the object.");
		}
	}  
	if (src == bMove) {		if (target.mode!=1){
			target.mode=1;
			status.setText ("Move vertices and edges.  ");
		}
	}  
	if (src == bSym) {
		target.midx=-1;
		target.mode=2;//		if (!target.t.isAlive ()){
		target.t=new Thread(target);		target.t.start() ;//		}
	}  
	if (src == bConstr) {
		target.mode=3;		if (target.t.isAlive ()){target.t.stop() ;}
	}  
  }
}




