/**	Copyright 2000 Henry Bottomley (henry.bottomley@btinternet.com)
 *	Published for information and comment purposes
 */

import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.lang.*;

public class Cuboid extends Applet 
{
	protected Image     img;	
	protected double    heightVal, widthVal, depthVal;
	protected String    heightText, widthText, depthText;
	protected int       x1, x2, x3, x4, x5, y1, y2, y3, y4; 
	protected int       hScaled, wScaled, dScaled, hh, ww, dd ;
	protected int       xStart, yStart, xEnd, yEnd;
	protected int       xWidth, yHeight, arraySize, overMax;
	protected int       startCorner, scaling, scaleChange, reImaging; 
	protected int[]     drawMap;				
	protected String    pattern;
 	protected TextField heightBox = new TextField(6);
	protected TextField widthBox = new TextField(6);
	protected TextField depthBox = new TextField(6);
	//protected TextField counterDebug = new TextField(3);
	protected Button    uChoose = new Button("You choose");
	protected Choice    patternChoice = new Choice();
	protected Choice    scaleChoice = new Choice();
  	static final int    spaceControlX = 5; // stay away from edge
 	static final int    spaceControlY = 45; // space at top for controls
				
///////////////////////////////////////////////////////////////////////////
				
	public void init()
		{		
		add (heightBox); 
		add (widthBox); 
		add (depthBox);		
		add (uChoose);  
		patternChoice.addItem("distance from clicked point");
		patternChoice.addItem("sides used from clicked point");
		patternChoice.addItem("number of sides from clicked point");
		patternChoice.addItem("number of sides to opposite point");
		patternChoice.addItem("distance to opposite point");
		patternChoice.addItem("distance to furthest point - VERY SLOW ");
		add(patternChoice);
		pattern="distance from clicked point";
		scaleChoice.addItem("300");
		scaleChoice.addItem("200");
		scaleChoice.addItem("150");
		scaleChoice.addItem("100");
		scaleChoice.addItem("60");
		scaleChoice.addItem("40");
		scaleChoice.addItem("30");
		scaleChoice.addItem("20");
		add(scaleChoice);
		scaling=300;
		//add (counterDebug);  
		heightVal=3.0d;
		widthVal=5.0d;
		depthVal=1.0d;
		startCorner=1;
		scaleandCoord(heightVal,widthVal,depthVal,spaceControlX,spaceControlY,scaling);
		drawMap = new int[arraySize];
		drawMap = redrawMap();
		}				
				
///////////////////////////////////////////////////////////////////////////
				
