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

public class Squishy extends Applet
{
   static int size = 12;
   
   public void start()
     {
	Random random = new Random();
	
	array = new SquishyPoint[size+1][size+1];
	int x, y;
	for(x=0;x<=size;x++)
	  for(y=0;y<=size;y++)
	    {
	       array[x][y] = new SquishyPoint();
	       array[x][y].x = (float)(x)/(float)(size);
	       array[x][y].y = (float)(y)/(float)(size);
	       array[x][y].fixed = false;
	       array[x][y].color = Color.getHSBColor(random.nextFloat(), (float)0.3, (float)0.5);
	    }

	for(x=0;x<=size;x++)
	  for(y=0;y<=size;y++)
	    {
	       if(x==0) array[x][y].fixed = true;
	       else     array[x][y].left = array[x-1][y];
	       if(y==0) array[x][y].fixed = true;
	       else     array[x][y].up = array[x][y-1];
	       if(x==size) array[x][y].fixed = true;
	       else        array[x][y].right = array[x+1][y];
	       if(y==size) array[x][y].fixed = true;
	       else        array[x][y].down = array[x][y+1];
	    }

	enableEvents(MouseEvent.MOUSE_ENTERED|MouseEvent.MOUSE_MOVED|MouseEvent.MOUSE_CLICKED);
     }
   
   public void update(Graphics g)
     {
	paint(g);
     }
   
   public void paint(Graphics g)
     {
	if(head!=null)
	  repaint();
	
	int x, y;
	int w = getSize().width-1, h = getSize().height-1;
	for(x=0;x<size;x++)
	  for(y=0;y<size;y++)
	    {
	       dx[0] = (int)(array[x][y].x*w);     dy[0] = (int)(array[x][y].y*h);	       
	       dx[1] = (int)(array[x+1][y].x*w);   dy[1] = (int)(array[x+1][y].y*h);	       
	       dx[2] = (int)(array[x+1][y+1].x*w); dy[2] = (int)(array[x+1][y+1].y*h);	       
	       dx[3] = (int)(array[x][y+1].x*w);   dy[3] = (int)(array[x][y+1].y*h);	       

	       if(coloured)
		 g.setColor(array[x][y].color);
	       else
		 g.setColor(Color.white);
	       g.fillPolygon(dx,dy,4);
	       g.setColor(Color.black);
	       g.drawPolygon(dx,dy,4);
	    }

	for(x=0;x<size*size;x++)
	  if(!process())
	    break;
     }
   
   public void processEvent(AWTEvent e)
     {	
	super.processEvent(e);
	try
	  {		  
	     if(e.getID()==MouseEvent.MOUSE_ENTERED)
	       {
		  mouse = ((MouseEvent)e).getPoint();	
	       }
	     else if(e.getID()==MouseEvent.MOUSE_MOVED)
	       {
		  MouseEvent m = (MouseEvent)e;
		  Point point = m.getPoint();
		  int a, b;
		  
		  float x = (float)point.x/(float)getSize().width;
		  float y = (float)point.y/(float)getSize().height;
		  float dx = (float)(point.x-mouse.x)/(float)getSize().width;
		  float dy = (float)(point.y-mouse.y)/(float)getSize().height;
		  
		  for(a=0;a<size;a++)
		    for(b=0;b<size;b++)
		      if(!array[a][b].fixed)
			{
			   float dist = (x-array[a][b].x)*(x-array[a][b].x)+
			     (y-array[a][b].y)*(y-array[a][b].y);
			   dist = (float)(0.01/(0.01+dist));
			   array[a][b].x += dx*dist/2.0;
			   array[a][b].y += dy*dist/2.0;
			   addPoint(array[a][b]);
			}
		  
		  mouse = point;
	       }
	     else if(e.getID()==MouseEvent.MOUSE_CLICKED)
	       {
		  coloured = !coloured;
	       }
	  }
	catch(Throwable t)
	  {
	  }
     }
   
   private boolean process()
     {
	SquishyPoint p = getPoint();
	if(p==null) return false;

	float x1 = (p.left.x+p.right.x+p.down.x+p.up.x)/(float)4.0;
	float y1 = (p.left.y+p.right.y+p.down.y+p.up.y)/(float)4.0;
	float x2 = (p.left.up.x+p.right.up.x+p.left.down.x+p.right.down.x)/(float)4.0;
	float y2 = (p.left.up.y+p.right.up.y+p.left.down.y+p.right.down.y)/(float)4.0;	
	
	float ax = (float)(x1*0.7+x2*0.3-p.x)*(float)5.0;
	float ay = (float)(y1*0.7+y2*0.3-p.y)*(float)5.0;
	
	p.x += p.vx*0.05;
	p.y += p.vy*0.05;
	p.vx = (float)((p.vx+ax*0.05)*0.97);
	p.vy = (float)((p.vy+ay*0.05)*0.97);

	if((p.vx*p.vx+p.vy*p.vy)>0.00001)
	  {
	     addPoint(p.up);
	     addPoint(p.left);
	     addPoint(p.down);
	     addPoint(p.right);
	     addPoint(p.left.up);
	     addPoint(p.right.up);
	     addPoint(p.left.down);
	     addPoint(p.right.down);
	  }
	if((ax*ax+ay*ay)>0.00001)
	  addPoint(p);
	
	return true;
     }
   
   private SquishyPoint getPoint()
     {
	if(head==null) return null;
	
	SquishyPoint p = head;	
	head = p.next;
	if(tail==p) tail = p.next;
	p.next = null;
	return p;
     }
   
   private void addPoint(SquishyPoint p)
     {
	if(p==null || p.fixed || p.next!=null) return;
	if(head==null)
	  {
	     head = p;
	     repaint();
	  }
	if(tail==null) tail = p;
	else tail = tail.next = p;
     }
   
   private int[] dx = new int[4];
   private int[] dy = new int[4];
   private boolean coloured = true;
   private Point mouse;
   private SquishyPoint head;
   private SquishyPoint tail;
   private SquishyPoint[][] array;   
}
