/** 
 *Copyright 1999 and 2000 Henry Bottomley (henry.bottomley@btinternet.com)
 *Created December 1999
 *Revised with more projections and more options January 2000
 *
 *Uses an idea from Chapter 11 of Java Game Programming for Dummies 1998 IDG Books 
 *related to the capture and scaling of raster images
 *
 *Further work needed:
 * protection against calculations which are either too precise or not precise enough
 * improve explanation of choices
 * optimisation and speed
 * pick up parameter from HTML
 */

import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.lang.*;

public class Projects extends Applet {
		
	protected Image     img;
	protected int[]     pixels, rpixels, ppixels;
	protected int       startx, starty, endx, endy, imgWidth, imgHeight, imgTotal;
	protected double    xx, xy, xz, yx, yy, yz, zx, zy, zz;
	protected double    eastVal, northVal, rotVal;
	protected String    eastText, northText, rotText;
	protected String    imgName;
	protected String    proJection;
	protected String    projCentre;
	protected String    stretchSpin;
	protected Choice    centreChoice = new Choice(); 
	protected Choice    projChoice = new Choice(); 
//	protected Choice    stspChoice = new Choice(); 
	protected TextField eastBox = new TextField(3);
	protected TextField northBox = new TextField(3);
	protected TextField rotBox = new TextField(3);
	protected Button    uChoose = new Button("You choose");
	static final double pi = Math.PI; 
	static final double pi180 = pi/180.0d; 
	static final int    spaceControl = 45; // space at top for controls
		
		
	public void init() {
		
		projChoice.addItem("Longitude Latitude");
		projChoice.addItem("Mercator");
		projChoice.addItem("Cylindrical Equal Area");
		projChoice.addItem("Mollweide");
		projChoice.addItem("Sinusoidal");
//		projChoice.addItem("Sin./Moll. average");
		projChoice.addItem("Azimuthal Equal Area");
		projChoice.addItem("Azimuthal Distance");
		projChoice.addItem("Azimuthal Orthographic");
		projChoice.addItem("Azimuthal Stereographic");
		projChoice.addItem("Azimuthal Gnomonic");
		projChoice.addItem("Conic Distance (fat)");
		projChoice.addItem("Conic Distance (thin)");
		projChoice.addItem("Triangle Equal Area");
		add(projChoice);
		proJection="Longitude Latitude";
		
		centreChoice.addItem("Standard");
		centreChoice.addItem("North pole");
		centreChoice.addItem("South pole");
		centreChoice.addItem("Pacific");
		centreChoice.addItem("Random");
		add(centreChoice);
		projCentre="Standard";
		
		add (eastBox); 
		add (northBox); 
		add (rotBox);
		
		add(uChoose);  

//		stspChoice.addItem("Stretch");
//		stspChoice.addItem("Spin");
//		add(stspChoice);
		stretchSpin="Stretch";

		////imgName = (String) getParameter("BASICIMAGE").trim(); // Does not work
		imgName="world.gif"; // note image is "Longitude Latitude" projection 
		MediaTracker tracker = new MediaTracker(this);
		img = getImage(getCodeBase(), imgName );
		tracker.addImage(img, 0);
		try { tracker.waitForAll(); }
		catch (InterruptedException e) { }
		endx = imgWidth = img.getWidth(null);  
		endy = imgHeight = img.getHeight(null);
		imgTotal = imgWidth * imgHeight;
		// Extract pixel data using PixelGrabber
		pixels = new int[imgTotal];   //pixels is array with size = number of pixels in original image
		PixelGrabber pg = new PixelGrabber(img, 0, 0, imgWidth, imgHeight, pixels, 0, imgWidth);
		try { pg.grabPixels(); }
		catch (InterruptedException e) { }
			
		xx=1.0d; yy=1.0d; zz=1.0d;
		xy=0.0d; xz=0.0d; yx=0.0d; yz=0.0d; zx=0.0d; zy=0.0d;
		rpixels = new int[imgTotal];
		ppixels = new int[imgTotal]; // transverse version of rpixels
		rpixels = standardMap(pixels,imgWidth,imgHeight);
		ppixels = transverseMap(pixels,imgWidth,imgHeight);

	}
		
		
	public void paint (Graphics g) {
		int[] drawMap = newmap(rpixels, ppixels, imgWidth, imgHeight, endx, endy, proJection);
		Image sImg = createImage(new MemoryImageSource(endx, endy, drawMap, 0, endx));
		 eastVal   = Math.atan2(xz,zz)/pi180;
		 northVal  = Math.asin(-yz)/pi180;
		 rotVal    = Math.atan2(yx,yy)/pi180;
		eastBox.setText(Long.toString(Math.round(eastVal)));
		northBox.setText(Long.toString(Math.round(northVal)));
		rotBox.setText(Long.toString(Math.round(rotVal)));
		g.drawImage(sImg, 0, spaceControl, null);
		g.setColor(Color.white);
		g.fillRect(0, 0, size().width, spaceControl);
		g.fillRect(0, endy +spaceControl, size().width, size().height - endy -spaceControl);
		g.fillRect(endx, spaceControl, size().width - endx, endy);
		g.setColor(Color.black);
		g.drawString("Projection",310,40);
		g.drawString("Centre",450,40);
		g.drawString("(East     North    Direction)",520,40);
//		g.drawString("Projection",280,40);
//		g.drawString("Centre",420,40);
//		g.drawString("(East     North    Direction)",490,40);
//		g.drawString("Mouse option",690,40);
	}
	
