//---------------------------------------------------------//
//
//   Granule       Ishihama Yoshiaki    All right reserved
//                 ishmnn@cap.bekkoame.ne.jp
//
//---------------------------------------------------------//
import java.awt.*;
import java.applet.*;
import java.io.*;
import java.lang.String;
import java.util.Random;
import java.util.Date;

public class Granules extends Applet implements Runnable 
{
	Thread     runner;
	int        NUM, WIDTH,HEIGHT,i,j,myPhase=0,st=0;
    Graphics   g2;
    CGranule   granule[];
    Random     nrand;
 
	
	public void init() 
	{
	 	Date d=new Date();
		nrand=new Random(d.getTime());
	 	 
		WIDTH=this.size().width;
		HEIGHT=this.size().height;
		String s;
		
     
		g2=this.getGraphics();
		g2.setColor(Color.black);
        g2.fillRect(0,0,WIDTH,HEIGHT);
		
		
		s=getParameter("NUM");
		if (s==null)NUM=50;
		else NUM = Integer.parseInt(s);
	
		setBackground(Color.black);
		setForeground(Color.white);
		 
     	granule=new CGranule[NUM];
     	for(i=0;i < NUM;i++){
     	   granule[i]=new CGranule();
     	   granule[i].DoInit(WIDTH,HEIGHT,nrand);
     	}
	}
	
	public void start()
	{
		if (runner == null)
		{
			runner= new Thread(this);
			runner.start();
		}
	}
	
	public void stop()
	{
		if (runner!=null)
		{
			runner.stop();
			runner=null;
		}
	}
	

	public void run()
	{
		
		while (true) {
			try { Thread.sleep(10); } 
			 catch (InterruptedException e){};
			if ( isVisible()  ) {
		 	
	            DomyRun();
			    offpaint();
			}
		}
		  
	}
	
	
	public void  DomyRun()//----------------------------------//
	{
	
	 switch(myPhase){
       
        case 0: //------------- Normal State -----------//
          
         st ++;    if(st > 400) {   st=0;  myPhase=10; }
      
       for(i=0;i < NUM;i++) {
         int       total=0;
         double[]  LOC,loc;
         int[]     myCellP,cellp;
       
           LOC=new double[2];
           loc=new double[2];
           myCellP=new int[2];
           cellp  =new int[2];
           
           LOC[0]=LOC[1]=0.0; 
           granule[i].GetCellPoint(myCellP);
          
           for(j=0;j < NUM;j++){
          
              if(j==i) continue;
			  granule[j].GetCellPoint(cellp);
             
             if(java.lang.Math.abs(myCellP[0] - cellp[0]) < 2 && 
                java.lang.Math.abs(myCellP[1] - cellp[1]) < 2){
                  granule[j].DoGetLoc(loc);
                  LOC[0] +=loc[0];
                  LOC[1] +=loc[1];
                  total ++;
              }
           }
           if(total!=0){
             double f1;
             f1= total;
             loc[0]=LOC[0]/f1;
             loc[1]=LOC[1]/f1;
           } else {
             loc[0]=loc[1]=0.0;
            }
           
            granule[i].SetVelocity(loc);
            granule[i].Course(nrand);
            granule[i].Mechanics();
          
         }
        
            break;
       
        case 10: //--------- Explosion ------------//
            for(i=0;i < NUM;i++)   granule[i].SetPhase(100);
            myPhase=11;
            break;
        
        case 11:
      
           for(i=0;i < NUM;i++){
             granule[i].Course(nrand);
             granule[i].Mechanics();
           }
           st ++; 
          if(st > 170){ 
             st=0;   
             myPhase=0;
             for(i=0;i < NUM;i++)  granule[i].SetPhase(110);
           }
         
           break;
        }
	
	
	}//-----------------------------------------------------//
	
	public void offpaint ()
	{
	 int     k;
	 int[]   nn;
	 
	     nn=new int[2];

		for (k=0;k < NUM;k++){
		
		      g2.setColor(Color.black);
		      granule[k].DoGetOldLocInt(nn);
		      g2.drawLine(nn[0],nn[1],nn[0], nn[1]);
		      
		      g2.setColor(Color.white);
		      granule[k].DoGetLocInt(nn);
	 	      g2.drawLine(nn[0],nn[1],nn[0], nn[1]);
		}
	}

	public void paint(Graphics g2)
	{
		//g2.drawImage(offscreen, 0, 0,Color.black,this);
	}
	


}

//------------------------------ CGranule -----------------------//

