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) {}
}