	public void update (Graphics g) {
		paint(g);
	}
	
		
	public boolean action (Event evt, Object arg) { // handle choices from boxes and buttons 
								
		if (evt.target == projChoice) {
			proJection = (String) arg;
			if (proJection=="Cylindrical Equal Area") {
				endx = imgWidth;
				endy = (int) (endx / pi);  //Start cylinder scaled correctly at equator
			}
			else if (proJection=="Azimuthal Distance" || proJection=="Azimuthal Equal Area") {
				endy = imgHeight;
				endx = endy;  // Basic border is square around circle
			}
			else if (proJection=="Conic Distance (fat)" || proJection=="Conic Distance (thin)") {
				endy = imgHeight;
				endx = (int) 4*endy/3; // to give an idea of cut and flattened cone
			}
			else {
				endx = imgWidth;
				endy = (int) endx/2; // 2x1 rectangle 
			}
		}
			
		else if (evt.target == centreChoice) {  
			projCentre = (String) arg;
			if (projCentre =="Standard"){
				rpixels=standardMap(pixels,imgWidth,imgHeight);
				ppixels=transverseMap(pixels,imgWidth,imgHeight);
				xx=1.0d; yy=1.0d; zz=1.0d; 
				xy=0.0d; xz=0.0d; yx=0.0d; yz=0.0d; zx=0.0d; zy=0.0d;
			}
			else if (projCentre == "Pacific") {
				rpixels=reverseMap(pixels,imgWidth,imgHeight);
				ppixels=transverseMap(rpixels,imgWidth,imgHeight); // note use of rpixels
				xx=1.0d; yy=-1.0d; zz=-1.0d;
				xy=0.0d; xz=0.0d; yx=0.0d; yz=0.0d; zx=0.0d; zy=0.0d;
			}
			else if (projCentre=="North pole") {
				rpixels=reverseMap(transverseMap(pixels,imgWidth,imgHeight),imgWidth,imgHeight);
				ppixels=standardMap(pixels,imgWidth,imgHeight);
				xx=1.0d; yy=0.0d; zz=0.0d;
				xy=0.0d; xz=0.0d; yx=0.0d; yz=-1.0d; zx=0.0d; zy=1.0d;
			}
			else if (projCentre=="South pole") {
				rpixels=transverseMap(pixels,imgWidth,imgHeight);
				ppixels=reverseMap(pixels,imgWidth,imgHeight);
				xx=1.0d; yy=0.0d; zz=0.0d;
				xy=0.0d; xz=0.0d; yx=0.0d; yz=1.0d; zx=0.0d; zy=-1.0d;
			}
			else if (projCentre == "Random") {  // random centre and rotation
				double randrotA = Math.random()*2.0d*pi;
				double randrotB = Math.random()*2.0d*pi;
				double randrotC = Math.random()*2.0d*pi;
				roTate (0.000001d, 0.000001d, randrotC ); // angle
				roTate (randrotA,  randrotB,  0.000001d); // centre 	 
				rpixels = obliqueMap(pixels,imgWidth,imgHeight,xx,xy,xz,yx,yy,yz,zx,zy,zz);
				ppixels = transverseMap(rpixels,imgWidth,imgHeight);
			}
			else {
				return false;  // event not handled
			}
		}
		
//		else if (evt.target == stspChoice) {
//			stretchSpin = (String) arg;
//		}
		
		else if (evt.target == uChoose) {
			eastVal  = Double.valueOf(eastBox.getText().trim()).doubleValue();
			northVal = Double.valueOf(northBox.getText().trim()).doubleValue();
			rotVal   = Double.valueOf(rotBox.getText().trim()).doubleValue();
			xx=1.0d; yy=1.0d; zz=1.0d;
			xy=0.0d; xz=0.0d; yx=0.0d; yz=0.0d; zx=0.0d; zy=-0.0d;
			roTate (0.0000001d,0.0000001d,rotVal*pi180-0.0000001d); // zeros seem to cause problems
			roTate (northVal*pi180-0.0000001d,eastVal*pi180-0.0000001d,0.0000001d);
			rpixels = obliqueMap(pixels,imgWidth,imgHeight,xx,xy,xz,yx,yy,yz,zx,zy,zz);
			ppixels = transverseMap(rpixels,imgWidth,imgHeight);
		}
		
		else {
			return false;  // event not handled
		}
		
		repaint();
		return true;   //event handled 
	}	
		
		
	public boolean mouseDown (Event evt, int x, int y) {
		if (stretchSpin=="Stretch") {
			endx = x;
			endy = y-spaceControl;
			if (proJection=="Azimuthal Distance" || proJection=="Azimuthal Equal Area") {
				endy = endx;  // Keep border as square around circle
				}
			else if (proJection=="Azimuthal Orthographic" || proJection=="Sinusoidal") {
				endy = (int) endx/2;  // Keep border as 2x1 rectangle
				}
			else if (proJection=="Conic Distance (fat)") {
				if (endy>endx) {
					endy = endx;
				}
				if (2*endy<endx) {
					endy = (int) endx/2;
				}
			}
			else if (proJection=="Conic Distance (thin)" && 2*endy<endx) {
				endy = (int) endx/2;
			}
			repaint();
			return true;
		}
		else if (stretchSpin=="Spin") {
			startx = x;
			starty = y;
			return true;
		}
		else {
			return false;
		}
	}	
		
