//
//	Java Flies - 	http://www.vulliamy.demon.co.uk/alex.html
//					alex@vulliamy.demon.co.uk
//
//	You are welcome to do whatever you wish with this code, as long
//	as Jeff Cragg and I (Alex Vulliamy) are credited.
//	updated 12th February 98 to speed it up
//  updated 3rd March 98 to make it less processor-greedy
//  updated 10th March to speed up a little bit by not drawing background in first
//  updated 5th April 98 to add controls
// updated 19th April 98 to stop bugs due to browser's ill-written java VMs

import java.awt.*;
import java.applet.*;
import java.io.*;
import java.lang.String;
import java.util.Random;
import java.util.Date;



public class flies6 extends Applet implements Runnable 
{
	Image off_g;
	int delay=20;
	int delaymax=80;
	int delaymin=5;
	Label l1,l2;
	Thread runner;
	Random nrand;
	int NF;
	int REVDIST;
	int ACC;
	int ACCTOMID;	
	int MAXSPEED;
	int BOUNCESPEED;
	int[] flyx;
	int[] flyy;
	int[] flyvx;
	int[] flyvy;
	int[][] flyn;
	int[] lpx;
	int[] lpy;
	int[] lp2x;
	int[] lp2y;
	int WIDTH,HEIGHT;
	int j;
    Graphics g,g2;
	Panel flypanel=new Panel();
	Panel flycontrols=new Panel();
	BorderLayout layout=new BorderLayout();
	GridLayout layout2=new GridLayout(2,2);
	Scrollbar nfscroll=new Scrollbar(Scrollbar.HORIZONTAL);
	Scrollbar speedscroll=new Scrollbar(Scrollbar.HORIZONTAL);

	public int rand(int range)
	{
		return( java.lang.Math.abs(nrand.nextInt()) % range);
	}
	
	public void init() 
	{
		Float temp;
		Date d=new Date();
		nrand=new Random(d.getTime());
		setBackground(Color.black);
		setForeground(Color.white);
		String s;
		s=getParameter("NF");
		if (s==null)
			NF=30;
		else NF = Integer.parseInt(s);
		s=getParameter("REVDIST");
		if (s==null)
			REVDIST=2000000;
		else REVDIST = Integer.parseInt(s);
		s=getParameter("ACCTOMID");
		if (s==null)
			ACCTOMID=10;
		else {
				temp=new Float(s);
				ACCTOMID = 100*temp.intValue();
			}
		s=getParameter("ACC");
		if (s==null)
			ACC=30;
		else {
				temp=new Float(s);
				ACC = 100*temp.intValue();
			}
		s=getParameter("MAXSPEED");
		if (s==null)
			MAXSPEED=500;
		else {
				temp=new Float(s);
				MAXSPEED = 100*temp.intValue();
			}
		s=getParameter("BOUNCESPEED");
		if (s==null)
			BOUNCESPEED=80;
		else {
				temp=new Float(s);
				BOUNCESPEED = 100*temp.intValue();
			}
		flyx = new int[NF*3+1];
		flyy = new int[NF*3+1];
		flyvx = new int[NF*3+1];
		flyvy = new int[NF*3+1];
		flyn = new int[NF*3+1][2];
		lpx = new int[NF*3+1];
		lpy = new int[NF*3+1];
		lp2x = new int[NF*3+1];
		lp2y = new int[NF*3+1];
		int i;
		setLayout(layout);
		add("Center",flypanel);
		add("South",flycontrols);
		speedscroll.setValues(delaymax-delay,delaymax/4,0,delaymax-delaymin);
		nfscroll.setValues(NF,3*NF/4,0,NF*3);
		flycontrols.setLayout(layout2);
		flycontrols.add(nfscroll);
		flycontrols.add(speedscroll);
		l1=new Label("Number");
		l1.setAlignment(Label.CENTER);
		l2=new Label("Speed");
		l2.setAlignment(Label.CENTER);
		flycontrols.add(l1);
		flycontrols.add(l2);
		validate();
		speedscroll.setValue(delaymax-delay);
		nfscroll.setValue(NF);
		WIDTH=this.size().width;
		HEIGHT=flypanel.size().height;
        flypanel.setBackground(Color.black);
		for(i=0;i<NF*3;i++)
		{
			lp2x[i]=lpx[i]=flyx[i]=rand(WIDTH-60)*100+3000;
			lp2y[i]=lpy[i]=flyy[i]=rand(HEIGHT-60)*100+3000;
			flyvx[i]= rand(500)-200;
			flyvy[i]= rand(500)-200;
		}
		randemize();
	}
	
	public void start()
	{
		if (runner == null)
		{
			runner= new Thread(this);
			runner.start();
		}
	}
	
