package nl.tudelft.bw4t.map; import java.awt.Color; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.xml.bind.annotation.XmlID; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlRootElement; import nl.tudelft.bw4t.map.Door.Orientation; import nl.tudelft.bw4t.util.OneTimeInitializing; /** * A zone is a square area in the map. The zone also functions as a 'navpoint' * in that the bot uses these zones for navigation. The navpoint is the middle * of the area. Zones can be rooms or corridor areas. * * This class is not thread safe. However it is required that * {@link #hashCode()} and {@link #equals(Object)} are thread safe. */ @SuppressWarnings("serial") @XmlRootElement public class Zone implements Serializable, OneTimeInitializing { /** * Type of the zone */ public enum Type { /** Room. Only 1 bot allowed */ ROOM, /** Corridor. Adjustable 1 bot or multiple bots allowed */ CORRIDOR, /** Charging zone. Where robots charge */ CHARGINGZONE, /** Blockade. Obstacle in the path of robots */ BLOCKADE } /** * Set name of dropzone, startzone, blockade, corridor, charging zone and * room */ public static final String DROP_ZONE_NAME = "DropZone"; public static final String START_ZONE_NAME = "StartZone"; public static final String BLOCKADE_NAME = "Blockade"; public static final String CORRIDOR_NAME = "Corridor"; public static final String CHARGING_ZONE_NAME = "ChargeZone"; public static final String ROOM_NAME = "Room"; /** Set color of blockade and chargingzone. */ public static final Color BLOCKADE_COLOR = new Color(0.6f, 0f, 0f); public static final Color CHARGING_ZONE_COLOR = new Color(0f, 0.5f, 0f); /** * NAME MUST BE SET UNIQUE. Default null otherwise XML serialization will * fail. */ private String name = null; /** * defult type null otherwise XML serialization will fail. */ private Type type = null; private List<Door> doors = new LinkedList<>(); private List<BlockColor> blocks = new LinkedList<>(); private Rectangle boundingbox = new Rectangle(); /** * In the XML file, we refer to the name as unique ID. */ private List<Zone> neighbours = new LinkedList<>(); /** * Default render options: null. */ private RenderOptions renderOptions = null; /** * Empty constructor, initialize Zone. */ public Zone() { } /** * Constructor. * * @param nm * name of zone * @param bbox * boundingbox * @param t * type */ public Zone(String nm, Rectangle bbox, Type t) { name = nm; type = t; boundingbox = bbox; } public Type getType() { return type; } public void setType(Type newType) { if (type != null) { throw new IllegalStateException("type has already been set."); } type = newType; } public Rectangle getBoundingbox() { return boundingbox; } public void setBoundingbox(Rectangle boundingbox) { this.boundingbox = boundingbox; } public List<Door> getDoors() { return doors; } public void setDoors(List<Door> doors) { this.doors = doors; } public List<BlockColor> getBlocks() { return blocks; } public void setBlocks(List<BlockColor> blocks) { this.blocks = blocks; } /** * XmlIDREF annotation indicates XML serializer to use only the IDs and not * the full elements in the list * * @return List<Zone> containing neighbours */ @XmlIDREF public List<Zone> getNeighbours() { return neighbours; } public void setNeighbours(List<Zone> neighbours) { this.neighbours = neighbours; } /** * XmlID hints the XML serializer that this field can be used as unique ID * for a zone. * * @return name of this zone. It's also the name of the "navpoint" */ @XmlID public String getName() { return name; } public void setName(String newName) { if (name != null) { throw new IllegalStateException("name has already been set."); } name = newName; } /** * Add new {@link Door} * * @param door * {@link Door} */ public void addDoor(Door door) { doors.add(door); } /** * Add new {@link BlockColor} * * @param block * {@link BlockColor} */ public void addBlock(BlockColor block) { blocks.add(block); } /** * @param zone * {@link Zone} to be added as Neighbour */ public void addNeighbour(Zone zone) { neighbours.add(zone); } @Override public String toString() { List<String> neighbournames = new ArrayList<>(neighbours.size()); for (Zone neighbour : neighbours) { neighbournames.add(neighbour.getName()); } return "Zone[" + name + "," + boundingbox + "," + type + "," + doors + "," + blocks + "," + neighbournames + "]"; } /** * @return {@link RenderOptions}. null if default should be used. */ public RenderOptions getRenderOptions() { return renderOptions; } public void setRenderOptions(RenderOptions renderOptions) { this.renderOptions = renderOptions; } public boolean isOpenSpace() { return getType() == Type.CORRIDOR || getType() == Type.CHARGINGZONE; } /** * * @return true if the zone has a door on its north side. */ public boolean hasNorth() { boolean temp = false; for (Door d : doors) { if (d.getOrientation() == Orientation.HORIZONTAL) { temp = (d.getPosition().getY() < boundingbox.getY()); } } return temp; } /** * * @return true if the zone has a door on its east side. */ public boolean hasEast() { boolean temp = false; for (Door d : doors) { if (d.getOrientation() == Orientation.VERTICAL) { temp = (d.getPosition().getX() > boundingbox.getX()); } } return temp; } /** * * @return true if the zone has a door on its south side. */ public boolean hasSouth() { boolean temp = false; for (Door d : doors) { if (d.getOrientation() == Orientation.HORIZONTAL) { temp = (d.getPosition().getY() > boundingbox.getY()); } } return temp; } /** * * @return true if the zone has a door on its west side. */ public boolean hasWest() { boolean temp = false; for (Door d : doors) { if (d.getOrientation() == Orientation.VERTICAL) { temp = (d.getPosition().getX() < boundingbox.getX()); } } return temp; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } /** * Two zones are equal if name and type are equal. */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Zone other = (Zone) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (type != other.type) return false; return true; } @Override public boolean isInitialized() { return null != type && null != name; } }