	public boolean mouseDrag (Event evt, int x, int y) {
		if (stretchSpin=="Spin") {
			double polerot= 2.0d*pi*(startx-x)/endx; //rotation about poles
			double centrot= 2.0d*pi*(starty-y)/endy; //rotation about 0,0 off Togo
			roTate (0.0d, polerot, centrot);
			rpixels = obliqueMap(pixels,imgWidth,imgHeight,xx,xy,xz,yx,yy,yz,zx,zy,zz);
			ppixels = obliqueMap(pixels,imgWidth,imgHeight,xx,-xz,xy,yx,-yz,yy,zx,-zz,zy);
			repaint();
		}
		return mouseDown(evt, x, y);
	}
		
		
	public int[] newmap (int[] src1, int[] src2, int sWid, int sHyt, int dWid, int dHyt, String proJ) {
		// Return a scaled and newmapped version of the image data in either src1[] or src2[]
		int[] buf = new int[dWid * dHyt];			//buf is array with new scaled size
		double scaleX = (double) sWid / dWid;			//scaleX is old width divided by new width
		double scaleY = (double) sHyt / dHyt;			//scaleY is old height divided by new height
		double rectShape = (double) dHyt/dWid;
		
		if (proJ=="Longitude Latitude") { // rectangle
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				int sy = (int) (dy * scaleY);				//keep latitude 
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					int sx = (int) (dx * scaleX);			//keep longitude 
					buf[dRow + dx] = src1[sRow + sx];	//find corresponding old pixel
				}// end of dx for loop
			}//end of dy for loop
		} // end of LongLat if
		
		else if (proJ=="Mercator") {  // conformal rectangle off-edge
			double scaleM=rectShape*pi;
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;				// down (up) distance from centre of display
				int sy = (int) ( 1.0d * sHyt * ( 2.0d*Math.atan(Math.exp(py*scaleM))/pi ) );  //for mercator
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					int sx = (int) (dx * scaleX);			//keep longitude  
					buf[dRow + dx] = src1[sRow + sx]; 	//find corresponding old pixel
				}// end of dx for loop
			}//end of dy for loop
		} // end of Mercator if
		
		else if (proJ=="Cylindrical Equal Area") { // equal area rectangle
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;				// down (up) distance from centre of display
				int sy = (int) (1.0d * sHyt * (Math.asin(py)/pi + 0.5d) ); //for cylindrical
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					int sx = (int) (dx * scaleX);			//keep longitude 
					buf[dRow + dx] = src1[sRow + sx];	//find corresponding old pixel
				}// end of dx for loop
			}//end of dy for loop
		} // end of Cylindrical if 
		
		else if (proJ=="Sinusoidal") {  // equal area
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
 				int sy = (int) (dy * scaleY);				//longlat or sinusoidal 
				double py = 2.0d*dy/dHyt-1.0d; 				// down (up) distance from centre of display
				double cosy = Math.cos(py*pi/2.0d);			// for sinusiodal projection	 		
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;			//right (left) distance from centre of display
					if (px<cosy && px>-cosy) {			// inside sinusoid
						int sx = (int) (0.5d * sWid * (1.0d + px/cosy));  //work out corresponding sinusoidal old value for row 
						buf[dRow + dx] = src1[sRow + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;       // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Sinusoidal if 
		
		else if (proJ=="Mollweide") {  // equal area ellipse horizontal lat
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                        // down (up) distance from centre of display
				double opy = Math.sqrt(1.0d-py*py);	
				int sy =(int) (1.0d*sHyt*(Math.asin(2.0d*(Math.asin(py)+py*opy)/pi)/pi+0.5d));
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;                  //right (left) distance from centre of display
					if (px<opy && px>-opy) {                             // inside limiting ellipse
						int sx = (int) (0.5d*sWid*(px/opy+1.0d));      //
						buf[dRow + dx] = src1[sRow + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;      // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Mollweide if
		
/*		else if (proJ=="Sin./Moll. average") {  // 
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                        // down (up) distance from centre of display
				double opy = Math.sqrt(1.0d-py*py);	//for Mollewide
				double cosy = Math.cos(py*pi/2.0d);	// for sinusiodal projection	 		
				double cosopy = 0.5d*(opy+cosy);		  //does this preserve areas?
				int sy =(int) (0.5d*dy*scaleY + 0.5d*sHyt*(Math.asin(2.0d*(Math.asin(py)+py*opy)/pi)/pi+0.5d));
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;			//right (left) distance from centre of display
					if (px<cosopy && px>-cosopy) {
						int sx = (int) (0.5d*sWid*(px/cosopy+1.0d));  //work out average corresponding value for row 
						buf[dRow + dx] = src1[sRow + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;       // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Sin./Moll. average if 
*/		
		else if (proJ=="Azimuthal Distance") { //azimuthal circle
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                        // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;                  //right (left) distance from centre of display
					double pz=Math.sqrt(px*px+py*py);             // Pythagorean distance from centre
					if (pz < 1.0d) {                              // inside limiting circle
						int sx = (int) (0.5d*sWid*(Math.atan2(px,py)/pi + 1.0d) );      //polar angle 
						int sy = (int) (1.0d*sHyt*pz);        //polar distance
						buf[dRow + dx] = src2[sy * sWid + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;      // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Azimuthal Distance if
		
		else if (proJ=="Azimuthal Equal Area") {  //azimuthal circle equal area
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                        // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;                  //right (left) distance from centre of display
					double pz2=px*px+py*py;                       // Pythagorean distance from centre
					if (pz2 < 1.0d) {                              // inside limiting circle
						int sx = (int) (0.5d*sWid*(Math.atan2(px,py)/pi + 1.0d) );      //polar angle 
						int sy = (int) (1.0d*sHyt*Math.acos(1.0d-2.0d*pz2)/pi);//polar equal area 
						buf[dRow + dx] = src2[sy * sWid + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;       // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Azimuthal Equal Area if
		
		else if (proJ=="Azimuthal Orthographic") {  // azimuthal circle hemisphere x2 parallel views from great distance
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                         // down (up) distance from centre of display
				int dRow = dy * dWid;
				int dRow1 = (dy+1) * dWid - 1;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=4.0d*dx/dWid-1.0d;                   //right (left) distance from centre of display
					double pz=Math.sqrt(px*px+py*py);              // Pythagorean distance from centre
					if (pz < 1.0d) {                               // inside limiting circle
						int sx = (int) (0.5d*sWid*(Math.atan2(px,py)/pi + 1.0d) );      //polar angle 
						int sy = (int) (1.0d*sHyt*Math.asin(pz)/pi);//polar parallel view 
						buf[dRow + dx] = src2[sy * sWid + sx]; //find corresponding old pixel
						buf[dRow1 - dx] = src2[(sHyt-sy -1) * sWid + sx]; //other hemisphere
					}
					else {
						buf[dRow + dx] = -16777215;        // black for off edge
						buf[dRow1 - dx] = -16777215;// black for off edge other hemisphere
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of PolarOrthograhic if 
		
		else if (proJ=="Azimuthal Stereographic") {  // azimuthal off-edge projection from opposite pole to plane touching pole
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                          // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=(2.0d*dx-dWid)/dHyt;                  //right (left) distance from centre of display keep circles
					double pz=Math.sqrt(px*px+py*py);               // Pythagorean distance from centre
					int sx = (int) (0.5d*sWid*(Math.atan2(px,py)/pi + 1.0d) );      //polar angle 
					int sy = (int) (2.0d*sHyt*Math.atan(scaleX*pz)/pi); // Stereographic scaling
					buf[dRow + dx] = src2[sy * sWid + sx];   //find corresponding old pixel
				}// end of dx for loop
			}//end of dy for loop
		} // end of Azimuthal Stereographic if 
		
		else if (proJ=="Azimuthal Gnomonic") {  //azimuthal hemisphere off-edge projection of hemishere from centre to plane touching pole
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*dy/dHyt-1.0d;                           // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=(2.0d*dx-dWid)/dHyt;                   //right (left) distance from centre of display keep circles
					double pz=Math.sqrt(px*px+py*py);                // Pythagorean distance from centre
					int sx = (int) (0.5d*sWid*(Math.atan2(px,py)/pi + 1.0d) );      //polar angle 
					int sy = (int) (1.0d*sHyt*Math.atan(scaleX*pz)/pi);//polar projection 2 
					buf[dRow + dx] = src2[sy * sWid + sx];    //find corresponding old pixel
				}// end of dx for loop
			}//end of dy for loop
		} // end of Azimuthal Gnomonic if
		
		else if (proJ=="Triangle Equal Area") {  // simple version of little practical use 
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 1.0d*dy/dHyt;                            // down distance from top of display
				int sy = (int) (1.0d * sHyt * Math.acos(1.0d-2.0d*py*py) / pi );
				int sRow = sy * sWid;
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;                  //right (left) distance from centre of display
					if (py > Math.abs(px)) {                             // inside triangle
						int sx = (int) (0.5d*sWid*(px/py+1.0d));      //
						buf[dRow + dx] = src1[sRow + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;      // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Triangle if

		else if (proJ=="Conic Distance (fat)") { //based on azimuthal circle
			double cutang = pi-Math.acos(2.0d*rectShape-1.0d);
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 2.0d*(dy-dHyt)/dWid + 1.0d;    // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=2.0d*dx/dWid-1.0d;                  //right (left) distance from centre of display
					double pz=Math.sqrt(px*px+py*py);             // Pythagorean distance from centre
					double pang=Math.atan2(px,py);
					if (pz < 1.0d && pang < cutang && pang > -cutang) {     // inside limits
						int sx = (int) (0.5d*sWid*(pang/cutang + 1.0d) ); //polar angle 
						int sy = (int) (1.0d*sHyt*pz);        //polar distance
						buf[dRow + dx] = src1[sy * sWid + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;      // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Conic Distance if

		else if (proJ=="Conic Distance (thin)") { //based on azimuthal circle
			double cutang = Math.asin(0.5d/rectShape);
			if (dWid>=2*dHyt) {
				cutang = pi/2.0d;  // just in case
			}
			for (int dy = dHyt; --dy>= 0; ) {				//cycle through each new column
				double py = 1.0d*dy/dHyt;    // down (up) distance from centre of display
				int dRow = dy * dWid;
				for (int dx = dWid; --dx >= 0; ) {			//cycle through each new row
					double px=(1.0d*dx-0.5d*dWid)/dHyt;                  //right (left) distance from centre of display
					double pz=Math.sqrt(px*px+py*py);             // Pythagorean distance from centre
					double pang=Math.atan2(px,py);
					if (pz < 1.0d && pang < cutang && pang > -cutang) {    // inside limits
						int sx = (int) (0.5d*sWid*(pang/cutang + 1.0d) ); //polar angle 
						int sy = (int) (1.0d*sHyt*pz);        //polar distance
						buf[dRow + dx] = src1[sy * sWid + sx]; //find corresponding old pixel
					}
					else {
						buf[dRow + dx] = -16777215;      // black for off edge
					}
				}// end of dx for loop
			}//end of dy for loop
		} // end of Conic Distance if

									
		return buf;  // send back new array of image
	} // end of newmap method
		
		
	public int[] standardMap (int[] src, int sWid, int sHyt) {
		return src;
	} // end of standardMap method
		
	public int[] reverseMap (int[] src, int sWid, int sHyt) {
		int[] buf = new int[sWid*sHyt];
		int sTot   = sWid * sHyt;
		int halfsW = (int) sWid/2;
		int sTmhW  = sTot - halfsW;
		for (int i = sTmhW; --i >= 0; ) {
			buf[i + halfsW] = src[sTot - i - 1];
		}	
		for (int i = halfsW; --i >= 0; ) {
			buf[i] = src[sTmhW - i - 1];
		}	
		return buf;  // send back array 
	} // end of reverseMap method
			
	public int[] transverseMap (int[] src, int sWid, int sHyt) {
		return obliqueMap (src, sWid, sHyt, 1.0d,0.0d,0.0d,0.0d,0.0d,1.0d,0.0d,-1.0d,0.0d); 
	} // end of transverseMap method

	public int[] transverseMap2 (int[] src, int sWid, int sHyt) {
		return reverseMap (transverseMap (src, sWid, sHyt),sWid, sHyt);
	} // end of transverseMap method
		
	public int[] obliqueMap (int[] src, int sWid, int sHyt, double xX, double xY, double xZ, double yX, double yY, double yZ, double zX, double zY, double zZ) {
		int[] buf = new int[sWid*sHyt];
		double sHpied = 1.0d*sHyt/pi; 
		double sWpied = 1.0d*sWid/pi; 
		for (int ypix = sHyt; --ypix >= 0; ) {
			int    yrow = ypix * sWid;
			double yang = 1.0d*ypix/sHpied;
			double ysp  = Math.cos(yang);
	 		double yssp = Math.sin(yang);
			for (int xpix = sWid; --xpix >= 0; ) {                       
				double xang = 2.0d*xpix/sWpied;
				double xsp  = Math.sin(xang)*yssp;
				double zsp  = Math.cos(xang)*yssp;
				double xspN = xsp*xX+ysp*xY+zsp*xZ;
				double yspN = xsp*yX+ysp*yY+zsp*yZ;
				double zspN = xsp*zX+ysp*zY+zsp*zZ;
				double xspT = 1.0d - Math.atan2(xspN,-zspN) / pi ;
				int xpixN = (int) (0.5d * sWid * xspT);  
				int ypixN = (int) (1.0d * sHpied * Math.acos(yspN) );  
				buf[yrow + xpix] = src[ypixN * sWid + xpixN];
			}	// end of xpix for
		}		// end of ypix for
		return buf;	// send back array 
	} // end of obliqueMap method

		
	public void roTate (double xr, double yr, double zr) {// rotate around x, y and z axes in that order
		
		double xcos= Math.cos(xr);
		double xsin= Math.sin(xr);
		double ycos= Math.cos(yr);
		double ysin= Math.sin(yr);
		double zcos= Math.cos(zr);
		double zsin= Math.sin(zr);
		                 
		double yxR = yx *xcos - zx *xsin;
		double yyR = yy *xcos - zy *xsin;
		double yzR = yz *xcos - zz *xsin;
		double zxR = zx *xcos + yx *xsin;
		double zyR = zy *xcos + yy *xsin;
		double zzR = zz *xcos + yz *xsin;
		
		double xxR = xx *ycos + zxR*ysin;
		double xyR = xy *ycos + zyR*ysin;
		double xzR = xz *ycos + zzR*ysin;
		zx         = zxR*ycos - xx *ysin;
		zy         = zyR*ycos - xy *ysin;
		zz         = zzR*ycos - xz *ysin;
		       
		xx         = xxR*zcos - yxR*zsin;
		xy         = xyR*zcos - yyR*zsin;
		xz         = xzR*zcos - yzR*zsin;
		yx         = yxR*zcos + xxR*zsin;
		yy         = yyR*zcos + xyR*zsin;
		yz         = yzR*zcos + xzR*zsin;
		
		return;
	}	
		
		
	public void destroy() {
	}
}



