/*
Java Graphics Demos: 
by Orion Lawlor, 12/1998
olawlor@acm.org

This code is totally free, and
should be considered in the public domain.
Use and modify it at will.
*/
import java.awt.*;
import java.awt.image.*;
//import ColorMap;
//import ByteTable;

class Demo 
{
	int w,h;
//Utilities
	void blur(byte pix[])
	{	//Blur the given image, doing nothing to boundary
	/*	for (int y=1;y<h-1;y++)
		{
			int l=y*w;
			for (int x=1;x<w-1;x++)*/
		
		//Blur all red pixels
		for (int y=1;y<h-1;y++)
		{
			int l=y*w;
			for (int x=1+(y%2);x<w-1;x+=2)
			{
				int x1=l+x-w;
				int x2=l+x;
				int x3=l+x+w;
				int sum=
					(0xff&(int)pix[x1-1])+  (0xff&(int)pix[x1])+(0xff&(int)pix[x1+1])+
					(0xff&(int)pix[x2-1])+                      (0xff&(int)pix[x2+1])+
					(0xff&(int)pix[x3-1])+  (0xff&(int)pix[x3])+(0xff&(int)pix[x3+1]);
				pix[x2]=(byte)((sum+3)>>>3);//Note: a>>>3 == a/8
			}
		}
		//Blur all black pixels
		for (int y=1;y<h-1;y++)
		{
			int l=y*w;
			for (int x=1+((y+1)%2);x<w-1;x+=2)
			{
				int x1=l+x-w;
				int x2=l+x;
				int x3=l+x+w;
				int sum=
					(0xff&(int)pix[x1-1])+  (0xff&(int)pix[x1])+(0xff&(int)pix[x1+1])+
					(0xff&(int)pix[x2-1])+                      (0xff&(int)pix[x2+1])+
					(0xff&(int)pix[x3-1])+  (0xff&(int)pix[x3])+(0xff&(int)pix[x3+1]);
				pix[x2]=(byte)((sum+3)>>>3);//Note: a>>>3 == a/8
			}
		}
	}

	byte phase[];//Fluid phase (0-- solid; 255-- liquid) field
	byte temperature[];//Fluid temperature (0-- very cold; 255-- very hot) field
	byte outImage[];//Output image pixels
	static final int freeze_pt=120;//Fluid freezes at this temperature
	static final float heat_of_fusion=900/255.0f;//This much temperature released during freezing process.
	static final float heat_of_fusion_i=1.0f/heat_of_fusion;//This much phase change corresponds to one temperature
	//These constants indicate the direction the pixel was "colonized" from--
	// they give the local crystal orientation
	static final byte COL_TABLE[/* <V>*2+<H> */]= 
		{ (byte)240,//<- something is very wrong-- pixel is *not* colonized!
		  (byte)253,//Colonized in horizontal direction only
		  (byte)254,//Colonized in vertical direction only
		  (byte)255,//Colonized from both horiz. and vert. directions
		};
	public void init(ColorMap cm,byte pix[],int Nw,int Nh)
	{
		int x,y;
		w=Nw;h=Nh;
		int dex[]  ={0, 80,110,120,125,250,253,254,255},
			red[]  ={0,200,255,255,255,255,  0, 50,150},
			green[]={0,  0,100,255,255,255,  0, 50,150},
			blue[] ={0,  0, 50,  0,255,255,150,250,150};
		cm.addBreaks(dex,red,green,blue);
		phase=new byte[w*h];
		temperature=new byte[w*h];
		outImage=pix;
		for (y=0;y<h;y++)
		{
			int l=y*w;
			for (x=0;x<w;x++) {
				phase[l+x]=(byte)255;//Initially, all is liquid
				temperature[l+x]=(byte)((freeze_pt*1.0)*Math.random());
				outImage[l+x]=(byte)0;
			}
		}
		//Make a single solid (crystal seed) pixel
		x=w/2;y=h/2;
		phase[y*w+x]=0;
		outImage[y*w+x]=(byte)255;
		//Spread the temerature a few times
		for (int i=0;i<3;i++)
			blur(temperature);
	}
	public boolean handleAction(Event ev,Object obj) {
		return false;
	}
	//Determines where this pixel is "colonized" from, and sets the
	// output array accordingly
	//Applies physical freeze modeling to given pixel index
	public void physics(int dex)
	{
		int t=0xff&(int)temperature[dex];
		int p=0xff&(int)phase[dex];
		if (t<freeze_pt && p!=0)
		{//This pixel might be freezing-- check its neighbors
			boolean horiz=(phase[dex-1]==(byte)0) || (phase[dex+1]==(byte)0);
			boolean vert=(phase[dex-w]==(byte)0) || (phase[dex+w]==(byte)0);
			if (horiz||vert)
			{//This fluid can be frozen a bit-- it's near a frozen pixel
				if ((0xff&(int)outImage[dex])<240)
					outImage[dex]=COL_TABLE[(vert?2:0)+(horiz?1:0)];
				int dt=5*(freeze_pt-t);//Surrounding coldness we'll use up
				int dp=(int)(dt*heat_of_fusion_i);//Phase change
				if (dp>p) dp=p;//<- keeps phase positive
				phase[dex]=(byte)(p-dp);
				//Deposit heat of fusion on neighbors
				byte delT=(byte)(dp*heat_of_fusion*(1.0/5.0));
				temperature[dex  ]+=delT;//<- don't need to convert to unsigned bytes here
				temperature[dex+1]+=delT;
				temperature[dex-1]+=delT;
				temperature[dex+w]+=delT;
				temperature[dex-w]+=delT;
			}
		}
	}
	int frameCount=0;
	public void fillBuffer(ColorMap cm,byte pix[])
	{
		//Physics pass(es)--goes in each direction to avoid bias
		int left=2,right=2,top=2,bot=2;
		int fcm=(frameCount++)%2;
		if (fcm==0)
			for (int y=top;y<h-bot;y++)
			  {int l=y*w; for (int x=left;x<w-right;x++) physics(l+x);}
		else if (fcm==1)
			for (int y=h-bot-1;y>=top;y--)
			  {int l=y*w; for (int x=w-right-1;x>=left;x--) physics(l+x);}
		
		//Propagate new temperatures
		blur(temperature);
		
		//Convert temperature for output
		for (int y=0;y<h;y++)
		{
			int l=y*w;
			for (int x=0;x<w;x++)
			{
				int dex=l+x;
				if ((0xff&(int)outImage[dex])<240)
					outImage[dex]=(byte)(temperature[dex]);
			}
		}
	}
}
