/*	An Artifical Life Simulator!	Written by Tom Atkinson 	funk.co.nz*/import java.awt.*;import java.applet.Applet;import java.util.*;import StatsThread;////////////////////////////////////////////////////////////////////public class Amoeba extends Applet { // implements Runnable////////////////////////////////////////////////////////////////////	// Program Variables:	static int width, height, cycles, toxic, dx, dy, herbGestation,		clock, magni, digest, metabolism, childHealth, divisionTax,		frameSkip, aPop, hPop, aWeight, hWeight, aHealth, hHealth;	// GUI Variable:	static int slideHeight, lifePlaneOffset, lifePlaneOffsetLeft,		numSliders, screenHeight, screenWidth, sleepAmount, sleepCount, offset3D, shear;	static boolean verbose = false;	static boolean fertile = false;	static boolean running = false;	static boolean needsBlack = true;	static boolean needsArrayChange = false;	static boolean isBooting = true;	static boolean isFinished = false;	static boolean is3DMode = false;	static boolean hasExploded = false;	static int [][] cell;	static int[] seasonLimits = { 40, 41 }; // Point at which fertile reverses.		// Temporary:	static String sentence, temp, status; // status is the browser status bar.	static int tmp;		static long ctm; //  System.currentTimeMillis()	static long drawTime; // Time for drawLifePlane in Millis.		static long growTime; // Time for growLife in Millis.		///////////////// GUI DECLARATIONS		Panel p = new Panel();	static int panelWidth;		Color col = new Color( 0, 0, 0);		GridBagLayout  gridbag  =  new  GridBagLayout();	GridBagConstraints  c  =  new  GridBagConstraints();		Button pauseBut = new Button("Pause");	Button redrawBut = new Button("Force Redraw");	// Button sizeBut = new Button("Change Size");	// pauseBut.setLabel( "PressMe!" );	Checkbox cbVerbose = new Checkbox("Verbose");	Checkbox cbFertile = new Checkbox("Fertile");	Checkbox cb3DMode = new Checkbox("3D Rendering");		Scrollbar hbar, vbar;	Choice choice = new Choice();		TextField widthTxt, heightTxt, metaTxt, toxTxt, frmTxt;		// Panel c = new displayCanvas();	Thread cycleOfLife;	StatsThread statsClock;	static Frame f;	static Graphics g;		//--REPLACE   Image titleImg;			////////////////////////////////////////////////	public static void main(String args[]) {	// Having a Main method makes it works as an Application too.	////////////////////////////////////////////////			System.out.println("Amoeba: Executing as an \"Application\"" +			"This has not yet been implemented\n" +			"Please try viewing amoebaworld.html on a browser such as Netscape");						//Would like to execute these methods...		//init();		//start();	}	////////////////////////////////////////////////	////////////////////////////////////////////////	public void init() {	////////////////////////////////////////////////				g = getGraphics();				System.out.println("WELCOME TO AMOEBA");		System.out.println("_________________");		System.out.println("");		System.out.println("Written by Tom Atkinson 1998");		System.out.println("funk.co.nz");		System.out.println("");		System.out.println("Please Wait Initialising...");				// Toolkit toolkit = Toolkit.getDefaultToolkit();		//--REPLACE   	titleImg = getImage( getDocumentBase(), "image/amoeba-title.gif");		//--REPLACE   	g.drawImage( titleImg, 0, 0 , 1, 1, this );		width = 40;  // Size of Life-Array. Only use Even numbers.		height = 20; // Size of Life-Array. Only use Even numbers.		cycles = 10000; // How many frames.		clock = 1; // init the clock		toxic = 250; // Level at which Amoeba turns red and then dies.				cell = new int [width][height];		herbGestation = 254; // point at which Herbivore divides.		magni = 1; // Zoom up so we can see the damn thing.		digest = 1 ; // Raise this to lower the feeding speed		metabolism = 35; // speed at which Herbivores burn fuel.		childHealth = metabolism + 5; // Health of the Herbivore's children.		divisionTax = 225; // Amount of life lost when Herbivore divides.		//Random.setSeed( (long) 0.5 ); // RANDOM NUMBER SEED, makes same sequence every time!				aPop = 0; // Populations		hPop = 0; // Populations		aWeight = 0;		hWeight = 0;		aHealth = 0;		hHealth = 0;		lifePlaneOffset = 100; // Amount to offset the image vertically.		lifePlaneOffsetLeft = 5;		frameSkip = 1; // how many frames to skip... cause graphics too slow.		screenWidth = size().width;		screenHeight = size().height;		status = "Welcome to Amoeba, starting up...";		if ( screenWidth < 360 )			screenWidth = 360;		show();		setBackground( Color.black );		setForeground( Color.white );				// Lay down some turf on the matrix:		for (int xx = 0; xx < 15; xx++) {			cell [xx][10] = 50;			cell [xx][15] = -200;		}				cell[ width - 2 ][ height - 2 ] = 1; // Little dot of turf!				bootingAnimation();				panelWidth = 70;		fixSize();		addGadgets(); // method to add the GUI controls.				System.out.println ("Now starting Threads..." );				cycleOfLife = new Thread( "Amoeba-cycleOfLife" );		//--REPLACE   	g.drawImage( titleImg, 0, 0, 1, 1, this );				// normally use:		//statsClock = new StatsThread( p.getGraphics(), lifePlaneOffset, this );		statsClock = new StatsThread( getGraphics(), lifePlaneOffset, this );		statsClock.start();				// Either start the thread or call run();		//cycleOfLife.start();				running = true;				System.out.println ("...init() is finished." );			}// end of Init	////////////////////////////////////////////////	public void start() {	////////////////////////////////////////////////			System.out.println ("\nCURRENT WORLD VALUES:" +				"\n Width = " + width + 		"\n Height = " + height +		"\n Number of Frames = " + cycles +		"\n Amoeba Toxicity = " + toxic +				"\n Herbivore Gestation Point = " + herbGestation +		"\n Magnification = " + magni +		"\n Digestion Speed = " + digest);				run();				}			////////////////////////////////////////////////		////////////////////////////////////////////////			public void stop() {		////////////////////////////////////////////////									System.out.println( "Applet has been told to stop." );				amoebaPause();								}					////////////////////////////////////////////////		////////////////////////////////////////////////		public void destroy() {		////////////////////////////////////////////////			System.out.println( "Running destroy()..." );						cycleOfLife.stop();			running = false;			statsClock.killIt();			// statsClock.stop();						System.out.println( "Applet has been destroyed." );		}		////////////////////////////////////////////////				// HERE'S WHERE THE MAIN LOOP RESIDES		////////////////////////////////////////////////		public void run() {	////////////////////////////////////////////////		System.out.println ("The run() method has been called...");		show();				for (clock=1; clock < cycles; clock++) {							GrowLife();			// High values of frameskip will speed up the algorithm.			// 30 frames pass to warm up the stage:			if (clock % frameSkip == 0 ) {												// Check to see if window has been resized:				if ( screenWidth != size().width ) {					screenWidth = size().width;					fixSize();				}				if ( screenHeight != size().height ) {					screenHeight = size().height;					fixSize();				}								drawLifePlane( getGraphics() );								if ( aHealth == 0 ) {					cell[1][1] = 1;				}								try { cycleOfLife.sleep(2); }				catch (InterruptedException e) {}								while ( !running ) {					try { cycleOfLife.sleep(2000); }					catch (InterruptedException e) {}					if ( verbose )						System.out.print( "." );				}					}						// Update the Statistics:			aHealth = aWeight / ( width * height );			hHealth = Math.abs( hWeight / ( hPop + 1 ) );			this.statsClock.refresh( clock, aPop, Math.abs( hPop ), aHealth, hHealth, drawTime, growTime, frameSkip, status );						// This will switch Fertility on and off automatically:			if ( aHealth > seasonLimits[ 1 ] ) {				// System.out.println( "DEBUG: TRYING TO GO FERTILE... aHealth = " + aHealth + fertile );				if ( !fertile ) {					fertile = true;					cbFertile.setState( fertile );				}			}			else if ( aHealth < seasonLimits[ 0 ] ) {				if ( fertile = true ) {					fertile = false;					cbFertile.setState( fertile );				}			}						// If the user has changed the size of array:			if ( needsArrayChange ) {								try {					int width = Integer.parseInt( widthTxt.getText() );					if ( width > 1024 )						width = 1024;					if ( width < 6 )						width = 6;											int height = Integer.parseInt( heightTxt.getText() );					if ( height > 1024 )						height = 1024;					if ( height < 6 )						height = 6;										changeSize ( width, height );								}				catch ( NumberFormatException e) {					System.out.println( "NumberFormatExcpetion: " + e );				}								// using temp just to get access to valueOf( int ) method				widthTxt.setText( temp.valueOf( width )  );				heightTxt.setText( temp.valueOf( height )  );								needsArrayChange = false;			}									// Give up some processor for other tasks:			cycleOfLife.yield();		}				// THAT'S IT !		System.out.println ("Completed " + cycles + " frames, which required " + (cycles * width * height) + " iterations of the life algorithm. \n Have a nice day, and thank you for using Amoeba. \n Remember to visit the website at http://funk.co.nz/");		isFinished = true;		repaint();		statsClock = null;				} // End run.	/////////////////////////////////////////////////*	////////////////////////////////////////////////	public boolean handleEvent (Event event, Object what ) {	////////////////////////////////////////////////		if ( verbose ) {			System.out.println("*** handleEvent ***");			System.out.println(what.getClass().getName() +			"= " + what);		return true;			} // end of handleEvent	////////////////////////////////////////////////*/	////////////////////////////////////////////////	public boolean action(Event event, Object what) {	////////////////////////////////////////////////		System.out.println("ACTION: " + what.getClass().getName() + "= " + what);		if (event.target instanceof Button) {			System.out.print("Button: ");			if ( event.target.equals( pauseBut ) && running ) {				amoebaPause();			}			else if ( event.target.equals( pauseBut ) ) {				amoebaResume();							}			if (event.target.equals( redrawBut ) ) {				System.out.println("Forced Redraw..." );				drawLifePlane( getGraphics() );								}				}		if (event.target instanceof TextField) {			int t = 0;			if (event.target.equals( widthTxt ) ) {				needsArrayChange = true;			}							if (event.target.equals( heightTxt ) ) {				needsArrayChange = true;			}							if (event.target.equals( frmTxt ) ) {								try {					t = Integer.parseInt( frmTxt.getText() );					if ( t > 255 )						t = 255;					if ( t < 1 )						t = 1;					frameSkip = t;									}				catch ( NumberFormatException e) {					System.out.println( "NumberFormatExcpetion: " + e );				}				frmTxt.setText( temp.valueOf( frameSkip )  );			}						if (event.target.equals( metaTxt ) ) {								try {					t = Integer.parseInt( metaTxt.getText() );					if ( t > 255 )						t = 255;					if ( t < 0 )						t = 0;					metabolism = t;					childHealth = metabolism + 5;				}				catch ( NumberFormatException e) {					System.out.println( "NumberFormatExcpetion: " + e );				}				metaTxt.setText( temp.valueOf( metabolism )  );							}						if (event.target.equals( toxTxt ) ) {				try {					t = Integer.parseInt( toxTxt.getText() );					if ( t > 255 )						t = 255;					if ( t < 1 )						t = 1;					toxic = t;				}				catch ( NumberFormatException e) {					System.out.println( "NumberFormatExcpetion: " + e );				}				toxTxt.setText( temp.valueOf( toxic )  );			}					}		if (event.target instanceof Checkbox) {			System.out.print("Checkbox: ");			if (event.target.equals( cbVerbose ) ) {				verbose = !verbose;			}			if (event.target.equals( cbFertile ) ) {				fertile = !fertile;			}			if (event.target.equals( cb3DMode ) ) {				is3DMode = !is3DMode;				needsBlack = true;			}		}					if (event.target instanceof Choice) {			needsBlack = true;			System.out.print("Choice: ");			if ( what == "Double" ) {				magni = magni * 2;			}			if ( what == "Plus 2" ) {				magni += 2;			}			if ( what == "Plus 1" ) {				magni++;			}			if ( what == "Best Fit" ) {				fixSize();			}			if ( what == "Minus 1" ) {				magni--;			}			if ( what == "Minus 2" ) {				magni -= 2;			}			if ( what == "Half" ) {				magni = magni / 2;			}			if ( magni < 1 )				magni = 1;			else if ( magni > 256 )				magni = 256;		}		return true;	}	////////////////////////////////////////////////	////////////////////////////////////////////////	public void amoebaPause() {	////////////////////////////////////////////////		System.out.println("*** Trying to Pause...");		running = false;		pauseBut.setLabel( "Resume" );		statsClock.suspend();		} // end of amoebaPause	////////////////////////////////////////////////	////////////////////////////////////////////////	public void amoebaResume() {	////////////////////////////////////////////////		System.out.println("*** Trying to start up again...");			running = true;			pauseBut.setLabel( "Pause" );			statsClock.resume();		} // end of amoebaResume	////////////////////////////////////////////////	////////////////////////////////////////////////	public static void changeSize( int x, int y ) {	////////////////////////////////////////////////		System.out.println( "Changing the life array size...");				int z = 6; // the smallest dimension allowable.		if ( y < z ) y = z;		if ( x < z ) x = z;				int[][] tempCell = new int[ x ][ y ];		int q;				System.out.println(			"Old Size: " + width + ", " + height + "\r" +			"New Size: " + x + ", " + y );				//Copy the old Amoeba out:		for ( int ay = 0; ay < y; ay++ ) {			for ( int ax = 0; ax < x; ax++ ) {				q = 0;				if ( ax < width - 1 && ay < height - 1 ) {										q = cell[ ax ][ ay ];				}				tempCell[ ax ][ ay ] = q;			}		}		tempCell[ 2 ][ 2 ] = 1; // Seed it just in case.		cell = tempCell;		width = x;		height = y;		fixSize();	}	////////////////////////////////////////////////	////////////////////////////////////////////////	public static void fixSize() {	////////////////////////////////////////////////		System.out.println( "fixSize()" );		// use ( screenWidth - panelWidth ) because panel p is 40 pixels wide.		int x = ( ( screenWidth - panelWidth ) - ( lifePlaneOffsetLeft * 2 ) ) / width;		int y = ( screenHeight - ( lifePlaneOffset ) ) / height;					if ( x >= y ) {			magni = ( screenHeight - lifePlaneOffset ) / height;		}		else {			magni = ( screenWidth - panelWidth ) / width;		}				shear = ( screenWidth - ( lifePlaneOffsetLeft * 2 ) ) / ( magni * width );		// resize( screenWidth, screenHeight ) ;			needsBlack = true;	}	////////////////////////////////////////////////		////////////////////////////////////////////////	public static void resizeArray() {	////////////////////////////////////////////////		Frame dialog = new Frame();		dialog.show();		Graphics dg = dialog.getGraphics();		Button okay = new Button( "Okay" );		Button cancel = new Button( "Cancel" );		dialog.add( okay );		dialog.add( cancel );		dg.drawString( "Select a new size...", 30, 30 );		}		////////////////////////////////////////////////		public static void GrowLife() {		////////////////////////////////////////////////				ctm = System.currentTimeMillis();				//Notice that these limits have been reduced by 1 on each side		//This gives a "sterile" border, so that it doesn't overgrow it's boundaries		//This method simply tells each individual cell to grow by calling GrowCell()		aPop = 0;		hPop = 0;		aWeight = 0;		hWeight = 0;				for (int y=1; y<height-1; y++) {			for (int x=1; x<width-1; x++) {				GrowCell(x,y);			}		}		calcStatus(); 				if ( verbose )			System.out.println( "Finished cycle: " + clock );				growTime = System.currentTimeMillis() - ctm;		}	////////////////////////////////////////////////	public static void calcStatus() {	// Figures out what to say about the situation in the status line.	////////////////////////////////////////////////				if ( hPop == 0 ) {			status = "Amoeba is growing, when it reaches " + toxic + ", Herbivore will roam the surface.";		}		else {			if ( hPop == 1 ) { // SINGULAR wording.				if ( fertile ) {					status = "Just one Herbivore is feeding on the Amoeba, and will now multiply.";				}				else {					status = "Just one Herbivore is feeding on the Amoeba, to make life easier for the Herbivor, reduce the Metabolism setting.";				}			}			else { // PLURAL wording				if ( hPop > 80 ) { // If there are heaps of Herbivor...					if ( fertile ) {						status = hPop + " Herbivore are feeding on the Amoeba, and are multiplying.";					}					else {						status = hPop + " Herbivore are feeding on the Amoeba, to reduce the population try increasing the Metabolism setting.";					}				}				else {					if ( fertile ) {						status = hPop + " Herbivore are feeding on the Amoeba, and are multiplying.";					}					else {						status = hPop + " Herbivore are feeding on the Amoeba, and are waiting for more Amoeba to grow.";					}								}			}			}		if ( hasExploded ) {			status = "An Amoeba had reached toxic level and exploded.";			hasExploded = false;		}	}	////////////////////////////////////////////////	public static void GrowCell(int x, int y) {	////////////////////////////////////////////////			// This method will grow the amoeba at location cell[x][y]		// Most of the hardcore processing is done here.				//int winner = -1; // The best position for Herbivore to go.		int a, b, c, deltaX, deltaY; // a, b, c are arbitrary, deltaX & deltaY related to "movement"				if (cell[x][y] > 0) {			// More than zero means it's Amoeba.						if (cell[x][y] > toxic) {				if (verbose)					System.out.println ("Cell " + x + y + " has become Toxic and will die...!");				// The cells explodeds, and sterilise's neighbouring cells...					NukeCell(x,y); // Calls it's own method				cell[x][y] = -( herbGestation - metabolism ); // Replace the dead ground with a Herbivore.														}			else {				// Cell is healthy, and will now reproduce...				// dx dy will contain the co-ordinates of the "Neighbouring" cell.				dx = (int)Math.round((Math.random() * 2) - 1);				dy = (int)Math.round((Math.random() * 2) - 1);								if (cell[x + dx][y + dy] < 0) {									// The neighbouring cell is a Herbivore, so this will just feed the Herbivore					cell[x + dx][y + dy]--;					}									else { 					// Grow the Amoeba!					cell[x + dx][y + dy]++; 					}				}						aPop++; // Calc Population				aWeight += cell[x][y]; // Calc Health		}		//-------------------------------Herbivore			////////////////////////////////////////////////		else if (cell[x][y] < 0) {			hPop++; // Calc Population				hWeight += cell[x][y]; // Calc Health						// Less than zero means it's a "Herbivore".						// The life force of a Herbivore grows weaker with every cycle:			cell[x][y] += metabolism;						// if Herbivore is dead, set to zero:			if ( cell[x][y] > 0 ) {				cell[x][y] = 0; // Dead.			}						// The Herbivore decides a favourable direction to go...						// First we decide the horizontal vector...			// a, b, & c are just working variables.			// Note that we add the values, and becuase Herbivore's are represented			// Will negative numbers, Herbivores will repel each other (when crowded)			// Higher values indicate a rich source of life-giving Amoeba!						a = cell[x+1][y-1] + cell[x+1][y] + cell[x+1][y+1] ; // NE, E, SE			if ( cell[x+1][y-1] < 0 ||				cell[x+1][y] < 0 ||				cell[x+1][y+1] < 0 )				a += 500;						b = cell[x][y-1] + cell[x][y+1] ; // N, S			if ( cell[x][y-1] < 0 ||				cell[x][y+1] < 0 )				b += 500;						c = cell[x-1][y-1] + cell[x-1][y] + cell[x-1][y+1] ; // NW, W, SW			if ( cell[x-1][y-1] < 0 ||				cell[x-1][y] < 0 ||				cell[x-1][y+1] < 0 )							c += 500;			if (a > b && a > c) {				deltaX = 1; // Go East!			}			else {				if (b > c) {					deltaX = 0; // Stay Still!				}				else {					deltaX = -1; // Go West!				}			}						// Now we decide the vertical vector...			a = cell[x-1][y-1] + cell[x][y-1] + cell[x+1][y-1] ; // NE, N, NW			b = cell[x-1][y] + cell[x+1][y] ; // E, W			c = cell[x-1][y+1] + cell[x][y+1] + cell[x+1][y+1] ; // SE, S, SW			if (a > b && a > c) {				deltaY = -1; // Go North!			}			else {				if (b > c) {					deltaY = 0; // Stay Still!				}				else {					deltaY = 1; // Go South!				}			}												// The following code keeps the wondering Herbivore away from the borders			if (x <= 2) deltaX = 1; // Jump East			if (x >= width - 2) deltaX = -1; // Jump West			if (y <= 2) deltaY = 1; // Jump South			if (y >= height - 2) deltaY = -1; // Jump North									// if the neighbour is an Amoeba...			if (cell[x+deltaX][y+deltaY] > 0) {				cell[x][y] -= (cell[x+deltaX][y+deltaY] / digest); // Herbivore eats and grows!				// Now we  if the cell is ready to multiply...								if ( Math.abs( cell[x][y] ) > herbGestation )  {					if (verbose)							System.out.println ("A Herbivore is above herbGestation.");					if ( fertile ) {						if (verbose)							System.out.println ("A Herbivore has divided.");						// Old Algor: cell[x][y] = -(herbGestation - (metabolism * 6)); // this will stop it from reproducing.						//cell[x][y] = -(herbGestation - divisionTax); // Giving birth is taxing.						cell[x][y] = -100;                             // Giving birth is taxing.											 cell[x+deltaX][y+deltaY] = cell[x][y]; // Herbivore moves into new residence.					}					else {						// DEBUG get rid of this verbose line:						if (verbose)							System.out.println ("But not fertile.");						cell[x][y] = -herbGestation - 5; // Plateu					}				}								else {					// Herbivore not ready to divide, so it moves...					cell[x+deltaX][y+deltaY] = cell[x][y]; // Herbivore moves into new residence.					cell[x][y] = 0; // Previous location is eaten bare, returns to zero.				} 			}		} // end herb	} // end method GrowCell	////////////////////////////////////////////////		////////////////////////////////////////////////	public static void NukeCell(int x, int y) {	// This method will destroy surronding tissue	////////////////////////////////////////////////		int bombSize = 2;				if ( x < bombSize )			x = bombSize + 1;		else if ( x >= width - bombSize )			x = width - bombSize - 1;		if ( y < bombSize )			y = bombSize + 1;		else if ( y >= height - bombSize )			y = height - bombSize - 1;				tmp = 4; // margin around edges		if ( !is3DMode && x > tmp && x < width - tmp && y > tmp && y < height - tmp )			deliverBomb( x, y );				for (int nukeX = -bombSize; nukeX <=bombSize; nukeX++) {			for (int nukeY = -bombSize; nukeY <=bombSize; nukeY++) {				cell[x + nukeX][y + nukeY]=0;			}		}	} // end method NukeCell	////////////////////////////////////////////////		////////////////////////////////////////////////	public static void deliverBomb( int x, int y ) {	////////////////////////////////////////////////		hasExploded = true;						x -= 2; // Bomb seems to need to offset by 2		y -= 2;					Color c1 = new Color( 255, 255, 0 );		g.setXORMode( c1 );		if ( verbose ) 			System.out.println( "Boom!" );								// Lay down the bomb:		tmp = 2;		g.fillOval(			( ( x * magni ) + lifePlaneOffsetLeft ) - ( tmp * ( magni / 2 ) ),			( ( y * magni ) + lifePlaneOffset ) - ( tmp * ( magni / 2 ) ),				( tmp * magni ) + ( magni / 2 ), ( tmp * magni ) + ( magni / 2 ) );		// pick it back up, but leave one behind!		tmp = 3;		g.fillOval(			( ( x * magni ) + lifePlaneOffsetLeft ) - ( tmp * ( magni / 2 ) ),			( ( y * magni ) + lifePlaneOffset ) - ( tmp * ( magni / 2 ) ),				( tmp * magni ) + ( magni / 2 ), ( tmp * magni ) + ( magni / 2 ) );			g.setPaintMode();	}		////////////////////////////////////////////////	public boolean mouseDown( Event e, int x, int y) {	////////////////////////////////////////////////			// plus 2 is to get rid of the borders of the lifeplane.		int gridX = ( ( x - lifePlaneOffsetLeft ) / magni ) + 2;		int gridY = ( ( y - lifePlaneOffset ) / magni ) + 2;				if ( verbose )			System.out.println( "mouseDown() at: " + x + ", " + y );				// Add a new Herbivore at x,y coord		// Take care of vertical offset and magnification too:		if ( gridX >= width || gridX < 0 || gridY >= height || gridY < 0) {			System.out.println( "You need to click _inside_ Lifeplane area to add a Herbivore" );			return true;		}			else {			cell[ gridX ][ gridY ] = -200; 			return true;		}	}	////////////////////////////////////////////////		////////////////////////////////////////////////	public boolean mouseDrag( Event e, int x, int y) {	////////////////////////////////////////////////			// plus 2 is to get rid of the borders of the lifeplane.		int gridX = ( ( x - lifePlaneOffsetLeft ) / magni ) + 2;		int gridY = ( ( y - lifePlaneOffset ) / magni ) + 2;				if ( verbose )			System.out.println( "mouseDrag() at: " + x + ", " + y );				// Add a new Herbivore at x,y coord		// Take care of vertical offset and magnification too:		if ( gridX >= width || gridX < 0 || gridY >= height || gridY < 0) {			System.out.println( "You need to click _inside_ Lifeplane area to add a Herbivore" );			return true;		}			else {			cell[ gridX ][ gridY ] = -200; 			return true;		}	}	////////////////////////////////////////////////		////////////////////////////////////////////////	public void update(Graphics g) {	////////////////////////////////////////////////		if ( verbose )			System.out.println("update method called...");				if ( !isBooting ) {			g.setColor ( new Color( 0, 0, 0 ) );			g.fillRect( 1, 1, size().width - 1, size().height - 1 );		}				paint(g);	}	////////////////////////////////////////////////		////////////////////////////////////////////////	public void paint(Graphics g) {	////////////////////////////////////////////////		// image is 463 pixels wide.		// 130 is how wide the stats display is.				if ( isBooting ) {			drawRedSteps( g );			drawCredits( g );		}		else {			//--REPLACE   		g.drawImage( titleImg, lifePlaneOffsetLeft, lifePlaneOffset - 90 , this );			if ( isFinished ) {				g.setColor( new Color( 255, 10, 10 ) );				g.setFont(new  Font("Serif",  Font.BOLD,  16 ) );				g.drawString( "Finished", size().width / 2, ( size().height / 2 ) + 10 );				drawLifePlane( g );								}		}		// paintBorders( g );			} // end Paint().		////////////////////////////////////////////////	public void drawCredits(Graphics g) {	////////////////////////////////////////////////		g.setColor( new Color( 180, 180, 180 ) );		g.setFont(new  Font("Serif",  Font.BOLD,  16 ) );		g.drawString( "Amoeba Artificial Life Simulator", size().width / 3, ( size().height / 2 ) + 30 );		g.drawString( "©1998 Tom Atkinson - funk.co.nz", size().width / 3, ( size().height / 2 ) + 90 );	}	////////////////////////////////////////////////	public void drawLifePlane( Graphics g ) {	////////////////////////////////////////////////				ctm = System.currentTimeMillis();				if ( needsBlack ) {			//g.setColor( new Color(0, 0, 0) );			//g.fillRect( 0, lifePlaneOffset, size().width, size().height );			repaint();				needsBlack = false;		}				if ( is3DMode )			draw3DPlane( g );		else			draw2DPlane( g );			// g.setColor( new Color( 255, 255, 255 ) );				drawTime = System.currentTimeMillis() - ctm;	} // end of method drawLifePlane		////////////////////////////////////////////////	public void draw2DPlane( Graphics g ) {	////////////////////////////////////////////////				for (int y = 2; y < height - 2; y++ ) {						for ( int x = 2; x < width - 2; x++ ) {										if (cell[x][y] >= toxic ) { // Red					g.setColor(new Color(255,0,0));				}								else if (cell[x][y] > 0) { // It's an Amoeba.					// Make it Green in colour.					g.setColor(new Color(0, cell[x][y], 129 - (cell[x][y] / 2)));				}				else if (cell[x][y] < 0) { // It's a Herbivore.					// Make it a shade of Grey.					// Need to use Absolute Value.						// DEBUG: COPY THIS TO BOTH 2D amd 3D methods.					tmp = Math.abs(cell[x][y]);					if ( tmp > 255 )						tmp = 255;					g.setColor(new Color(128, 128, tmp ) );						}								else if (cell[x][y] == 0) { // Black					g.setColor(new Color(0, 0, 0));					// System.out.print ("Black");				}											// Use Filled Rectangles...				// use ( x - 2 ) because of start point on loop.				g.fillRect( lifePlaneOffsetLeft + ( ( x - 2 ) * magni), lifePlaneOffset + ( ( y - 2 ) * magni), magni, magni);			}		}		g.setColor( new Color( 255, 255, 255 ) );		}		////////////////////////////////////////////////	public void draw3DPlane( Graphics g ) {	////////////////////////////////////////////////		int cellHeight = 30; // The height of a single pixel step upwards.	int current; // current cell.		int offset3d = 5 * magni;	g.setColor( new Color( 0, 0, 0 ) );	g.fillRect( 0, lifePlaneOffset, size().width, offset3D * 2 );		for (int y = 2; y < height - 2; y++ ) {					// Clear the line:		g.setColor( new Color(0, 0, 0) );		g.fillRect( 0, lifePlaneOffset + ( y * magni ) + offset3d, size().width, magni );						for ( int x = 2; x < width - 2; x++ ) {									current = cell[x][y];								if ( current >= toxic-1) { // Red					g.setColor(new Color(255,0,0));				}								else if ( current > 0) { // It's an Amoeba.					// Make it Green in colour.					g.setColor(new Color(0, cell[x][y], 129 - (cell[x][y] / 2)));				}				else if ( current < 0) { // It's a Herbivore.					// Make it a shade of Grey.					// Need to use Absolute Value.						// DEBUG: COPY THIS TO BOTH 2D amd 3D methods.					tmp = Math.abs(cell[x][y]);					if ( tmp > 255 )						tmp = 255;					g.setColor(new Color(128, 128, tmp ) );						}								else if ( current == 0) { // Black					g.setColor(new Color(0, 0, 0));				}											int dx = ( ( height - y ) * shear ) + lifePlaneOffsetLeft + (x * magni);					if ( current < 0) { // It's a Herbivore.					current = Math.abs( current );					g.fillOval(						dx,						lifePlaneOffset + offset3d + ( ( y * magni ) - ( ( current * magni ) / cellHeight) ),						magni,						magni );				}				else { // This is the 3D part:									// Lateral line to rightmost neighbour:					g.drawLine(						dx,						lifePlaneOffset + offset3d + ( ( y * magni ) - ( ( current * magni ) / cellHeight) ),						dx + magni,						lifePlaneOffset + offset3d + ( ( y * magni ) - ( ( Math.abs( cell[ x + 1 ][ y ] ) * magni ) / cellHeight) ) );					// Longitudinal line to underneath neighbour:					g.drawLine(						dx,						lifePlaneOffset + offset3d + ( ( y * magni ) - ( ( current * magni ) / cellHeight) ),						dx - shear,						lifePlaneOffset + offset3d + magni + ( ( y * magni ) - ( ( Math.abs( cell[ x ][ y + 1 ] ) * magni ) / cellHeight) ) );				}							}		}		g.setColor( new Color( 255, 255, 255 ) );		}	////////////////////////////////////////////////	public void paintBorders( Graphics g ) {	////////////////////////////////////////////////								g.setColor ( new Color( 255, 255, 255 ) );		g.drawLine ( 0, 0, 0, 0 );		g.setColor ( new Color( 192, 192, 192 ) );		g.drawLine ( 1, 0, size().width, 0 );		g.drawLine ( 0, 1, 0, size().height ); 		g.setColor ( new Color( 128, 128, 128 ) );		g.drawLine ( size().width, 1, size().width, size().height - 1 ); 		g.drawLine ( 1, size().height, size().width - 1, size().height ); 							} // end of paintBorders		////////////////////////////////////////////////	public void addGadgets() {	////////////////////////////////////////////////				//Graphics g = g;				setLayout( new BorderLayout() );				p.setBackground( new Color( 30, 30, 50 ) );		p.setForeground( new Color( 255, 255, 255 ) );		// p.reshape( size().width - panelWidth, 0, panelWidth, size().height) ;				add ( "East", p );		setFont(new  Font("SansSerif",  Font.PLAIN,  12));		p.setLayout(gridbag);		c.fill  =  GridBagConstraints.BOTH;		c.weightx  =  1.0;				// 1 gadget per line, 17 gadgets ? i think.		p.setLayout( new GridLayout( 17, 1 ) );				addComponent( pauseBut );		c.gridwidth  =  GridBagConstraints.REMAINDER;		addComponent( redrawBut );				widthTxt = setupText( widthTxt,  "" + width, 3, "Width") ;		addComponent( widthTxt );		heightTxt = setupText( heightTxt,  "" + height, 3, "Height") ;		addComponent( heightTxt );		metaTxt = setupText( metaTxt,  "" + metabolism, 3, "Metabolism") ;		addComponent( metaTxt );		toxTxt = setupText( toxTxt, "" + toxic, 3, "Toxic Level" );		addComponent( toxTxt );		frmTxt = setupText( frmTxt, "" + frameSkip, 3, "Skip Frames");		addComponent( frmTxt );			addComponent( cbVerbose );		addComponent( cbFertile );		addComponent( cb3DMode );		cb3DMode.setState( is3DMode );				addComponent( new Label( "Magnification" ) );				choice.addItem("Double");		choice.addItem("Plus 2");		choice.addItem("Plus 1");		choice.addItem("Best Fit");		choice.addItem("Minus 1");		choice.addItem("Minus 2");		choice.addItem("Half");				addComponent( choice );				validate();		p.show();			} // end of addGadgets		////////////////////////////////////////////////	protected  void  addComponent( Component component ) {	////////////////////////////////////////////////                gridbag.setConstraints( component,  c);                p.add( component );	}	////////////////////////////////////////////////	public  void  javaExample()  {	////////////////////////////////////////////////                GridBagLayout  gridbag  =  new  GridBagLayout();                GridBagConstraints  c  =  new  GridBagConstraints();                setFont(new  Font("SansSerif",  Font.PLAIN,  12));                setLayout(gridbag);                c.fill  =  GridBagConstraints.BOTH;                c.weightx  =  1.0;                       // end row              c.gridwidth  =  GridBagConstraints.REMAINDER;              //makebutton("Button4",  gridbag,  c);                c.weightx  =  0.0;                    //reset  to  the  default                //makebutton("Button5",  gridbag,  c);  //another  row            // next-to last  in  row            c.gridwidth  =  GridBagConstraints.RELATIVE;                 // makebutton("Button6",  gridbag,  c);            c.gridwidth  =  GridBagConstraints.REMAINDER;  //end  row               // makebutton("Button7",  gridbag,  c);            c.gridwidth  =  1;                // reset to the default            c.gridheight  =  2;                c.weighty  =  1.0;              //  makebutton("Button8",  gridbag,  c);                c.weighty  =  0.0;                    //reset  to  the  default            // end  row            c.gridwidth  =  GridBagConstraints.REMAINDER;              c.gridheight  =  1;               // reset to the default                //makebutton("Button9",  gridbag,  c);               // makebutton("Button10",  gridbag,  c);                resize(300,  100);	} // end of javaExample    			////////////////////////////////////////////////	public String currentTime() {	////////////////////////////////////////////////		Date now = new Date();		return now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds();	} // end of currentTime()	////////////////////////////////////////////////	public TextField setupText( TextField t, String initialText, int size, String label ) {	////////////////////////////////////////////////		p.add( new Label( label ) );		t = new TextField( initialText, size );		t.setBackground( new Color( 0, 0, 0 ) );		t.setForeground( new Color( 255, 255, 255 ) );		return t;	}	/*	////////////////////////////////////////////////	public void printJob() {	////////////////////////////////////////////////		PrintJob pj = null;		pj = toolkit.getPrintJob ( this, "Main Window", null );		if ( pj == null )      // user cancels print job.			return;		Graphics g = pj.getGraphics();		paintAll( g );		printAll( g );		g.dispose();		pj.end();	}*/	////////////////////////////////////////////////	public void bootingAnimation() {	////////////////////////////////////////////////		Graphics g = getGraphics();				// this is now handled by paint()		//drawRedSteps( g ); 		//drawCredits( g );				String prog = "Amoeba";										g.setColor( new Color( 255, 255, 255 ) );		g.setXORMode( new Color( 255, 255, 255 ) );		tmp = 50; // num frames of animation		int stepSize = 1;				for ( int i = 0; i < tmp; i += stepSize ) {			g.setFont(new  Font("SansSerif",  Font.PLAIN,  5 + ( i * 3)  ) );		/*			g.drawString(				prog,				(int)( lifePlaneOffsetLeft + ( Math.random() * ( size().width - lifePlaneOffsetLeft ) ) ),				(int)( lifePlaneOffset + ( Math.random() * ( size().height - lifePlaneOffset ) ) )			);		*/			g.drawString(				prog,				(int)( 50 + ( Math.random() * 100 ) ),				(int)( 250 + ( Math.random() * 100 ) )			);					}		g.setPaintMode();			/*		try {			wait( 2000 );		} catch ( InterruptedException e ) {}				for ( int i = 0; i < tmp; i += 2 ) {			g.setColor( new Color( 255 - i, 255 - i, 255 - i ) );			g.setFont(new  Font("SansSerif",  Font.PLAIN,  600 - ( i * 4 ) ) );			g.drawString( prog, 10 + i, ( size().height / 2 ) - i );		}	*/		isBooting = false;		repaint();	}		////////////////////////////////////////////////	public void drawRedSteps( Graphics g ) {	////////////////////////////////////////////////				tmp = 60; // max shade of red		for (int i = 0; i < tmp; i += 2 ) {			g.setColor ( new Color( i, 0, 0 ) );			g.fillRect( 0, ( size().height / tmp ) * i, size().width, size().height );		}	}		}// end of Amoeba class////////////////////////////////////////////////
