package nl.tudelft.bw4t.map.renderer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JPanel;
import nl.tudelft.bw4t.map.BlockColor;
import nl.tudelft.bw4t.map.Door;
import nl.tudelft.bw4t.map.Path;
import nl.tudelft.bw4t.map.Point;
import nl.tudelft.bw4t.map.Zone;
import nl.tudelft.bw4t.map.view.ViewBlock;
import nl.tudelft.bw4t.map.view.ViewEPartner;
import nl.tudelft.bw4t.map.view.ViewEntity;
/** Implementation of the {@link MapRenderInterface} */
public class MapRenderer extends JPanel implements MapRendererInterface {
/**
* Serialization id.
*/
private static final long serialVersionUID = 2854272641217795283L;
/**
* Initialize MapController (to null).
*/
private transient MapController controller = null;
/**
* Path_size used for scaling.
*/
private static final int PATH_SIZE = 1;
/**
* Constructor.
*
* @param controller
* MapController
*/
public MapRenderer(MapController controller) {
this.setController(controller);
}
public MapController getController() {
return controller;
}
/**
* Set controller and update minimum size.
*
* @param controller
* MapController
*/
public void setController(MapController controller) {
if (this.controller != null) {
this.controller.removeRenderer(this);
}
this.controller = controller;
this.controller.addRenderer(this);
updateMinimumSize();
}
/**
* Update minimum size based on controller.
*/
private void updateMinimumSize() {
MapRenderSettings set = getController().getRenderSettings();
Dimension size = new Dimension(set.scale(set.getWorldWidth()),
set.scale(set.getWorldHeight()) + set.getSequenceBlockSize());
this.setMinimumSize(size);
this.setPreferredSize(size);
this.setMaximumSize(size);
}
/**
* Processes all objects to display them on the panel
*
* @param g
* the graphics object
*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
updateMinimumSize();
Graphics2D g2d = (Graphics2D) g;
drawChargingZones(g2d);
drawRooms(g2d);
drawBlockades(g2d);
drawLabels(g2d);
drawDropZone(g2d);
drawBlocks(g2d);
drawEntity(g2d);
drawSequence(g2d);
drawEPartners(g2d);
drawPaths(g2d);
}
/**
* Display the goal sequence
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawSequence(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
final int size = set.getSequenceBlockSize();
final int wh = set.scale(set.getWorldHeight());
int startPosX = 0;
for (BlockColor color : getController().getSequence()) {
g2d.setColor(color.getColor());
g2d.fill(new Rectangle2D.Double(startPosX, set.scale(set
.getWorldHeight()), size, size));
if (getController().getSequenceIndex() > (startPosX / size)) {
g2d.setColor(Color.BLACK);
int[] xpoints = new int[] { startPosX, startPosX,
startPosX + size };
int[] ypoints = new int[] { wh, wh + size, wh + (size / 2) };
g2d.fillPolygon(xpoints, ypoints, 3);
}
startPosX += size;
}
}
/**
* Process all rooms and their connected doors, and display them in the
* panel with an outline
*
* @param g2d
* t Graphics2D where will be drawn
*/
public void drawRooms(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (Zone room : getController().getRooms()) {
boolean occupied = getController().isOccupied(room);
for (Door door : room.getDoors()) {
drawDoor(g2d, door, occupied);
}
// paint the room
g2d.setColor(Color.GRAY);
Shape roomDisplayCoordinates = set.transformRectangle(room
.getBoundingbox().getRectangle());
g2d.fill(roomDisplayCoordinates);
g2d.setColor(Color.BLACK);
g2d.draw(roomDisplayCoordinates);
}
}
/**
* Draw charging zones in transpart green (color predefined in Zone).
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawChargingZones(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (Zone chargingzone : getController().getChargingZones()) {
// paint the charging zone in transparent green
g2d.setColor(Zone.CHARGING_ZONE_COLOR);
Shape roomDisplayCoordinates = set.transformRectangle(chargingzone
.getBoundingbox().getRectangle());
g2d.fill(roomDisplayCoordinates);
}
}
/**
* Draw blockades in transparent green (color predefined in Zone).
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawBlockades(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (Zone blockade : getController().getBlockades()) {
// paint the blockades in transparent green
g2d.setColor(Zone.BLOCKADE_COLOR);
Shape roomDisplayCoordinates = set.transformRectangle(blockade
.getBoundingbox().getRectangle());
g2d.fill(roomDisplayCoordinates);
g2d.setColor(Color.BLACK);
g2d.draw(roomDisplayCoordinates);
}
}
/**
* Draw door, color depends on status of door (open or close).
*
* @param g2d
* Graphics2D where will be drawn
* @param door
* Door that needs to be drawn
* @param closed
* based on if the door is closed or not, the color will be
* determined
*/
public void drawDoor(Graphics2D g2d, Door door, boolean closed) {
MapRenderSettings set = getController().getRenderSettings();
if (closed) {
g2d.setColor(Door.COLOR_CLOSED);
} else {
g2d.setColor(Door.COLOR_OPEN);
}
g2d.fill(set.transformCenterRectangle(new Rectangle2D.Double(door
.getPosition().getX(), door.getPosition().getY(), door
.getWidth(), door.getHeight())));
}
/**
* Process the labels for the different areas
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawLabels(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
g2d.setColor(Color.DARK_GRAY);
g2d.setFont(new Font("Arial", Font.PLAIN, 10));
for (Zone zone : getController().getZones()) {
Rectangle2D bbox = zone.getBoundingbox().getRectangle();
g2d.drawString(zone.getName(),
(int) (set.scale(bbox.getCenterX()) - (g2d.getFontMetrics()
.stringWidth(zone.getName()) / 2.0)), (int) set
.scale(bbox.getCenterY()));
}
}
/**
* Process the drop zone and connected doors and display them in the panel
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawDropZone(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
Zone dropZone = getController().getDropZone();
if (dropZone == null) {
return;
}
g2d.setColor(Color.getHSBColor(0.0639f, 0.58f, 0.5f)); // brown
Rectangle2D rect = set.transformRectangle(dropZone.getBoundingbox()
.getRectangle());
g2d.fill(rect);
g2d.setColor(Color.BLACK);
g2d.draw(rect);
boolean occupied = getController().isOccupied(dropZone);
for (Door door : dropZone.getDoors()) {
drawDoor(g2d, door, occupied);
}
}
/**
* Process all blocks that are visible and display them with their color
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawBlocks(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
// HACK to alleviate #3292. controller is not thread safe!
Set<ViewBlock> blocks = new HashSet<>(getController().getVisibleBlocks());
for (ViewBlock box : blocks) {
g2d.setColor(box.getColor().getColor());
g2d.fill(set.transformCenterRectangle(new Rectangle2D.Double(box
.getPosition().getX(), box.getPosition().getY(),
ViewBlock.BLOCK_SIZE, ViewBlock.BLOCK_SIZE)));
}
}
/**
* Display the robot this panel represents. The color is adapted depending
* on whether it holds a block or not.
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawEntity(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (ViewEntity e : getController().getVisibleEntities()) {
g2d.setColor(e.getColor());
Point2D loc = e.getLocation();
g2d.fill(set.transformCenterRectangle(new Rectangle2D.Double(loc
.getX(), loc.getY(), e.getRobotSize(), e.getRobotSize())));
if (set.isRenderEntityName()) {
g2d.setColor(Color.RED);
g2d.setFont(new Font("Arial", Font.BOLD, 16));
g2d.drawString(e.getName(),
(int) set.scale(e.getLocation().getX())
- g2d.getFontMetrics().stringWidth(e.getName())
/ 2, (int) set.scale(e.getLocation().getY())
+ set.getEntityNameOffset());
}
}
}
/**
* Display the EPartners in black.
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawEPartners(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (ViewEPartner eP : getController().getVisibleEPartners()) {
g2d.setColor(eP.getColor());
Point2D loc = eP.getLocation();
int x = (int) set.scale(loc.getX());
int y = (int) set.scale(loc.getY());
final int size = set.scale(eP.getSize());
int[] xpoints = new int[] { x, x - size, x + size };
int[] ypoints = new int[] { y + size, y - size, y - size };
g2d.fillPolygon(xpoints, ypoints, 3);
g2d.setColor(Color.BLACK);
g2d.drawPolygon(xpoints, ypoints, 3);
}
}
/**
* Display the paths.
*
* @param g2d
* Graphics2D where will be drawn
*/
public void drawPaths(Graphics2D g2d) {
MapRenderSettings set = getController().getRenderSettings();
for (Path p : controller.getPaths()) {
Point previous = null;
for (Point point : p.getPath()) {
int x = (int) set.scale(point.getX());
int y = (int) set.scale(point.getY());
int scale = set.scale(PATH_SIZE);
g2d.setColor(p.getColor());
drawCenteredCircle(g2d, x, y, scale);
if (previous != null) {
int px = (int) set.scale(previous.getX());
int py = (int) set.scale(previous.getY());
g2d.setColor(Color.BLACK);
g2d.drawLine(px, py, x, y);
}
previous = point;
}
}
}
/**
* @param g2d
* Graphics2D where will be drawn
* @param x
* x coordinate of center
* @param y
* y coordinate of center
* @param r
* radius of circle
*/
private void drawCenteredCircle(Graphics2D g2d, int x, int y, int r) {
int newX = x - (r / 2);
int newY = y - (r / 2);
g2d.fillOval(newX, newY, r, r);
}
}