package vooga.rts.util;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import util.Location;
import vooga.rts.gui.Window;
/**
* This Camera is used to draw objects with the correct perspective.
* Since this game engine supports the Isometric View angle, the Camera
* is here to support the transformations between world and view coordinates. <li>
* View Coordinates are what are drawn on the screen, with (0,0) being the top left of the screen
* and (max X, max Y) being the bottom right of the screen.</li> <li>
* World Coordinates are the actual coordinates in the game's model. This is the location that each
* entity in the game knows about and all simulation is done with.</li> The camera also provides the
* functionality to check if a particular entity is visible
* before it is painted. This helps in performance by only drawing the objects that are required.
*
* @author Jonathan Schmidt
*
*
*/
public class Camera {
public static final double ISO_WIDTH = 1.0;
public static final double ISO_HEIGHT = 0.5;
public static final double MOVE_SPEED = Window.SCREEN_SIZE.getWidth() / 4;
public static final double VISION = 100.0;
private static Camera myInstance;
private Dimension myScreenSize;
private Dimension myMapSize;
private Shape myWorldVision;
private Location3D myWorldCenter;
public Camera (Location3D playerLoc) {
myWorldCenter = new Location3D(playerLoc);
myScreenSize = Window.SCREEN_SIZE;
updateWorldSize();
}
public void setMapSize (Dimension mapSize) {
myMapSize = mapSize;
updateWorldSize();
}
/**
* Converts a world location into a view location.
* This is used to convert something that is on the map
* into the coordinates for painting on the screen.
*
* @param world
* @return
*/
public Point2D worldToView (Location3D world) {
double x = (world.getX() - myWorldCenter.getX()) * ISO_WIDTH;
x += -(world.getY() - myWorldCenter.getY()) * ISO_WIDTH;
x += myScreenSize.getWidth() / 2;
double y = (world.getX() - myWorldCenter.getX()) * ISO_HEIGHT;
y += (world.getY() - myWorldCenter.getY()) * ISO_HEIGHT;
y += -world.getZ();
y += myScreenSize.getHeight() / 2;
return new Location(x, y);
}
/**
* Converts view location to the world location.
* This turns something that is drawn on the screen into a place
* on the game map.
*
* @param view
* @return
*/
public Location3D viewtoWorld (Point2D view) {
double Zvalue = myWorldCenter.getZ();
double x = (ISO_HEIGHT * view.getX()) + view.getY() + Zvalue;
x += -(myScreenSize.getHeight() * ISO_HEIGHT);
x += -(myScreenSize.getWidth() * ISO_HEIGHT * ISO_HEIGHT);
x += myWorldCenter.getX();
double y = view.getY() + myWorldCenter.getY() + Zvalue;
y += -myScreenSize.getHeight() * ISO_HEIGHT;
y += -view.getX() * ISO_HEIGHT;
y += myScreenSize.getWidth() * (ISO_HEIGHT * ISO_HEIGHT);
return new Location3D(x, y, Zvalue);
}
/**
* This turns a rectangle in view coordinates (as drawn on the screen)
* into a shape that represents the same area in the world.
*
* @param view The rectangle in view coordinates.
* @return The Shape that is represented by the rectangle in the world.
*/
public Shape viewtoWorld (Shape view) {
PathIterator rect = view.getPathIterator(null);
Polygon world = new Polygon();
double[] points = new double[6];
while (!rect.isDone()) {
rect.currentSegment(points);
Location3D toWorld = viewtoWorld(new Point2D.Double(points[0], points[1]));
world.addPoint((int) toWorld.getX(), (int) toWorld.getY());
rect.next();
}
return world;
}
private Location3D deltaviewtoWorld (Point2D delta) {
double Zvalue = 0;
double x = (ISO_WIDTH * delta.getX()) + delta.getY() + Zvalue;
double y = delta.getY() + Zvalue - delta.getX() * ISO_HEIGHT;
return new Location3D(x, y, Zvalue);
}
/**
* Returns whether the specified location is visible by the camera or not.
* This location is in view coordinates.
*
* @param screen The location in view coordinates.
* @return Whether it is visible or not.
*/
public boolean isVisible (Point2D screen) {
return true;
/*
* return !(screen.getX() < -VISION || screen.getY() < -VISION ||
* screen.getX() > myScreenSize.getWidth() + VISION || screen.getY() > myScreenSize
* .getHeight() + VISION);
*/
}
/**
* Returns whether the specified location is visible by the camera or not.
* It estimates based on the world location of the object and of the camera
* This is a pretty inaccurate check and it is recommended to use the other
* isVisible method to validate with a Screen Location.
*
* @param world The world location of the object.
* @return Whether it is visible or not.
*/
public boolean issVisible (Location3D world) {
Location3D temp = new Location3D(world);
temp.negate();
temp.add(myWorldCenter);
return !(temp.getX() < -myScreenSize.getWidth() ||
temp.getY() < -myScreenSize.getHeight() ||
temp.getX() > myScreenSize.getWidth() + VISION || temp.getY() > myScreenSize
.getHeight());
}
public boolean isVisible (Location3D world) {
return myWorldVision.contains(world.to2D());
}
public void paint (Graphics2D pen) {
double x = myScreenSize.getWidth() / 2;
double y = myScreenSize.getHeight() / 2;
double height = 10;
double width = 10;
// pen.setStroke();
Polygon p = new Polygon();
p.addPoint((int) (x - width / 2), (int) y);
p.addPoint((int) x, (int) y);
p.addPoint((int) (x + width / 2), (int) y);
p.addPoint((int) x, (int) y);
p.addPoint((int) x, (int) (y - height / 2));
p.addPoint((int) x, (int) y);
p.addPoint((int) x, (int) (y + height / 2));
p.addPoint((int) x, (int) y);
pen.draw(p);
// System.out.println(myWorldCenter);
// pen.fill(new Ellipse2D.Double(x - width / 2, y - width / 2, width, height));
}
public static Camera instance () {
if (myInstance == null) {
myInstance = new Camera(new Location3D(VISION, VISION, 0));
}
return myInstance;
}
public void setWorldLocation (Location3D center) {
myWorldCenter = center;
}
/**
* Moves the camera by a specified change. This change
* is represented in view coordinates and will be
* converted into moving the camera's world location.
*
* @param change The amount to move the camera by
*/
public void moveCamera (Location change) {
Location3D tochange = deltaviewtoWorld(change);
Location3D temp = new Location3D(tochange);
temp.add(myWorldCenter);
if (myMapSize == null) {
return;
}
if (temp.getX() < VISION || temp.getX() > myMapSize.getWidth() * ISO_HEIGHT - VISION) {
tochange.add(-tochange.getX(), 0, 0);
}
if (temp.getY() < VISION || temp.getY() > myMapSize.getHeight() * ISO_HEIGHT - VISION) {
tochange.add(0, -tochange.getY(), 0);
}
myWorldCenter.add(tochange);
updateWorldSize();
}
private void updateWorldSize () {
Rectangle2D bigger = new Rectangle(myScreenSize);
// Scale to stop shearing
double zoom = 1.1;
AffineTransform scale = new AffineTransform();
scale.translate((bigger.getWidth() / 2) - (bigger.getWidth() * zoom / 2),
(bigger.getHeight() / 2) - (bigger.getHeight() * zoom / 2));
scale.scale(zoom, zoom);
myWorldVision = viewtoWorld(scale.createTransformedShape(bigger));
}
public Shape getWorldVision() {
return myWorldVision;
}
public void setViewSize(Dimension dim) {
myScreenSize = new Dimension(dim);
updateWorldSize();
}
}