	public void scaleandCoord(double hV, double wV, double dV, int sCX, int sCY, int sc)
		{ 
		double scaleVal=(1.0d*sc)/(hV+2.0d*dV);
		if (scaleVal>1.0d*sc/(wV+dV))
			{
			scaleVal=1.0d*sc/(wV+dV);
			}	
		hScaled=(int) (hV*scaleVal);
		wScaled=(int) (wV*scaleVal);
		dScaled=(int) (dV*scaleVal);

		y1=sCY;
		y2=y1+dScaled;
		y3=y2+hScaled;
		y4=y3+dScaled;
		x1=sCX;
		x2=x1+dScaled;
		x3=x2+wScaled;
		x4=x3+dScaled;
		x5=x4+wScaled;

 		xWidth=x5-x1+1;
		yHeight=y4-y1+1;
		arraySize=xWidth*yHeight;
		overMax=(hScaled+wScaled+dScaled)*(hScaled+wScaled+dScaled); // no pair of points will be this far apart

		scaleChange=1;

		return;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public void paint (Graphics g)
		{
		heightBox.setText(Double.toString(heightVal));
		widthBox.setText(Double.toString(widthVal));
		depthBox.setText(Double.toString(depthVal));

		g.setColor(Color.white);
		g.fillRect(0, 0, size().width, size().height);
		scaleChange=0;

		img = createImage(new MemoryImageSource(xWidth, yHeight, drawMap, 0, xWidth));
		reImaging=0;
		g.drawImage(img, x1, y1, null);

		
		//this draws the rectangles
		g.setColor(Color.black);		
		g.drawRect(x2,y1,x3-x2,y2-y1);//draw the rectangles
		g.drawRect(x1,y2,x2-x1,y3-y2);
		g.drawRect(x2,y3,x3-x2,y4-y3);
		g.drawRect(x2,y2,x3-x2,y3-y2);
		g.drawRect(x3,y2,x4-x3,y3-y2);
		g.drawRect(x4,y2,x5-x4,y3-y2);
		}

				
///////////////////////////////////////////////////////////////////////////
				
	//public void update (Graphics g) 
	//	{
	//	paint(g);
	//	}			
		
	//taken from comp.lang.java FAQ 
	  private Image offScreenImage;
	  private Dimension offScreenSize;
	  private Graphics offScreenGraphics;
	   
	  public final synchronized void update (Graphics g) 
	    {
	    Dimension d = size();
	    if((offScreenImage == null) || (d.width != offScreenSize.width) ||  (d.height != offScreenSize.height)) 
	      {
	      offScreenImage = createImage(d.width, d.height);
	      offScreenSize = d;
	      offScreenGraphics = offScreenImage.getGraphics();
	      }
	    offScreenGraphics.clearRect(0, 0, d.width, d.height);
	    paint(offScreenGraphics);
	    g.drawImage(offScreenImage, 0, 0, null);
	    }
	    
///////////////////////////////////////////////////////////////////////////
				
	public int[] redrawMap()
		{
		reImaging=1;
		
		if (startCorner==1) //(re)initialise some points after changing dimensions just off the corner
			{
			xStart=x2+1;
			yStart=y2+1;
			xEnd = xStart;
			yEnd = yStart;
			startCorner=0;
			}
		
		if (pattern=="distance to furthest point - VERY SLOW ") // title says it all
			{
			return distColour(maxDistAll());
			}
				
		if (pattern=="distance to opposite point") //surprisingly dull
			{
			int[] oppSP = new int[arraySize];
			
			for (int ySP=y1; ySP<=y4; ySP++) 
				{
				int xRowSP=(ySP-y1)*xWidth; 
				for (int xSP=x1; xSP<=x5; xSP++)   
					{
					if ((ySP<y2 || ySP>y3) && (xSP<x2 || xSP>x3)) // outside the net
						{
						oppSP[xRowSP+xSP-x1]=0; 
					 	}
					else
						{
						oppSP[xRowSP+xSP-x1]=distOpposite(xSP,ySP);
						}
					}
				}
			return distColour(oppSP);
			}
				
		if (pattern=="number of sides to opposite point") 
			{
			int[] oppSP = new int[arraySize];
			
			for (int ySP=y1; ySP<=y4; ySP++) 
				{
				int xRowSP=(ySP-y1)*xWidth; 
				for (int xSP=x1; xSP<=x5; xSP++)   
					{
					if ((ySP<y2 || ySP>y3) && (xSP<x2 || xSP>x3)) // outside the net
						{
						oppSP[xRowSP+xSP-x1]=0; 
					 	}
					else
						{
						oppSP[xRowSP+xSP-x1]=sidesOpposite(xSP,ySP);
						}
					}
				}
			return oppSP;
			}

		if (pattern=="sides used from clicked point" || pattern=="number of sides from clicked point") 
			{
			return sidesColour(distSquared(xStart,yStart),xStart,yStart);
			}
					
		// here in theory only if pattern equals "distance from clicked point"
		return distColour(distSquared(xStart,yStart));
		}
				
//////////////////////////////////////////////////////////////////////////
				
	public int h(int f, int r) //part of re-orientation depending on starting face and rotation
		{
		if ((f==0 || f==5) && (r==0 || r==2)) return hScaled;
		if ((f==0 || f==5) && (r==1 || r==3)) return dScaled;
		if ((f==1 || f==4) && (r==0 || r==2)) return hScaled;
		if ((f==1 || f==4) && (r==1 || r==3)) return wScaled;
		if ((f==2 || f==3) && (r==0 || r==2)) return dScaled;
		if ((f==2 || f==3) && (r==1 || r==3)) return wScaled;
		return 0;//error if reach here
		} 
				
///////////////////////////////////////////////////////////////////////////
				
	public int w(int f, int r) //part of re-orientation depending on starting face and rotation
		{
		if ((f==0 || f==5) && (r==0 || r==2)) return dScaled;
		if ((f==0 || f==5) && (r==1 || r==3)) return hScaled;
		if ((f==1 || f==4) && (r==0 || r==2)) return wScaled;
		if ((f==1 || f==4) && (r==1 || r==3)) return hScaled;
		if ((f==2 || f==3) && (r==0 || r==2)) return wScaled;
		if ((f==2 || f==3) && (r==1 || r==3)) return dScaled;
		return 0;//error if reach here
		} 
				
///////////////////////////////////////////////////////////////////////////
				
	public int d(int f, int r) //part of re-orientation depending on starting face and rotation
		{
		if (f==0 || f==5) return wScaled;
		if (f==1 || f==4) return dScaled;
		if (f==2 || f==3) return hScaled;
		return 0;//error if reach here
		} 

///////////////////////////////////////////////////////////////////////////
				


				
///////////////////////////////////////////////////////////////////////////
				
	public int xS(int f, int r, int xxx, int yyy) // measuring start from corner depending on starting face and rotation 
		{
		switch (r)
			{
			case 0:
			if (f==0) return xxx-x2;
			if (f==5) return xxx-x4;
			if (f==4) return xxx-x5;
			return xxx-x3;
			
			case 1:
			if (f==3) return y1-yyy;
			if (f==2) return y3-yyy;
			return y2-yyy;
			
			case 2:
			if (f==0) return x1-xxx;
			if (f==5) return x3-xxx;
			if (f==4) return x4-xxx;
			return x2-xxx;
			
			case 3:
			if (f==3) return yyy-y2;
			if (f==2) return yyy-y4;
			return yyy-y3;
			
			default:
			}
		return 0;//error if reach here
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int yS(int f, int r, int xxx, int yyy) // measuring start from corner depending on starting face and rotation 
		{
		switch (r)
			{
			case 0:		
			if (f==3) return yyy-y1;
			if (f==2) return yyy-y3;
			return yyy-y2;

			case 1:
			if (f==0) return xxx-x1;
			if (f==5) return xxx-x3;
			if (f==4) return xxx-x4;
			return xxx-x2;

			case 2:
			if (f==3) return y2-yyy;
			if (f==2) return y4-yyy;
			return y3-yyy;

			case 3:
			if (f==0) return x2-xxx;
			if (f==5) return x4-xxx;
			if (f==4) return x5-xxx;
			return x3-xxx;

			default:
			}
		return 0;//error if reach here
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int cellxy(int x, int y, int f, int r, int ao) //co-ordinates for finishing point based on adjusted starting point 
		{
		int key=100*f+10*r+ao;
		switch (key)
			{
			case   0: return x2-x1+x+(y2-y1+y)*xWidth;
			case   1: return x3-x1+x+(y2-y1+y)*xWidth;
			case  10: return x2-x1+x+(y1-y1+y)*xWidth;
			case  11: return x4-x1-y+(y2-y1+x)*xWidth;
			case  20: return x5-x1-x+(y3-y1-y)*xWidth;
			case  21: return x4-x1-x+(y3-y1-y)*xWidth;
			case  30: return x2-x1+x+(y3-y1+y)*xWidth;
			case  31: return x3-x1+y+(y3-y1-x)*xWidth;
			case 100: return x3-x1+x+(y2-y1+y)*xWidth;
			case 101: return x4-x1+x+(y2-y1+y)*xWidth;
			case 110: return x2-x1+y+(y2-y1-x)*xWidth;
			case 111: return x5-x1-y+(y2-y1+x)*xWidth;
			case 120: return x2-x1-x+(y3-y1-y)*xWidth;
			case 121: return x5-x1-x+(y3-y1-y)*xWidth;
			case 130: return x3-x1-y+(y3-y1+x)*xWidth;
			case 131: return x4-x1+y+(y3-y1-x)*xWidth;
			case 200: return x3-x1+y+(y3-y1-x)*xWidth;
			case 201: return x3-x1-x+(y2-y1-y)*xWidth;
			case 210: return x2-x1+y+(y3-y1-x)*xWidth;
			case 211: return x2-x1+y+(y2-y1-x)*xWidth;
			case 220: return x1-x1+y+(y3-y1-x)*xWidth;
			case 221: return x2-x1+x+(y1-y1+y)*xWidth;
			case 230: return x4-x1+y+(y3-y1-x)*xWidth;
			case 231: return x3-x1-y+(y1-y1+x)*xWidth;
			case 300: return x4-x1-y+(y2-y1+x)*xWidth;
			case 301: return x3-x1-x+(y4-y1-y)*xWidth;
			case 310: return x5-x1-y+(y2-y1+x)*xWidth;
			case 311: return x2-x1+y+(y4-y1-x)*xWidth;
			case 320: return x2-x1-y+(y2-y1+x)*xWidth;
			case 321: return x2-x1+x+(y3-y1+y)*xWidth;
			case 330: return x3-x1-y+(y2-y1+x)*xWidth;
			case 331: return x3-x1-y+(y3-y1+x)*xWidth;
			case 400: return x1-x1+x+(y2-y1+y)*xWidth;
			case 401: return x2-x1+x+(y2-y1+y)*xWidth;
			case 410: return x3-x1-y+(y1-y1+x)*xWidth;
			case 411: return x3-x1-y+(y2-y1+x)*xWidth;
			case 420: return x4-x1-x+(y3-y1-y)*xWidth;
			case 421: return x3-x1-x+(y3-y1-y)*xWidth;
			case 430: return x2-x1+y+(y4-y1-x)*xWidth;
			case 431: return x2-x1+y+(y3-y1-x)*xWidth;
			case 500: return x4-x1+x+(y2-y1+y)*xWidth;
			case 501: return x1-x1+x+(y2-y1+y)*xWidth;
			case 510: return x3-x1-x+(y2-y1-y)*xWidth;
			case 511: return x2-x1-y+(y2-y1+x)*xWidth;
			case 520: return x3-x1-x+(y3-y1-y)*xWidth;
			case 521: return x2-x1-x+(y3-y1-y)*xWidth;
			case 530: return x3-x1-x+(y4-y1-y)*xWidth;
			case 531: return x1-x1+y+(y3-y1-x)*xWidth;
			default:
			}
		return 0;//error if reach here
		}
				
///////////////////////////////////////////////////////////////////////////

	public int minDist(int xS1, int yS1, int faceS1, int xE2, int yE2, int faceE2)
		{
		// assume opposite faces		
		int testing=overMax;
		for (int rots=0; rots<4; rots++) // need to consider four different rotations
			{
			hh=h(faceS1,rots);
			ww=w(faceS1,rots);
			dd=d(faceS1,rots);
			int xStartTest=xS(faceS1,rots,xS1,yS1);
			int yStartTest=yS(faceS1,rots,xS1,yS1);
			int xEndTest=ww+xS(faceE2,rots,xE2,yE2);
			int yEndTest=hh-yS(faceE2,rots,xE2,yE2);
			testing=minisq(testing,(xEndTest+dd-xStartTest),(yEndTest-yStartTest));
			testing=minisq(testing,(yEndTest-xStartTest),(-dd-xEndTest-yStartTest));
			testing=minisq(testing,(-dd-ww-xEndTest-xStartTest),(-ww-yEndTest-yStartTest));
			testing=minisq(testing,(-dd-ww-xEndTest-xStartTest),(2*hh+ww-yEndTest-yStartTest));
			testing=minisq(testing,(hh-yEndTest-xStartTest),(hh+dd+xEndTest-yStartTest));
			}
		return testing;
		}

///////////////////////////////////////////////////////////////////////////
				
	public int faceChoice(int xFace, int yFace) // identify which face a point is on
		{
		if (yFace>=y2 && yFace<=y3)
			{
			if (xFace>=x1 && xFace<x2)
				{
				return 0;
				}
			else if (xFace>=x2 && xFace<=x3)
				{
				return 1;
				}
			else if (xFace>x3 && xFace<=x4)
				{
				return 5;
				}
			else if (xFace>x4 && xFace<=x5)
				{
				return 4; 
				}
			}			
		else if (yFace>=y1 && yFace<y2 && xFace>=x2 && xFace<=x3)
			{
			return 3;
			}
		else if (yFace>y3 && yFace<=y4 && xFace>=x2 && xFace<=x3)
			{
			return 2;
			}
		return -1; // error if reach here
		}
				
///////////////////////////////////////////////////////////////////////////

	public int[] maxDistAll() // array showing smallest square of distance from all starting points to other points on net 
		{		
		int[] maxDistSP = new int[arraySize];
		
		// //commented out block tries all starting points, but symmetry means only an eighth need be tested 
		//for (int ySP=y1; ySP<=y4; ySP++) 
		//	{
		//	int xRowSP=(ySP-y1)*xWidth; 
		//	for (int xSP=x1; xSP<=x5; xSP++)   
		//		{
		//		if ((ySP<y2 || ySP>y3) && (xSP<x2 || xSP>x3)) // outside the net
		//			{
		//			maxDistSP[xRowSP+xSP-x1]=0; 
		//		 	}
		//		else
		//			{
		//			maxDistSP[xRowSP+xSP-x1]=maxArray(distSquared(xSP,ySP));
		//			}
		//		}
		//	}
		//return maxDistSP;

		// //the following ugly blocks do the same as the previous commented out block, but several times (up to 8) faster
		int ySP,xSP,xRowSP1,xRowSP2,xRowSP3,xRowSP4,mD;
		for (ySP=y1; ySP<=y4; ySP++) // blank outside the net 
			{
			if (ySP<y2 || ySP>y3)
				{
				xRowSP1=(ySP-y1)*xWidth; 
				for (xSP=x1; xSP<=x5; xSP++)   
					{
					if (xSP<x2 || xSP>x3) // outside the net
						{
						maxDistSP[xRowSP1+xSP-x1]=0; 
						}
				 	}
				}
			}
		for (ySP=y1; 2*ySP<=y2+y1; ySP++)  // take a quarter of a face, do the calculations and then reproduce over that face and the opposite one
			{
			xRowSP1=(ySP-y1)*xWidth; 
			xRowSP2=(y2-ySP)*xWidth; 
			xRowSP3=(y3-y1+ySP-y1)*xWidth; 
			xRowSP4=(y4-ySP)*xWidth; 
			for (xSP=x2; 2*xSP<=x3+x2; xSP++)   
				{
				mD=maxArray(distSquared(xSP,ySP));
				maxDistSP[xRowSP1+xSP-x1]=mD;
				maxDistSP[xRowSP1+x4-xSP]=mD;
				maxDistSP[xRowSP2+xSP-x1]=mD;
				maxDistSP[xRowSP2+x4-xSP]=mD;
				maxDistSP[xRowSP3+xSP-x1]=mD;
				maxDistSP[xRowSP3+x4-xSP]=mD;
				maxDistSP[xRowSP4+xSP-x1]=mD;
				maxDistSP[xRowSP4+x4-xSP]=mD;
				}
			}
		for (ySP=y2; 2*ySP<=y3+y2; ySP++) // and do the same with quarters of two other faces 
			{
			xRowSP1=(ySP-y1)*xWidth; 
			xRowSP2=(y4-ySP)*xWidth; 
			for (xSP=x2; 2*xSP<=x3+x2; xSP++)   
				{
				mD=maxArray(distSquared(xSP,ySP));
				maxDistSP[xRowSP1+xSP-x1]=mD;
				maxDistSP[xRowSP1+x4-xSP]=mD;
				maxDistSP[xRowSP1+x3-x1+xSP-x1]=mD;
				maxDistSP[xRowSP1+x3-x1+x4-xSP]=mD;
				maxDistSP[xRowSP2+xSP-x1]=mD;
				maxDistSP[xRowSP2+x4-xSP]=mD;
				maxDistSP[xRowSP2+x3-x1+xSP-x1]=mD;
				maxDistSP[xRowSP2+x3-x1+x4-xSP]=mD;
				}
			for (xSP=x1; 2*xSP<=x2+x1; xSP++)   
				{
				mD=maxArray(distSquared(xSP,ySP));
				maxDistSP[xRowSP1+xSP-x1]=mD;
				maxDistSP[xRowSP1+x2-xSP]=mD;
				maxDistSP[xRowSP1+x3-x1+xSP-x1]=mD;
				maxDistSP[xRowSP1+x3-x1+x2-xSP]=mD;
				maxDistSP[xRowSP2+xSP-x1]=mD;
				maxDistSP[xRowSP2+x2-xSP]=mD;
				maxDistSP[xRowSP2+x3-x1+xSP-x1]=mD;
				maxDistSP[xRowSP2+x3-x1+x2-xSP]=mD;
				}
			}
		return maxDistSP;
		}
			
///////////////////////////////////////////////////////////////////////////
				
	public int[] distSquared(int x0, int y0) // array showing smallest square of distance from starting point to other points on net 
		{		
		int face0=faceChoice(x0,y0);
		int[] buf = new int[arraySize];
		int xRow,ySq,xStartTest,yStartTest,cel,testing,x,y;
		
		for (y=y1; y<=y4; y++) // basic distance on portrayed net (needed at least for points on the same face and provides starting point for other tests)
			{
			xRow=(y-y1)*xWidth; 
			ySq=(y-y0)*(y-y0);
			for (x=x1; x<=x5; x++)   
				{
				if ((y<y2 || y>y3) && (x<x2 || x>x3)) // outside the net
					{
					buf[xRow+x-x1]=0; 
				 	}
				else
					{
					buf[xRow+x-x1]=(x-x0)*(x-x0)+ySq;
					}
				}
			}
						
		for (int rots=0; rots<4; rots++) // need to consider four different rotations
			{
			hh=h(face0,rots);
			ww=w(face0,rots);
			dd=d(face0,rots);
			xStartTest=xS(face0,rots,x0,y0);
			yStartTest=yS(face0,rots,x0,y0);
			for (y=0; y<=hh; y++)
				{
				for (x=0; x<=dd; x++) // Test each adjacent face (one for each rotation)			
					{
					cel = cellxy(x,y,face0,rots,0);
					testing=buf[cel]; // see which is shortest
					testing=minisq(testing,(x-xStartTest),(y-yStartTest));
					testing=minisq(testing,(y-xStartTest),(-x-yStartTest));
					testing=minisq(testing,(dd-x-xStartTest),(-dd-y-yStartTest));
					testing=minisq(testing,(-ww-x-xStartTest),(-ww-y-yStartTest));
					testing=minisq(testing,(-ww-x-xStartTest),(2*hh+ww-y-yStartTest));
					testing=minisq(testing,(dd-x-xStartTest),(2*hh+dd-y-yStartTest));
					testing=minisq(testing,(hh-y-xStartTest),(hh+x-yStartTest));
					buf[cel]=testing;
					}
				for (x=0; x<=ww; x++) // Test opposite face once on each rotation 			
					{
					cel = cellxy(x,y,face0,rots,1);
					testing=buf[cel];
					testing=minisq(testing,(x+dd-xStartTest),(y-yStartTest));
					testing=minisq(testing,(y-xStartTest),(-dd-x-yStartTest));
					testing=minisq(testing,(-dd-ww-x-xStartTest),(-ww-y-yStartTest));
					testing=minisq(testing,(-dd-ww-x-xStartTest),(2*hh+ww-y-yStartTest));
					testing=minisq(testing,(hh-y-xStartTest),(hh+dd+x-yStartTest));
					buf[cel]=testing;
					}
				}
			}
		return buf;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int[] sidesColour(int[] inBuf, int x0, int y0) // base sides on distance
		{		
		int face0=faceChoice(x0,y0);
		int[] buf = new int[arraySize];
				
		for (int y=y1; y<=y4; y++) // basic distance on portrayed net (needed at least for points on the same face and provides starting point for other tests)
			{
			int xRow=(y-y1)*xWidth; 
			int ySq=(y-y0)*(y-y0);
			for (int x=x1; x<=x5; x++)   
				{
				if ((y<y2 || y>y3) && (x<x2 || x>x3)) // outside the net
					{
					buf[xRow+x-x1]=0; 
				 	}
				else
					{
					buf[xRow+x-x1]=-1;
					}
				}
			}
						
		for (int rots=0; rots<4; rots++) // need to consider four different rotations
			{
			hh=h(face0,rots);
			ww=w(face0,rots);
			dd=d(face0,rots);
			int xStartTest=xS(face0,rots,x0,y0);
			int yStartTest=yS(face0,rots,x0,y0);
			for (int y=0; y<=hh; y++)
				{
				for (int x=0; x<=dd; x++) // Test each adjacent face (one for each rotation)			
					{
					int cel = cellxy(x,y,face0,rots,0);
					int testing=inBuf[cel]; // see which is shortest
					int testRoute=buf[cel];
					testRoute=sqSide(testRoute,rots,1,testing,(x-xStartTest),(y-yStartTest));
					testRoute=sqSide(testRoute,rots,2,testing,(y-xStartTest),(-x-yStartTest));
					testRoute=sqSide(testRoute,rots,3,testing,(dd-x-xStartTest),(-dd-y-yStartTest));
					testRoute=sqSide(testRoute,rots,4,testing,(-ww-x-xStartTest),(-ww-y-yStartTest));
					testRoute=sqSide(testRoute,rots,5,testing,(-ww-x-xStartTest),(2*hh+ww-y-yStartTest));
					testRoute=sqSide(testRoute,rots,6,testing,(dd-x-xStartTest),(2*hh+dd-y-yStartTest));
					testRoute=sqSide(testRoute,rots,7,testing,(hh-y-xStartTest),(hh+x-yStartTest));
					buf[cel]=testRoute;
					}
				for (int x=0; x<=ww; x++) // Test opposite face once on each rotation 			
					{
					int cel = cellxy(x,y,face0,rots,1);
					int testing=inBuf[cel]; // see which is shortest
					int testRoute=buf[cel];
					testRoute=sqSide(testRoute,rots,11,testing,(x+dd-xStartTest),(y-yStartTest));
					testRoute=sqSide(testRoute,rots,12,testing,(y-xStartTest),(-dd-x-yStartTest));
					testRoute=sqSide(testRoute,rots,14,testing,(-dd-ww-x-xStartTest),(-ww-y-yStartTest));
					testRoute=sqSide(testRoute,rots,15,testing,(-dd-ww-x-xStartTest),(2*hh+ww-y-yStartTest));
					testRoute=sqSide(testRoute,rots,17,testing,(hh-y-xStartTest),(hh+dd+x-yStartTest));
					buf[cel]=testRoute;
					}
				}
			}

		if (pattern=="number of sides from clicked point") 
			{
			for (int y=y1; y<=y4; y++) //change route to colour
				{
				int xRow=(y-y1)*xWidth; 
				int ySq=(y-y0)*(y-y0);
				for (int x=x1; x<=x5; x++)   
					{
					buf[xRow+x-x1]=colourSideNumber(buf[xRow+x-x1]);
					}
				}
			}	
		else // i.e. if (pattern=="sides used from clicked point") 	  
			{
			for (int y=y1; y<=y4; y++) //change route to colour
				{
				int xRow=(y-y1)*xWidth; 
				int ySq=(y-y0)*(y-y0);
				for (int x=x1; x<=x5; x++)   
					{
					buf[xRow+x-x1]=colourRoute(buf[xRow+x-x1]);
					}
				}
			}	
	
		
		// try to mark points of greatest distance
		int maxdist2=maxArray(inBuf); //find distace away of furthest point
		for (int z=0; z<arraySize; z++)
			{	
			if (inBuf[z]>=maxdist2) //put a black x at the point of greatest distance
				{
				buf[z] = -16777215;
				if (z > xWidth)
					{
					buf[z-xWidth+1] = -16777215;
					buf[z-xWidth-1] = -16777215;
					}
				if (z <= arraySize-xWidth-2)
					{
					buf[z+xWidth+1] = -16777215;
					buf[z+xWidth-1] = -16777215;
					}
				}
			}
					
		buf[(y0-y1)*xWidth+x0-x1]=-16777215; // try to put a dot at start
				
		return buf;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int colourSideNumber(int c) 		
		{
		switch (c)			
			{
			case  -1: return -1;        //1 for same face
			case   0: return 0;	    //0 off net
			case   1: return -153;	    //2 for right
			case   2: return -10027161; //3 for up right
			case   3: return -52479;    //4 for up up right 
			case   4: return -52479;    //4 for left up up 
			case   5: return -52479;    //4 for left down down  
			case   6: return -52479;    //4 for down down right 
			case   7: return -10027161; //3 for down right
			case  11: return -10027161; //3 for right right 
			case  12: return -52479;    //4 for up right up 
			case  14: return -10053123; //5 for left up up left   
			case  15: return -10053123; //5 for left down down left 
			case  17: return -52479;    //4 down right down 
			case 101: return -153;	    //2 for up
			case 102: return -10027161; //3 for left up 
			case 103: return -52479;    //4 for left left up 
			case 104: return -52479;    //4 for down left left 
			case 105: return -52479;    //4 for down right right 
			case 106: return -52479;    //4 for right right up 
			case 107: return -10027161; //3 for right up
			case 111: return -10027161; //3 for up up 
			case 112: return -52479;    //4 for left up left 
			case 114: return -10053123; //5 for down left left down 
			case 115: return -10053123; //5 for down right right        
			case 117: return -52479;    //4 for right up right 
			case 201: return -153;	    //2 for left
			case 202: return -10027161; //3 for down left
			case 203: return -52479;    //4 for down down left 
			case 204: return -52479;    //4 for right down down 
			case 205: return -52479;    //4 for right up up 
			case 206: return -52479;    //4 for up up left
			case 207: return -10027161; //3 for up left
			case 211: return -10027161; //3 for left left 
			case 212: return -52479;    //4 for down left down 
			case 214: return -10053123; //5 for right down down 
			case 215: return -10053123; //5 for right up up right 
			case 217: return -52479;    //4 for up left up 
			case 301: return -153;	    //2 for down
			case 302: return -10027161; //3 for right down
			case 303: return -52479;    //4 for right right down 
			case 304: return -52479;    //4 for up right right 
			case 305: return -52479;    //4 for up left left 
			case 306: return -52479;    //4 for left left down 
			case 307: return -10027161; //3 for left down
			case 311: return -10027161; //3 for down down 
			case 312: return -52479;    //4 for right down right 
			case 314: return -10053123; //5 for up right right up 
			case 315: return -10053123; //5 for up left left 
			case 317: return -52479;    //4 for left down left 

			default:
			}				
		return 0;
		}	
///////////////////////////////////////////////////////////////////////////
				
	public int colourRoute(int c) 		
		{
		switch (c)			
			{
			case  -1: return -1;        //white for same face
			case   0: return 0;	   //off net
			case   1: return -13107;    //v light red for right
			case   2: return -13209;    //orange for up right
			case   3: return -153;	    //yellow for up up right 
			case   4: return -13500977; //off green for left up up 
			case   5: return -10053123; //medium blue for left down down  
			case   6: return -10027011; //medium cyan for down down right 
			case   7: return -3351041;  //light blue for down right
			case  11: return -13107;    //v light red for right right 
			case  12: return -13209;    //orange for up right up 
			case  14: return -13500977; //off green for left up up left   
			case  15: return -10053123; //medium blue for left down down left 
			case  17: return -3351041;  //light blue for down right down 
			case 101: return -51;       //v light yellow for up
			case 102: return -6750463;  //lime green for left up 
			case 103: return -10027161; //medium green for left left up 
			case 104: return -13378202; //darker green for down left left 
			case 105: return -3394563;  //darker purple for down right right 
			case 106: return -39321;    //medium red for right right up 
			case 107: return -26265;    //orange-brown for right up
			case 111: return -51;       //v light yellow for up up 
			case 112: return -6750463;  //lime green for left up left 
			case 114: return -13378202; //darker green for down left left down 
			case 115: return -3394563;  //darker purple for down right right        
			case 117: return -26265;    //orange-brown right up right 
			case 201: return -3342387;  //v light green for left
			case 202: return -6693428;  //grey cyan for down left
			case 203: return -10027011; //medium cyan for down down left 
			case 204: return -52225;    //purple for right down down 
			case 205: return -52479;    //v red for right up up 
			case 206: return -153;	   //yellow for up up left
			case 207: return -3342591;  //light yellowish green for up left
			case 211: return -3342387;  //v light green for left left 
			case 212: return -6693428;  //grey cyan for down left down 
			case 214: return -52225;    //purple for right down down 
			case 215: return -52479;    //v red for right up up right 
			case 217: return -3342591;  //light yellowish green for up left up 
			case 301: return -3342337;  //v light cyan for down
			case 302: return -26113;    //light purple for right down
			case 303: return -39321;    //medium red for right right down 
			case 304: return -39423;    //red/orange for up right right 
			case 305: return -6684927;  //yellowish green for up left left 
			case 306: return -10027161; //medium green for left left down 
			case 307: return -3355394;  //light blue/purple for left down
			case 311: return -3342337;  //v light cyan for down down 
			case 312: return -26113;    //light purple for right down right 
			case 314: return -39423;    //red/orange for up right right up 
			case 315: return -6684927;  //yellowish green for up left left 
			case 317: return -3355394;  //light blue/purple for left down left 

			default:
			}				
		return 0;
		}	

///////////////////////////////////////////////////////////////////////////
									
	public int distOpposite(int x0, int y0) // array showing smallest square of distance from starting point to opposite point across centre of cuboid 
		{		
		int face0=faceChoice(x0,y0);
		int testing=overMax;
		for (int rots=0; rots<4; rots++) // need to consider four different rotations
			{
			hh=h(face0,rots);
			ww=w(face0,rots);
			dd=d(face0,rots);
			int xStartTest=xS(face0,rots,x0,y0);
			int yStartTest=yS(face0,rots,x0,y0);
			int xOpp=ww+xStartTest;
			int yOpp=hh-yStartTest;
			testing=minisq(testing,(xOpp+dd-xStartTest),(yOpp-yStartTest));
			testing=minisq(testing,(yOpp-xStartTest),(-dd-xOpp-yStartTest));
			testing=minisq(testing,(-dd-ww-xOpp-xStartTest),(-ww-yOpp-yStartTest));
			testing=minisq(testing,(-dd-ww-xOpp-xStartTest),(2*hh+ww-yOpp-yStartTest));
			testing=minisq(testing,(hh-yOpp-xStartTest),(hh+dd+xOpp-yStartTest));
			}
		return testing;
		}
				
///////////////////////////////////////////////////////////////////////////
									
	public int sidesOpposite(int x0, int y0) // based on number of sides of shortest distance to opposite point 
		{		
		int face0=faceChoice(x0,y0);
		int testing=overMax;
                int sidesColour=0;
		int tested,xStartTest,yStartTest,xOpp,yOpp; 
		for (int rots=0; rots<4; rots++) // need to consider four different rotations
			{
			hh=h(face0,rots);
			ww=w(face0,rots);
			dd=d(face0,rots);
			xStartTest=xS(face0,rots,x0,y0);
			yStartTest=yS(face0,rots,x0,y0);
			xOpp=ww+xStartTest;
			yOpp=hh-yStartTest;
			tested=minisq(testing,(xOpp+dd-xStartTest),(yOpp-yStartTest)); //3 sides
			if (tested<testing)
				{
				testing=tested;
				sidesColour=-10027161; 
				}
			tested=minisq(testing,(yOpp-xStartTest),(-dd-xOpp-yStartTest)); //4 sides
			if (tested<testing)
				{
				testing=tested;
				sidesColour=-52479; 
				}
			tested=minisq(testing,(-dd-ww-xOpp-xStartTest),(-ww-yOpp-yStartTest)); //5 sides
			if (tested<testing)
				{
				testing=tested;
				sidesColour=-10053123;  
				}
			tested=minisq(testing,(-dd-ww-xOpp-xStartTest),(2*hh+ww-yOpp-yStartTest)); //5 sides
			if (tested<testing)
				{
				testing=tested;
				sidesColour=-10053123;  
				}
			tested=minisq(testing,(hh-yOpp-xStartTest),(hh+dd+xOpp-yStartTest)); //4 sides
			if (tested<testing)
				{
				testing=tested;
				sidesColour=-52479;
				}
			}
		return sidesColour;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int[] distColour(int[] distSq) //transform square of distance array to colours
		{		
		int[] buf = new int[arraySize];

		//adjust from squared distance to colour			
		double colourScale=256.0d;
		int colourJump=256;
		double rings=colourScale*heightVal/(1.0d*hScaled);				
		for (int z=0; z<arraySize; z++)
			{	
			double squareRoot1 = Math.sqrt(distSq[z]);
			buf[z] = colourJump*( (int) (-rings*squareRoot1) );		
			}

		// try to mark points of greatest distance
		int maxdist2=maxArray(distSq); //find distace away of furthest point
		for (int z=0; z<arraySize; z++)
			{	
			int ddd=distSq[z];
			if (ddd>=maxdist2) //put a black x at the point of greatest distance
				{
				buf[z] = -16777215;
				if (z > xWidth)
					{
					buf[z-xWidth+1] = -16777215;
					buf[z-xWidth-1] = -16777215;
					}
				if (z <= arraySize-xWidth-2)
					{
					buf[z+xWidth+1] = -16777215;
					buf[z+xWidth-1] = -16777215;
					}
				}
			if (ddd==0) // try to put a dot at start
				{
				buf[z]=0;
				}
			}
		return buf;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int maxArray(int[] arrayToMax) // find largest value in an array
		{
		int tempMax=0;
		for (int z=0; z<arraySize; z++)
			{
			if (arrayToMax[z]>tempMax)
				{
				tempMax=arrayToMax[z];
				}
			}
		return tempMax;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int mini (int a, int b)
		{
		if (a<b) 
			{
			return a;
			}
		return b;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public int minisq (int a, int b, int c) // produce lower of a versus b^2+c^2
		{
		int d=b*b+c*c;
		if (d<a) 
			{
			return d;
			}
		return a;
		}

///////////////////////////////////////////////////////////////////////////
				
	public int sqSide (int tRoute, int r, int v, int t, int b, int c) // produce b^2+c^2 and check route
		{
		int d=b*b+c*c;
		if (d<=t) 
			{
			return r*100+v;
			}
		return tRoute;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	public boolean action (Event evt, Object arg)  // handle choices from boxes and buttons 								
		{
		if (evt.target == uChoose) 
			{
			heightVal = Double.valueOf(heightBox.getText().trim()).doubleValue();
			widthVal  = Double.valueOf(widthBox.getText().trim()).doubleValue();
			depthVal  = Double.valueOf(depthBox.getText().trim()).doubleValue();
			scaleandCoord(heightVal,widthVal,depthVal,spaceControlX,spaceControlY,scaling);
			drawMap = new int[arraySize];

			//if (pattern == "distance from clicked point" || pattern=="sides used from clicked point")
			//	{
				startCorner=1;
			//	}	
			}		
		else if (evt.target == patternChoice) 
			{
  			pattern = (String) arg;
			}
		else if (evt.target == scaleChoice) 
			{
  			String scText = (String) arg;
			scaling = (int) Double.valueOf(scText).doubleValue();
			scaleandCoord(heightVal,widthVal,depthVal,spaceControlX,spaceControlY,scaling);
			//if (pattern == "distance from clicked point" || pattern=="sides used from clicked point")
			//	{
				startCorner=1;
				scaleChange=1;
			//	}	
			}
		else 
			{
			return false;  // event not handled		
			}	
		drawMap = redrawMap(); // create array now rather than in paint() so redrawing is quicker 
		repaint();
		return true;   //event handled
	 	}					
				
///////////////////////////////////////////////////////////////////////////
				
	public boolean mouseDown (Event evt, int x, int y) // identify point chosen for start
		{
		if ( (y>=y1 && y<=y4 && x>=x2 && x<=x3) || (y>=y2 && y<=y3 && x>=x1 && x<=x5) )
			{
			xStart = x;
			yStart = y;
			xEnd = xStart;
			yEnd = yStart;
			//startCorner=0;
			if (pattern == "distance from clicked point" || pattern=="sides used from clicked point" || pattern=="number of sides from clicked point")
				{
				drawMap = redrawMap();
				repaint();	
				return true;
				}
			}
		return false;
		}			
				
///////////////////////////////////////////////////////////////////////////
				
	public boolean mouseDrag (Event evt, int x, int y) 
		{
		//had thought of doing this
		//if ( (y>=y1 && y<=y4 && x>=x2 && x<=x3) || (y>=y2 && y<=y3 && x>=x1 && x<=x5) )
		//	{
		//	xEnd = x;
		//	yEnd = y;
		//	//startCorner=0;
		//	repaint();
		//	return true;
		//	}
		//else
		//	{
		//	return false;
		//	}
		// could instead have done as above
		if ( (y>=y1 && y<=y4 && x>=x2 && x<=x3) || (y>=y2 && y<=y3 && x>=x1 && x<=x5) )
			{
			xStart = x;
			yStart = y;
			xEnd = xStart;
			yEnd = yStart;
			//startCorner=0;
			if (pattern == "distance from clicked point" || pattern=="sides used from clicked point" || pattern=="number of sides from clicked point")
				{
				drawMap = redrawMap();
				repaint();	
				return true;
				}
			}
		return false;
		}
				
///////////////////////////////////////////////////////////////////////////
				
	} // end of class