223 lines
6.7 KiB
Java
Executable File
223 lines
6.7 KiB
Java
Executable File
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.swing.*;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* A graphical view of the simulation grid.
|
|
* The view displays a colored rectangle for each location representing its contents.
|
|
* Colors for each type of species can be defined using the setColor method.
|
|
*
|
|
* @author Michael Kölling and David J. Barnes
|
|
* @version 2016.03.18
|
|
*/
|
|
public class GridView extends JFrame implements SimulatorView
|
|
{
|
|
// Colors used for empty locations.
|
|
private static final Color EMPTY_COLOR = Color.white;
|
|
|
|
// Color used for objects that have no defined color.
|
|
private static final Color UNKNOWN_COLOR = Color.gray;
|
|
|
|
private final String STEP_PREFIX = "Step: ";
|
|
private final String POPULATION_PREFIX = "Population: ";
|
|
private JLabel stepLabel, population;
|
|
private FieldView fieldView;
|
|
|
|
// A map for storing colors for participants in the simulation
|
|
private Map<Class<?>, Color> colors;
|
|
// A statistics object computing and storing simulation information
|
|
private FieldStats stats;
|
|
|
|
/**
|
|
* Create a view of the given width and height.
|
|
* @param height The simulation's height.
|
|
* @param width The simulation's width.
|
|
*/
|
|
public GridView(int height, int width)
|
|
{
|
|
stats = new FieldStats();
|
|
colors = new HashMap<>();
|
|
|
|
setTitle("Fox and Rabbit Simulation");
|
|
stepLabel = new JLabel(STEP_PREFIX, JLabel.CENTER);
|
|
population = new JLabel(POPULATION_PREFIX, JLabel.CENTER);
|
|
|
|
setLocation(20, 50);
|
|
|
|
fieldView = new FieldView(height, width);
|
|
|
|
Container contents = getContentPane();
|
|
contents.add(stepLabel, BorderLayout.NORTH);
|
|
contents.add(fieldView, BorderLayout.CENTER);
|
|
contents.add(population, BorderLayout.SOUTH);
|
|
pack();
|
|
setVisible(true);
|
|
}
|
|
|
|
/**
|
|
* Define a color to be used for a given class of animal.
|
|
* @param animalClass The animal's Class object.
|
|
* @param color The color to be used for the given class.
|
|
*/
|
|
public void setColor(Class<?> animalClass, Color color)
|
|
{
|
|
colors.put(animalClass, color);
|
|
}
|
|
|
|
/**
|
|
* @return The color to be used for a given class of animal.
|
|
*/
|
|
private Color getColor(Class<?> animalClass)
|
|
{
|
|
Color col = colors.get(animalClass);
|
|
if(col == null) {
|
|
// no color defined for this class
|
|
return UNKNOWN_COLOR;
|
|
}
|
|
else {
|
|
return col;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show the current status of the field.
|
|
* @param step Which iteration step it is.
|
|
* @param field The field whose status is to be displayed.
|
|
*/
|
|
public void showStatus(int step, Field field)
|
|
{
|
|
if(!isVisible()) {
|
|
setVisible(true);
|
|
}
|
|
|
|
stepLabel.setText(STEP_PREFIX + step);
|
|
stats.reset();
|
|
|
|
fieldView.preparePaint();
|
|
|
|
for(int row = 0; row < field.getDepth(); row++) {
|
|
for(int col = 0; col < field.getWidth(); col++) {
|
|
Object animal = field.getObjectAt(row, col);
|
|
if(animal != null) {
|
|
Class<?> cls = animal.getClass();
|
|
stats.incrementCount(cls);
|
|
fieldView.drawMark(col, row, getColor(cls));
|
|
}
|
|
else {
|
|
fieldView.drawMark(col, row, EMPTY_COLOR);
|
|
}
|
|
}
|
|
}
|
|
stats.countFinished();
|
|
|
|
population.setText(POPULATION_PREFIX + stats.getPopulationDetails(field));
|
|
fieldView.repaint();
|
|
}
|
|
|
|
/**
|
|
* Determine whether the simulation should continue to run.
|
|
* @return true If there is more than one species alive.
|
|
*/
|
|
public boolean isViable(Field field)
|
|
{
|
|
return stats.isViable(field);
|
|
}
|
|
|
|
/**
|
|
* Prepare for a new run.
|
|
*/
|
|
public void reset()
|
|
{
|
|
stats.reset();
|
|
}
|
|
|
|
/**
|
|
* Provide a graphical view of a rectangular field. This is
|
|
* a nested class (a class defined inside a class) which
|
|
* defines a custom component for the user interface. This
|
|
* component displays the field.
|
|
* This is rather advanced GUI stuff - you can ignore this
|
|
* for your project if you like.
|
|
*/
|
|
private class FieldView extends JPanel
|
|
{
|
|
private final int GRID_VIEW_SCALING_FACTOR = 6;
|
|
|
|
private int gridWidth, gridHeight;
|
|
private int xScale, yScale;
|
|
Dimension size;
|
|
private Graphics g;
|
|
private Image fieldImage;
|
|
|
|
/**
|
|
* Create a new FieldView component.
|
|
*/
|
|
public FieldView(int height, int width)
|
|
{
|
|
gridHeight = height;
|
|
gridWidth = width;
|
|
size = new Dimension(0, 0);
|
|
}
|
|
|
|
/**
|
|
* Tell the GUI manager how big we would like to be.
|
|
*/
|
|
public Dimension getPreferredSize()
|
|
{
|
|
return new Dimension(gridWidth * GRID_VIEW_SCALING_FACTOR,
|
|
gridHeight * GRID_VIEW_SCALING_FACTOR);
|
|
}
|
|
|
|
/**
|
|
* Prepare for a new round of painting. Since the component
|
|
* may be resized, compute the scaling factor again.
|
|
*/
|
|
public void preparePaint()
|
|
{
|
|
if(! size.equals(getSize())) { // if the size has changed...
|
|
size = getSize();
|
|
fieldImage = fieldView.createImage(size.width, size.height);
|
|
g = fieldImage.getGraphics();
|
|
|
|
xScale = size.width / gridWidth;
|
|
if(xScale < 1) {
|
|
xScale = GRID_VIEW_SCALING_FACTOR;
|
|
}
|
|
yScale = size.height / gridHeight;
|
|
if(yScale < 1) {
|
|
yScale = GRID_VIEW_SCALING_FACTOR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Paint on grid location on this field in a given color.
|
|
*/
|
|
public void drawMark(int x, int y, Color color)
|
|
{
|
|
g.setColor(color);
|
|
g.fillRect(x * xScale, y * yScale, xScale-1, yScale-1);
|
|
}
|
|
|
|
/**
|
|
* The field view component needs to be redisplayed. Copy the
|
|
* internal image to screen.
|
|
*/
|
|
public void paintComponent(Graphics g)
|
|
{
|
|
if(fieldImage != null) {
|
|
Dimension currentSize = getSize();
|
|
if(size.equals(currentSize)) {
|
|
g.drawImage(fieldImage, 0, 0, null);
|
|
}
|
|
else {
|
|
// Rescale the previous image.
|
|
g.drawImage(fieldImage, 0, 0, currentSize.width, currentSize.height, null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|