package nl.tudelft.bw4t.server.model;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import repast.simphony.context.Context;
import repast.simphony.space.continuous.ContinuousSpace;
import repast.simphony.space.continuous.NdPoint;
import repast.simphony.space.grid.Grid;
import repast.simphony.space.grid.GridPoint;
/**
* Represents an object in the world that can be moved around as needed. It
* forms the basis for all kinds of objects like robots, blocks and rooms. The
* object can also be removed from the world and moved back into the world.
*/
public abstract class BoundedMoveableObject implements BoundedMoveableInterface {
/**
* The counter which represents the id of a BMO.
*/
private static final AtomicLong COUNTER = new AtomicLong();
/**
* The id of the BMO.
*/
private final long id;
/**
* The server context this object is attached to.
*/
private final BW4TServerMap serverMap;
/**
* The box for the BMO.
*/
protected final Rectangle2D.Double boundingBox;
/**
* Creates a new object bounded by a box at (0,0) with size (0,0).
*
* @param space
* the space in which the object should be placed.
* @param grid
* the grid in which the object should be placed.
* @param context
* the context in which the object should be placed.
*/
public BoundedMoveableObject(BW4TServerMap smap) {
assert smap != null;
assert smap.hasMap();
assert smap.hasContext();
this.serverMap = smap;
if (getContext().isEmpty()) {
COUNTER.set(0);
}
this.id = COUNTER.getAndIncrement();
this.boundingBox = new Rectangle2D.Double();
addToContext();
}
/**
* @return The unique id of the object.
*/
@Override
public long getId() {
return id;
}
@Override
public BW4TServerMap getServerMap() {
return serverMap;
}
/**
* @return The context in which the object might be placed in.
*/
public Context<Object> getContext() {
return getServerMap().getContext();
}
/**
* @return The location of the object, if currently in a space.
*/
@Override
public NdPoint getLocation() {
NdPoint location = getSpace().getLocation(this);
if (location == null) {
return new NdPoint(0, 0);
}
return location;
}
/**
* Returns the location on the grid space.
*
* @return The location of the object, if currently in the grid space.
*/
@Override
public GridPoint getGridLocation() {
GridPoint location = getGrid().getLocation(this);
if (location == null) {
return new GridPoint(0, 0);
}
return location;
}
/**
* @return The space of an object.
*/
public ContinuousSpace<Object> getSpace() {
return getServerMap().getContinuousSpace();
}
/**
* @return The grid of an object.
*/
public Grid<Object> getGrid() {
return getServerMap().getGridSpace();
}
/**
* @return The bounding box of this object.
*/
@Override
public Rectangle2D getBoundingBox() {
return boundingBox;
}
/**
* Sets the size of this object.
*
* @param width
* the width of the object in unit size.
* @param height
* the height the object in unit size.
*/
@Override
public void setSize(double width, double height) {
boundingBox.width = width;
boundingBox.height = height;
}
/**
* Moves this object to a new location. The given location will be the
* center of the object.
*
* @param x
* The x coordinate of the location.
* @param y
* The y coordinate of the location.
*/
@Override
public void moveTo(double x, double y) {
boundingBox.x = x - boundingBox.width / 2;
boundingBox.y = y - boundingBox.height / 2;
getSpace().moveTo(this, x, y);
getGrid().moveTo(this, (int) x, (int) y);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return 1;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
BoundedMoveableObject other = (BoundedMoveableObject) obj;
if (boundingBox == null) {
if (other.boundingBox != null) {
return false;
}
} else if (!boundingBox.equals(other.boundingBox)) {
return false;
}
if (serverMap == null) {
if (other.serverMap != null) {
return false;
}
} else if (!serverMap.equals(other.serverMap)) {
return false;
}
return true;
}
/**
* Removes this object from the context given at construction.
*/
@Override
public void removeFromContext() {
getContext().remove(this);
}
/**
* Calculates the distance between our center point and the given point.
*
* @param there
* the point to calculate the distance to.
* @return the distance to the given point.
*/
@Override
public double distanceTo(NdPoint there) {
NdPoint here = getLocation();
return Math.sqrt(Math.pow(there.getX() - here.getX(), 2) + Math.pow(there.getY() - here.getY(), 2));
}
/**
* As {@link #distanceTo(NdPoint)} n
*
* @param o
* is the object to compute the distance to
* @return distance to center of o. Note that the distance to the bounding
* box of o may be smaller than this.
*/
@Override
public double distanceTo(BoundedMoveableObject o) {
return distanceTo(o.getLocation());
}
/**
* set the object to visible
*/
public void addToContext() {
getContext().add(this);
}
/**
* Returns all the points rounded to an integer occupied by the Bounded
* Moveable Object, including the given padding.
*
* @param padding
* The padding to add around the box.
* @return A list of all points occupied by the box.
*/
@Override
public List<NdPoint> getPointsOccupiedByObject(double padding) {
List<NdPoint> points = new LinkedList<>();
double halfPad = Math.ceil(padding / 2);
double x = Math.floor(getBoundingBox().getX());
double y = Math.floor(getBoundingBox().getY());
double width = Math.ceil(getBoundingBox().getWidth());
double height = Math.ceil(getBoundingBox().getHeight());
double x2 = x + width;
double y2 = y + height;
x -= halfPad;
y -= halfPad;
x2 += halfPad;
y2 += halfPad;
for (double i = x; i <= x2; i++) {
for (double j = y; j <= y2; j++) {
points.add(new NdPoint(i, j));
}
}
return points;
}
/**
* Whether this bounded moveable object is free of objects that are of the
* specified type.
*
* @param freeOfType
* The type of objects that this object should be free of.
* @return Whether this bounded moveable object is free of object.
*/
@Override
public boolean isFree(Class<? extends BoundedMoveableObject> freeOfType) {
for (NdPoint point : getPointsOccupiedByObject(0)) {
for (Object o : getSpace().getObjectsAt(point.getX(), point.getY())) {
if (this != o) {
return false;
}
}
}
return true;
}
}