public class CGranule 
{
 int        phase,st,count,sx,sy;
 double[]   loc, vct, deltaVCT,oldDelta;
 double     maxx,minx,maxy,miny,mycenterX,mycenterY;
 int[]     cellP,oldLoc;
	 
	 
	public int rand(Random nrand,int range)
	{
		return( java.lang.Math.abs(nrand.nextInt()) % range);
	}

 
 public void DoInit(int Width,int Height,Random nrand)
 {
  double    f1;
    
  
        loc=new double[2];
        vct=new double[2];
        deltaVCT=new double[2];
        oldDelta=new double[2];
 
       st=0;
       phase=10;
       deltaVCT[0]=0;
       deltaVCT[1]=0;
  
       sx=Width;
       sy=Height;
  
       loc[0]=rand(nrand,sx) - sx/2;
       loc[1]=rand(nrand,sy) - sy/2;
  
   
       f1=rand(nrand,360)-180;
       f1 *=0.01745;
   
       vct[0]=java.lang.Math.cos(f1);  
       vct[1]=java.lang.Math.sin(f1);  
  
       maxx=sx/2;  minx=-maxx;
       maxy=sy/2;  miny=-maxy;
  
       mycenterX=maxx;
       mycenterY=maxy;
   
       cellP=new int[2];
       oldLoc=new int[2];
       oldLoc[0]=oldLoc[1]=0;
  
       SetCellPoint();
 }


public void	 Mechanics()
{
 	vct[0]		    += deltaVCT[0];
	vct[1]	    	+= deltaVCT[1];
	loc[0]		    +=vct[0];
	loc[1]		    +=vct[1];
}

public void   DoGetLoc(double  dst[])
{
  	  dst[0] = loc[0];
	  dst[1] = loc[1];
 	
}

public void   DoGetLocInt(int  dst[])
{
  	  dst[0] =(int)( loc[0] + mycenterX);
	  dst[1] =(int)( loc[1] + mycenterY);
 	
 	  oldLoc[0]=dst[0];
 	  oldLoc[1]=dst[1];
}

public void   DoGetOldLocInt(int  dst[])
{
  	  dst[0] =oldLoc[0];
	  dst[1] =oldLoc[1];
 	
}


public void Course(Random nrand)
{
 
  switch(phase) {
  
       case 10: //-------- Normal ----------//

         if(CheckClosure()) return;  
         SetCellPoint();
         break;
       
       
       case 100: //-------- Explosion -------//
        {double  f1;
           f1=rand(nrand,360)-180;
           f1 *=0.01745;
           
           deltaVCT[0]=java.lang.Math.cos(f1);  
           deltaVCT[1]=java.lang.Math.sin(f1);  
         
           vct[0]=0;
           vct[1]=0;
           phase=101;st=0;
         }
          break;
          
       
       case 101:
       
          st ++;
          if(st > 20){
            st=0;
            oldDelta[0]=deltaVCT[0];
            oldDelta[1]=deltaVCT[1];
            deltaVCT[0]=0;
            deltaVCT[1]=0;
            phase=102;
          }
          CheckClosure2();
          break;
          
       case 102:  
          st ++;
          if(st > 50){
            st=0;
            deltaVCT[0]=-oldDelta[0] * 0.2;
            deltaVCT[1]=-oldDelta[1] * 0.2; 
            phase=103;
          }
           CheckClosure2();
           break;
           
       case 103:
          CheckClosure2();
          break;

       case 110:
          phase=10;
          CheckClosure2();
          break;

           
      }
       
      
}
  

public boolean CheckClosure()
{
  
    if(loc[0] > maxx)  {
       if( vct[0]  > 0 ){
        vct[0]=-vct[0]; 
        deltaVCT[0]=0;
        return true;
      }
    } 
    if(loc[0]  < minx){
      if(vct[0]  < 0 ){
         vct[0]=-vct[0];
         deltaVCT[0]=0;
         return true;
      }
    } 
    if(loc[1] > maxy){
      if(vct[1]  > 0 ){
         vct[1]=-vct[1];
         deltaVCT[1]=0;
         return true;
      }
    } 
    if(loc[1] < miny){
      if(vct[1]  < 0 ){
         vct[1]=-vct[1];
         deltaVCT[1]=0;
         return true;
      }
    } 
    
  return false;
  
   
}

public void  CheckClosure2()
{
   if(loc[0] > maxx)  {
       if( vct[0]  > 0 ){
        vct[0]=-vct[0]; 
        deltaVCT[0]=-deltaVCT[0];
      }
    } 
    if(loc[0]  < minx){
      if(vct[0]  < 0 ){
         vct[0]=-vct[0];
         deltaVCT[0]=-deltaVCT[0];
      }
    } 
    if(loc[1] > maxy){
      if(vct[1]  > 0 ){
         vct[1]=-vct[1];
         deltaVCT[1]=-deltaVCT[1];
       }
    } 
    if(loc[1] < miny){
      if(vct[1]  < 0 ){
         vct[1]=-vct[1];
         deltaVCT[1]=-deltaVCT[1];
       }
    } 
 
}

public void   SetCellPoint()
{
  int  s1,s2,s3,s4;
   
   s1= (int)loc[0] + sx/2;
   s2= (int)loc[1] + sy/2;
   
   s3=sx/10;
   s4=sy/10;
   
   cellP[0]=s1/s3;
   cellP[1]=s2/s4;
   
}

public void  GetCellPoint(int dst[])
{
   dst[0]=cellP[0];
   dst[1]=cellP[1];
    
}

public void  GetVelocity(double dst[])
{
   dst[0]= vct[0];
   dst[1]= vct[1];
}

public void  SetVelocity(double  p0[])
{
  double      px,py,  fp1;
    
    px=p0[0] - loc[0];
    py=p0[1] - loc[1];
  
    fp1= myLength2d(px,py);
    
    fp1= fp1/5.0;
    if(fp1!=0.0) {
      px /=fp1;
      py /=fp1;
    } else{
      px=py=0.0;
    }
  
    px -= vct[0];
    py -= vct[1];
   
    deltaVCT[0]=px/10.0;
    deltaVCT[1]=py/10.0;
 

}


public  double  myLength2d(double px,double py)
 {
  double  f;
       
      f=px*px + py*py;
      f=java.lang.Math.sqrt(f);  
 
      return f;
 }

 public int GetPhase()
 {
    return phase;
 }

 public void  SetPhase(int s)
 {
    phase=s;
  }


}