Perspective Floor
<< MakeMaze | FinalProjectsTrailIndex | StoryTextApplet >>
See a working version of it here.
You can read about the math involved here.
This applet changes the the view in 2 of the three possible directions...maybe you can see how to make the distance to eye change with a key press yourself!
Tile.java
import java.awt.Color; import java.awt.Graphics; import java.awt.Rectangle; import java.util.ArrayList; /** * @author Chris Thiel, OFMCap * @version 16 May 2012 */ public class Tile { public static final int GAP=10, SIZE=55, LEFT=140, TOP=60; public static final int NORTH=0, SOUTH=1, EAST=2, WEST=3; private int visits, row, col; private Color color; private Rectangle box; public Tile(int r, int c) { setVisits(0); row=r; col=c; box = new Rectangle (LEFT+col*(SIZE+GAP), TOP+row*(SIZE+GAP), SIZE, SIZE); } public void draw(Graphics g) { Color oldColor=g.getColor(); g.setColor(color); g.fillRect(box.x, box.y, box.width, box.height); g.setColor(oldColor); } public void draw(Graphics g, Perspective p) { Color oldColor=g.getColor(); g.setColor(color); g.fillPolygon(p.convert(box)); g.setColor(oldColor); } public Rectangle getBox(){return box;} public int getRow(){return row;} public int getCol(){return col;} public int getVisits(){return visits;} public void setVisits(int i){ visits=i; if (visits%3==0) color=Color.YELLOW; if (visits%3==1) color=Color.GREEN; if (visits%3==2) color=Color.CYAN; } public void incVisits(){setVisits(visits+1);} public void setColor(Color c){color=c;} public Color getColor(){ return color;} public String toString(){ return String.format("[%2d]",row)+String.format("[%2d]",col); } }
Perspective.java
import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; /** * @author Chris Thiel, OFMCap * @version 16 May 2012 */ public class Perspective { private double eyeX, eyeY, eyeToImage, maxDist; public Perspective(double eyeX, double eyeY, double eyeToImage, double maxDist){ this.setEyeX(eyeX); this.setEyeY(eyeY); this.setEyeToImage(eyeToImage); this.setMaxDist(maxDist); } /** * converts a flat rectangle to a perspective polygon * @param r * @return */ public Polygon convert(Rectangle r){ Polygon result=new Polygon(); int left=r.x; int right=r.x+r.width; int top=r.y; int bottom=r.y+r.height; result.addPoint(imageX(left,top), imageY(0,top)); result.addPoint(imageX(right,top), imageY(0,top)); result.addPoint(imageX(right,bottom), imageY(0,bottom)); result.addPoint(imageX(left,bottom), imageY(0,bottom)); return result; } public int imageX(double x, double z){ double dx=this.eyeX-x; double D=(this.maxDist-z); return (int)(x+(dx*D/(this.eyeToImage+D))); } public int imageY(double y, double z){ double dy=this.eyeY-y; double D=(this.maxDist-z); return (int)(this.maxDist-y-(dy*D/(this.eyeToImage+D))); } public Polygon topView(Rectangle r){ Polygon result=new Polygon(); result.addPoint(r.x, r.y); result.addPoint(r.x+r.width, r.y); result.addPoint(r.x+r.width, r.y+r.height); result.addPoint(r.x, r.y+r.height); return result; } public double getEyeX() { return eyeX; } public void setEyeX(double eyeX) { this.eyeX = eyeX; } public double getEyeY() { return eyeY; } public void setEyeY(double eyeY) { this.eyeY = eyeY; } public double getEyeToImage() { return eyeToImage; } public void setEyeToImage(double eyeToImage) { this.eyeToImage = eyeToImage; } public double getMaxDist() { return maxDist; } public void setMaxDist(double maxDist) { this.maxDist = maxDist; } }
PerspectiveFloor.java
import java.applet.Applet; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class PerspectiveFloor extends Applet implements KeyListener { public static final int ROWS=8, COLS=8; public static final int WIDTH=800, HEIGHT=600; private Tile[][] floor; private Rectangle grout; private Tile currentTile; String message; private Image imgBuffer; private Graphics gBuffer; private Perspective view; /** * @author Chris Thiel, OFMCap * @version 16 May 2012 */ public void init() { imgBuffer = createImage(WIDTH,HEIGHT); gBuffer = imgBuffer.getGraphics(); this.addKeyListener(this); message="Click for focus, then press Arrow keys to move"; floor = new Tile[ROWS][COLS]; for (int r=0;r<ROWS;r++) for(int c=0;c<COLS;c++) floor[r][c]=new Tile(r,c); currentTile=floor[ROWS/2][COLS/2]; currentTile.setColor(Color.RED); grout = new Rectangle(Tile.LEFT-Tile.GAP, Tile.TOP-Tile.GAP, COLS*(Tile.SIZE+Tile.GAP)+Tile.GAP, ROWS*(Tile.SIZE+Tile.GAP)+Tile.GAP); view=new Perspective(WIDTH/2, HEIGHT/2, 900.0, HEIGHT/2); } public void paint(Graphics g) { gBuffer.setColor(Color.WHITE); gBuffer.fillRect(0, 0, WIDTH, HEIGHT); gBuffer.setColor(Color.BLACK); gBuffer.drawString(message, 20, 20); gBuffer.drawString("Arrows move position, 'A' and 'Z' raise and lower view", 20, 40); gBuffer.drawString("'<' and '>' move view horizontally", 20, 60); gBuffer.fillPolygon(view.convert(grout)); for (int r=0;r<ROWS;r++) for(int c=0;c<COLS;c++) floor[r][c].draw(gBuffer, view); g.drawImage(imgBuffer,0,0,this); } public void update(Graphics g) { paint(g); } @Override public void keyPressed(KeyEvent e) { int keyCode=e.getKeyCode(); int r=currentTile.getRow(); int c=currentTile.getCol(); Tile next=null; if(keyCode==38 && r>0){ //up next=floor[r-1][c]; } else if (keyCode==40 && r<ROWS-1){ //down next=floor[r+1][c]; } else if (keyCode==37 && c>0){ //left next=floor[r][c-1]; } else if (keyCode==39 && c<COLS-1){ //right next=floor[r][c+1]; } else if (keyCode==65 ){ //a -raise view view.setEyeY(view.getEyeY()+50); } else if (keyCode==90 ){ //z lower view view.setEyeY(view.getEyeY()-50); } else if (keyCode==44 ){ //'<' move view to left view.setEyeX(view.getEyeX()-50); } else if (keyCode==46 ){ //'>' move view to right view.setEyeX(view.getEyeX()+50); } if (next!=null ){ currentTile.incVisits(); currentTile=next; next.setColor(Color.RED); message="Current Tile: "+currentTile.toString(); } repaint(); } @Override public void keyReleased(KeyEvent e) {} @Override public void keyTyped(KeyEvent e) {} }