//************************************************************************ // You are free to copy this source and use it in any way you deem fit //************************************************************************ // 3D globe class. to create 2D games on a 3 D grid // // Program: Globe class // Programmer: H K Gupta Date: Someday in March 1998 //************************************************************************ import java.awt.*; import java.lang.Math; public class HridayeshGlobe extends Canvas { int globeLevel = -1; // Icon File related variables private Image[] icons; private Graphics iconG; private boolean iconsCopied = false; private int noOfIcons = 16; MediaTracker tracker; private Image iconImages; public HridayeshGlobe(int level, Image iconImages) { super(); this.iconImages = iconImages; tracker = new MediaTracker(this); tracker.addImage(this.iconImages, 0); pntX = new double[maxPoints]; pntY = new double[maxPoints]; pntZ = new double[maxPoints]; dblX = new double[maxPoints]; dblY = new double[maxPoints]; dblZ = new double[maxPoints]; intX = new int[maxPoints]; intY = new int[maxPoints]; intZ = new int[maxPoints]; pntNeighbour = new int[maxPoints][6]; globeInit(maxPoints); setGlobeLevel(level); } void globeInit(int maxPoints) { // method to be overridden by subclass } void globeCreate() { // method to be overridden by subclass } void globeInitDisplayPoint(Graphics o, int point) { // method to be overridden by subclass } void globeDisplayPoint(Graphics o, int point) { // method to be overridden by subclass } void globePointSelected(int point, int mask) { // method to be overridden by subclass } void globeFinalUpdate(Graphics o, int xCenter, int yCenter) { // method to be overridden by subclass } void globeJustRotated() { // method to be overridden by subclass } boolean globeMouseAllowed() { // method to be overridden by subclass return true; } private void iconsCopy() { icons = new Image[noOfIcons]; for(int i = 0; i < noOfIcons; i++) { icons[i] = createImage(16, 16); iconG = icons[i].getGraphics(); iconG.drawImage(iconImages, - ((16 + 2) * i), 0, this); } } public void setGlobeLevel(int level) { // restrict no. of point to 4 <> 1026 drawBackground = true; int temp; temp = level; if(temp < 1) temp = 1; if(temp > 4) temp = 4; if(globeLevel != -1) { globeLevel = temp; createGrid(); repaint(); } globeLevel = temp; } boolean repaintInProgress = false; public void update(Graphics g) { if(repaintInProgress) return; if(!iconsCopied) { if(!tracker.checkAll()) { return; // wait for image to load } iconsCopy(); iconsCopied = true; } repaintInProgress = true; if(!bufferSet) { setBuffers(); createGrid(); } if(drawBackground) { o.drawImage(backGroundImage, 0, 0, this); drawBackground = false; } drawGrid(); globeFinalUpdate(o, xCenter, yCenter); g.drawImage(offImage, 0, 0, this); repaintInProgress = false; } private boolean drawBackground = true; private Image backGroundImage; // for backGround private Image offImage; private Graphics o; private boolean bufferSet = false; private int xOff, yOff, diameter, radius, xCenter, yCenter; private void setBuffers() { Graphics b; Dimension d = size(); offImage = createImage(d.width, d.height); o = offImage.getGraphics(); backGroundImage = createImage(d.width, d.height); b = backGroundImage.getGraphics(); calculateOffsets(); b.setColor(Color.black); b.fillRect(0, 0, d.width, d.height); b.setColor(Color.white); b.fillOval(xOff - 5, yOff - 5, diameter, diameter); drawArrows(b); bufferSet = true; } // Left LTop LBot Right RTop RBot Top Bottom private int[] arrType = { 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2}; private int[] arrXPos = { 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0}; private int[] arrYPos = { 0, 0, 1, 1, -1, -1, 0, 0, 1, 1, -1, -1, 1, 1, -1, -1}; private int[] arrXOff = { 1, 1, -2, -1, -2, -1, -1, -1, 2, 1, 2, 1, -6, 6, -6, 6}; private int[] arrYOff = { 2, -2, -2, -1, 2, 1, 2, -2, -2, -1, 2, 1, -1, -1, 1, 1}; private int[] arrXRot = { 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0}; private int[] arrYRot = { 0, 0, 1, 1, -1, -1, 0, 0, 1, 1, -1, -1, 1, 1, -1, -1}; private int arrGap = 20; private void drawArrows(Graphics b) { int x, y; for(int i = 0; i < 16; i++) { x = xCenter + arrXPos[i] * radius + arrXOff[i] * arrGap; y = yCenter - (arrYPos[i] * radius + arrYOff[i] * arrGap); b.drawImage(icons[i], x - 8, y - 8, this); } } public boolean mouseDown(Event e, int xIn, int yIn) { if(!globeMouseAllowed()) return true; int x, y; for(int i = 0; i < 16; i++) { x = xCenter + arrXPos[i] * radius + arrXOff[i] * arrGap; y = yCenter - (arrYPos[i] * radius + arrYOff[i] * arrGap); if(((xIn - x) * (xIn - x) + (yIn - y) * (yIn - y)) < 16) { if(repaintInProgress) return true; repaintInProgress = true; currAngleOne = incrAngle(currAngleOne, arrXRot[i] * arrType[i] * 10); currAngleTwo = incrAngle(currAngleTwo, arrYRot[i] * arrType[i] * 10); transformGrid(); globeJustRotated(); drawBackground = true; repaintInProgress = false; repaint(); return true; } } for(int i = 0; i < nPoints; i++) { // look for grid point selection if(intZ[i] >= 0) { x = xIn - intX[i]; y = yIn - intY[i]; if((x * x + y * y) < 16) { globePointSelected(i, e.modifiers & Event.META_MASK); return true; } } } return false; } private void calculateOffsets() { Dimension d = size(); if(d.width > d.height) { xOff = 1 + (d.width - d.height) / 2; yOff = 1; } else { xOff = 1; yOff = 1 + (d.height - d.width) / 2; } diameter = d.width - 2 * xOff; radius = diameter / 2; xCenter = xOff + radius; yCenter = yOff + radius; xOff += 5; yOff += 5; radius -= 5; } public void paint(Graphics g) { update(g); } public void rotate() { if(!iconsCopied) { repaint(); return; } if(repaintInProgress) return; repaintInProgress = true; currAngleOne = incrAngle(currAngleOne, 10); currAngleTwo = incrAngle(currAngleTwo, 10); transformGrid(); globeJustRotated(); drawBackground = true; repaintInProgress = false; repaint(); } private int incrAngle(int a, int incr) { if((a + incr) >= 360) return a + incr - 360; if((a + incr) < -360) return a + incr + 360; return a + incr; } private void drawGrid() { int k; for(int i = 0; i < nPoints; i++) { if(intZ[i] >= 0) globeInitDisplayPoint(o, i); } for(int i = 0; i < nPoints; i++) { if(intZ[i] >= 0) { for(int j = 0; j < 6; j++) { k = pntNeighbour[i][j]; if(k > i) { if(intZ[k] >= 0) { globeDrawLine(o, i, k); } } } } } for(int i = 0; i < nPoints; i++) { if(intZ[i] >= 0) globeDisplayPoint(o, i); } } void globeDrawLine(Graphics o, int i, int j) { o.setColor(Color.red); if((intZ[i] >= 0) && (intZ[j] >=0)) o.drawLine(intX[i], intY[i], intX[j], intY[j]); } int currAngleOne = 0; int currAngleTwo = 0; private void transformGrid() { double x, y, z; double r, a; // radius, final angle for(int i = 0; i < nPoints; i++) { x = dblX[i]; y = dblY[i]; z = dblZ[i]; r = Math.sqrt((double)(x * x + z * z)); a = getNewAngle(x, r, z, currAngleOne); x = r * Math.cos(a); z = r * Math.sin(a); r = Math.sqrt((double)(y * y + z * z)); a = getNewAngle(y, r, z, currAngleTwo); dblX[i] = x; dblY[i] = r * Math.cos(a); dblZ[i] = r * Math.sin(a); intX[i] = xCenter + (int)dblX[i]; intY[i] = yCenter - (int)dblY[i]; intZ[i] = (int)dblZ[i]; } currAngleOne = 0; currAngleTwo = 0; } private double getNewAngle(double point, double r, double dir, int delta) { double t; if(r == 0.0) { // avoid zero divide t = 0.0; } else { t = Math.acos((double)point / r); } if(dir < 0) t = - t; return angle(t, delta); } private double angle(double a, int rot) { // add angle in radian to angle in degrees if(rot == 0) return a; double retValue = a + ((double)(rot)) * Math.PI / 180.0; if(retValue > Math.PI) return retValue - 2 * Math.PI; return retValue; } int nPoints = 6; private int[][] pointInit = { // X Y Z { 100, 0, 0}, { -100, 0, 0}, { 0, 100, 0}, { 0, -100, 0}, { 0, 0, 100}, { 0, 0, -100} }; // for calculating the grid private double[] pntX; private double[] pntY; private double[] pntZ; // for calculating the rotation private double[] dblX; private double[] dblY; private double[] dblZ; // final position after rotation int[] intX; int[] intY; int[] intZ; int[][] pntNeighbour; private int maxPoints = 1026; private int oldCalculatedLevel = -2; // I am sick and tired of setting variables to -1 private void createGrid() { if(globeLevel != oldCalculatedLevel) { nPoints = 6; currAngleOne = 0; currAngleTwo = 0; for(int i = 0; i < nPoints; i++) { pntX[i] = pointInit[i][0] * radius / 100; pntY[i] = pointInit[i][1] * radius / 100; pntZ[i] = pointInit[i][2] * radius / 100; } findNeighbours(); calculateAllPoints(); } oldCalculatedLevel = globeLevel; for(int i = 0; i < nPoints; i++) { dblX[i] = pntX[i]; dblY[i] = pntY[i]; dblZ[i] = pntZ[i]; } for(int i = 0; i < nPoints; i++) { // move to integer co-ordinates intX[i] = xCenter + (int)dblX[i]; intY[i] = yCenter - (int)dblY[i]; intZ[i] = (int)dblZ[i]; } globeCreate(); } private void calculateAllPoints() { int currPoint; for(int i = 0; i < globeLevel; i++) { currPoint = nPoints; for(int j = 0; j < nPoints; j++) { for(int k = 0; k < 6; k++) { if(pntNeighbour[j][k] > j) { calculateMidPoint(j, pntNeighbour[j][k], currPoint); currPoint++; } } } nPoints = currPoint; findNeighbours(); } } private void calculateMidPoint(int i, int j, int index) { double xC = (pntX[i] + pntX[j]) / 2; double yC = (pntY[i] + pntY[j]) / 2; double zC = (pntZ[i] + pntZ[j]) / 2; double rCenter = Math.sqrt((double)(xC * xC + yC * yC + zC * zC)); double beta = Math.asin(zC / rCenter); // angle from x-y plane // next line contains a range to make sure that the argument remains <= 1 // Why is that required. Well to cut a long story short it is happening because // of round off errors double cosBeta = Math.cos(beta); // trying to speed up the things double temp = xC / ( rCenter * Math.abs(cosBeta)); if (temp < - 1.0) temp = -1.0; if (temp > 1.0) temp = 1.0; double alpha = Math.acos(temp); if (yC < 0) alpha = - alpha; pntX[index] = radius * Math.cos(alpha) * cosBeta; pntY[index] = radius * Math.sin(alpha) * cosBeta; pntZ[index] = radius * Math.sin(beta); } private void findNeighbours() { double x, y, z; double[] sortArr = new double[nPoints]; double highVal = 1000.0 * 1000.0; // should be greated than radius * radius for(int i = 0; i < nPoints; i++) { for(int j = 0; j < nPoints; j++) { if (i != j) { // avoid being neighbour of yourself. you dummy! x = pntX[i] - pntX[j]; y = pntY[i] - pntY[j]; z = pntZ[i] - pntZ[j]; sortArr[j] = x * x + y * y + z * z; } else { sortArr[j] = highVal; // high value } } for(int k = 0; k < 6; k++) { int selIndex = 0; for(int n = 1; n < nPoints; n++) { if(sortArr[n] < sortArr[selIndex]) selIndex = n; } pntNeighbour[i][k] = selIndex; sortArr[selIndex] = highVal; // move high value } if(i < 6) { // first six points can have only four neighbours pntNeighbour[i][4] = -1; pntNeighbour[i][5] = -1; } } } }