package engine.utility;
import engine.geometry.Polygon;
import game.terrain.Chunk;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Space {
private static final int CELL_WIDTH = Chunk.WIDTH;
private static final int CELL_HEIGHT = Chunk.HEIGHT;
private final int rows;
private final int cols;
private static class ObjectPolygonHashMap extends HashMap<Object, Polygon> {
private static final long serialVersionUID = -8752904839878943392L;
}
private Map<Object, Polygon> all;
private Map<Object, Polygon>[][] grid;
private Map<Object, Polygon> outside;
public Space(double width, double height) {
rows = (int) Math.ceil(height / CELL_HEIGHT);
cols = (int) Math.ceil(width / CELL_WIDTH);
all = new ObjectPolygonHashMap();
grid = new ObjectPolygonHashMap[rows][cols];
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
grid[r][c] = new ObjectPolygonHashMap();
}
}
outside = new ObjectPolygonHashMap();
}
public Polygon putObject(Object object, Polygon polygon) {
Polygon oldPolygon = null;
if (all.containsKey(object)) {
oldPolygon = removeObject(object);
}
all.put(object, new Polygon(polygon));
int r1 = (int) Math.floor(polygon.getMinY() / CELL_HEIGHT);
int r2 = (int) Math.ceil(polygon.getMaxY() / CELL_HEIGHT);
int c1 = (int) Math.floor(polygon.getMinX() / CELL_WIDTH);
int c2 = (int) Math.ceil(polygon.getMaxX() / CELL_WIDTH);
boolean outsideHandled = false;
for (int r = r1; r < r2; r++) {
for (int c = c1; c < c2; c++) {
try {
grid[r][c].put(object, polygon);
} catch (ArrayIndexOutOfBoundsException e) {
if (!outsideHandled) {
outside.put(object, polygon);
outsideHandled = true;
}
}
}
}
return oldPolygon;
}
public Polygon removeObject(Object object) {
Polygon polygon = all.remove(object);
if (polygon == null) {
return null;
}
int r1 = (int) Math.floor(polygon.getMinY() / CELL_HEIGHT);
int r2 = (int) Math.ceil(polygon.getMaxY() / CELL_HEIGHT);
int c1 = (int) Math.floor(polygon.getMinX() / CELL_WIDTH);
int c2 = (int) Math.ceil(polygon.getMaxX() / CELL_WIDTH);
boolean outsideHandled = false;
for (int r = r1; r < r2; r++) {
for (int c = c1; c < c2; c++) {
try {
grid[r][c].remove(object);
} catch (ArrayIndexOutOfBoundsException e) {
if (!outsideHandled) {
outside.remove(object);
outsideHandled = true;
}
}
}
}
return polygon;
}
/** @return all the objects which intersect the given polygon */
public Set<Object> findObjects(Polygon polygon) {
Set<Object> result = new HashSet<Object>();
int r1 = (int) Math.floor(polygon.getMinY() / CELL_HEIGHT);
int r2 = (int) Math.ceil(polygon.getMaxY() / CELL_HEIGHT);
int c1 = (int) Math.floor(polygon.getMinX() / CELL_WIDTH);
int c2 = (int) Math.ceil(polygon.getMaxX() / CELL_WIDTH);
boolean outsideHandled = false;
for (int r = r1; r < r2; r++) {
for (int c = c1; c < c2; c++) {
try {
for (Entry<Object, Polygon> e : grid[r][c].entrySet()) {
if (Physics.intersects(polygon, e.getValue())) {
result.add(e.getKey());
}
}
} catch (ArrayIndexOutOfBoundsException ex) {
if (!outsideHandled) {
for (Entry<Object, Polygon> e : outside.entrySet()) {
if (Physics.intersects(polygon, e.getValue())) {
result.add(e.getKey());
}
}
outsideHandled = true;
}
}
}
}
return result;
}
/**
* @return all the objects which intersect the given polygon that are of the
* given type
*/
public <T> Set<T> findObjects(Polygon polygon, Class<T> type) {
Set<T> result = new HashSet<T>();
int r1 = (int) Math.floor(polygon.getMinY() / CELL_HEIGHT);
int r2 = (int) Math.ceil(polygon.getMaxY() / CELL_HEIGHT);
int c1 = (int) Math.floor(polygon.getMinX() / CELL_WIDTH);
int c2 = (int) Math.ceil(polygon.getMaxX() / CELL_WIDTH);
boolean outsideHandled = false;
for (int r = r1; r < r2; r++) {
for (int c = c1; c < c2; c++) {
try {
for (Entry<Object, Polygon> e : grid[r][c].entrySet()) {
if (type.isInstance(e.getKey())) {
if (Physics.intersects(polygon, e.getValue())) {
@SuppressWarnings("unchecked")
T t = (T) e.getKey();
result.add(t);
}
}
}
} catch (ArrayIndexOutOfBoundsException ex) {
if (!outsideHandled) {
for (Entry<Object, Polygon> e : outside.entrySet()) {
if (type.isInstance(e.getKey())) {
if (Physics.intersects(polygon, e.getValue())) {
@SuppressWarnings("unchecked")
T t = (T) e.getKey();
result.add(t);
}
}
}
outsideHandled = true;
}
}
}
}
return result;
}
/**
* @return a single object which intersects the given polygon and is of the
* given type
*/
public <T> T findObject(Polygon polygon, Class<T> type) {
int r1 = (int) Math.floor(polygon.getMinY() / CELL_HEIGHT);
int r2 = (int) Math.ceil(polygon.getMaxY() / CELL_HEIGHT);
int c1 = (int) Math.floor(polygon.getMinX() / CELL_WIDTH);
int c2 = (int) Math.ceil(polygon.getMaxX() / CELL_WIDTH);
boolean outsideHandled = false;
for (int r = r1; r < r2; r++) {
for (int c = c1; c < c2; c++) {
try {
for (Entry<Object, Polygon> e : grid[r][c].entrySet()) {
if (type.isInstance(e.getKey())) {
if (Physics.intersects(polygon, e.getValue())) {
@SuppressWarnings("unchecked")
T t = (T) e.getKey();
return t;
}
}
}
} catch (ArrayIndexOutOfBoundsException ex) {
if (!outsideHandled) {
for (Entry<Object, Polygon> e : outside.entrySet()) {
if (type.isInstance(e.getKey())) {
if (Physics.intersects(polygon, e.getValue())) {
@SuppressWarnings("unchecked")
T t = (T) e.getKey();
return t;
}
}
}
outsideHandled = true;
}
}
}
}
return null;
}
}