/** *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= 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) { // 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) { // 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) { 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() { } }