	public void stop()
	{
		if (runner!=null)
		{
			runner.stop();
			runner=null;
		}
	}
	public void processfly(int tick)
	{
  // rev determines whether the neighbour attracts or repels, ACC is the acceleration amount
  // lpx & lpy are the last positions of the fly for drawing purposes.
  // REVDIST is the minimum distance squared that the flies can get to without repelling
  // tick is the fly number being processed. flyvx&y are the velocities in x and y directions
  // ACCTOMID is the acceleration towards the middle to attempt to keep the flies in the
  // middle of the display.
		int rev;
		lp2x[tick]=lpx[tick];
		lp2y[tick]=lpy[tick];
		lpx[tick]=flyx[tick];
		lpy[tick]=flyy[tick];
		rev=1;
		if (dist(tick,flyn[tick][0])<REVDIST)
			rev=-1;
		if (flyx[tick]<flyx[flyn[tick][0]])
			flyvx[tick]+=ACC*rev;
		else
			flyvx[tick]-=ACC*rev;
		if (flyy[tick]<flyy[flyn[tick][0]])
			flyvy[tick]+=ACC*rev;
		else
			flyvy[tick]-=ACC*rev;
		rev=1;
		if (dist(tick,flyn[tick][1])<REVDIST)
			rev=-1;
		if (flyx[tick]<flyx[flyn[tick][1]])
			flyvx[tick]+=ACC*rev;
		else
			flyvx[tick]-=ACC*rev;
		if (flyy[tick]<flyy[flyn[tick][1]])
			flyvy[tick]+=ACC*rev;
		else
			flyvy[tick]-=ACC*rev;
		if (flyvx[tick]>MAXSPEED) flyvx[tick]=MAXSPEED;
		if (flyvx[tick]<-MAXSPEED) flyvx[tick]=-MAXSPEED;
		if (flyvy[tick]>MAXSPEED) flyvy[tick]=MAXSPEED;
		if (flyvy[tick]<-MAXSPEED) flyvy[tick]=-MAXSPEED;
		if (flyx[tick]<0) flyvx[tick]=BOUNCESPEED;
		if (flyx[tick]>WIDTH*100) flyvx[tick]=-BOUNCESPEED;
		if (flyy[tick]<0) flyvy[tick]=BOUNCESPEED;
		if (flyy[tick]>HEIGHT*100) flyvy[tick]=-BOUNCESPEED;		
		if (flyx[tick]<WIDTH*50) flyvx[tick]+=ACCTOMID;
		if (flyx[tick]>WIDTH*50) flyvx[tick]-=ACCTOMID;
		if (flyy[tick]<HEIGHT*50) flyvy[tick]+=ACCTOMID;
		if (flyy[tick]>HEIGHT*50) flyvy[tick]-=ACCTOMID;		
		flyx[tick]+=flyvx[tick];
		flyy[tick]+=flyvy[tick];
		
	}
	
	public void doneighbours(int tick)
	{
	// this re-assigns neighbours if the neighbour's neighbours are closer to the fly than the
 // neighbours.
		int k,l,m;
		for (k=0;k<2;k++)
		{	
			m=flyn[tick][k];
			for (l=0;l<2;l++)
			{
				if (dist(flyn[m][l],tick)<dist(flyn[tick][0],tick))
				{
					if (flyn[m][l]!=flyn[tick][1])
						flyn[tick][0]=flyn[m][l];
				}
				if (dist(flyn[m][l],tick)<dist(flyn[tick][1],tick))
				{
					if (flyn[m][l]!=flyn[tick][0])
						flyn[tick][1]=flyn[m][l];
				}
			}
		}	
		for (k=0;k<2;k++)
		{	
  // this bit randomises the flies neighbour if it has been assigned itself as a neighbour
				if (flyn[tick][k]==tick) flyn[tick][k]=(int) (rand(NF));
		}
	}
	
// distance between flies function
	public int dist(int tick1,int tick2)
	{
		int d,d1,d2;
		d1=((int)(flyx[tick1]-flyx[tick2]));
		d2=((int)(flyy[tick1]-flyy[tick2]));
		d=d1*d1+d2*d2;
		return(d);
	}
	
// randomises the neighbours
	public void randemize()
	{
		int k;
		for (k=0;k<NF;k++)
		{
			flyn[k][0]=(int) (rand(NF));
			flyn[k][1]=(int) (rand(NF));
		}
	}
	public void run()
	{
		
		while (Thread.currentThread() ==runner)
		{
			for (int i=0;i<NF;i++)
			{
				doneighbours(i);
				processfly(i);
			}
			if (rand(20)==0) randemize();
			g2=flypanel.getGraphics();
			mypaint(g2);
		 	try{
		 		Thread.sleep(delay);
		 		}
		 	catch (InterruptedException e)
		 	{}
		}
	}
	
	public boolean handleEvent(Event evt)
	{
		switch (evt.id)
		{
			case Event.SCROLL_ABSOLUTE:
			case Event.SCROLL_LINE_DOWN:
			case Event.SCROLL_LINE_UP:
			case Event.SCROLL_PAGE_DOWN:
			case Event.SCROLL_PAGE_UP:
				if (evt.target==nfscroll)
				{
					NF=nfscroll.getValue();
				}
				else if (evt.target==speedscroll) 
				{
					delay=delaymax-speedscroll.getValue();
				}
				flypanel.repaint();
				return(true);
		}
		return(super.handleEvent(evt));
	}	
	
	public void mypaint (Graphics g2)
	{
		int k;
		g2.setColor(Color.black);
		g2.fillRect(0,0,WIDTH,HEIGHT);
		g2.setColor(Color.white);
		for (k=0;k<NF;k++)
		{
			g2.drawLine((int) lpx[k]/100, (int) lpy[k]/100,(int) flyx[k]/100,(int) flyy[k]/100);
		}
	}
}
