-
Notifications
You must be signed in to change notification settings - Fork 0
/
SimulatorView.java
190 lines (163 loc) · 6.02 KB
/
SimulatorView.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import java.awt.*;
import javax.swing.*;
/**
* A graphical view of the simulation grid. The view displays a rectangle for
* each location. Colors for each type of life form can be defined using the
* setColor method.
*
* @author Rayan Popat (K21056367) & James Coward (K22004743)
* @version 2023.02.23
*/
public class SimulatorView extends JFrame {
// Colors used for empty locations.
private static final Color EMPTY_COLOR = Color.white;
// Text for generation GUI label
private final String GENERATION_PREFIX = "Generation: ";
// Text for population GUI label
private final String POPULATION_PREFIX = "Population: ";
// GUI labels
private JLabel genLabel, population, infoLabel;
// Extends the multi-line plain text view to be suitable for a single-line
// editor view. (part of Swing)
private FieldView fieldView;
// 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 SimulatorView(int height, int width) {
stats = new FieldStats();
setTitle("Life Simulation");
genLabel = new JLabel(GENERATION_PREFIX, JLabel.CENTER);
infoLabel = new JLabel(" ", JLabel.CENTER);
population = new JLabel(POPULATION_PREFIX, JLabel.CENTER);
setLocation(100, 50);
fieldView = new FieldView(height, width);
Container contents = getContentPane();
JPanel infoPane = new JPanel(new BorderLayout());
infoPane.add(genLabel, BorderLayout.WEST);
infoPane.add(infoLabel, BorderLayout.CENTER);
contents.add(infoPane, BorderLayout.NORTH);
contents.add(fieldView, BorderLayout.CENTER);
contents.add(population, BorderLayout.SOUTH);
pack();
setVisible(true);
}
/**
* Display a short information label at the top of the window.
*/
public void setInfoText(String text) {
infoLabel.setText(text);
}
/**
* Show the current status of the field.
*
* @param generation The current generation.
* @param field The field whose status is to be displayed.
*/
public void showStatus(int generation, Field field) {
if (!isVisible()) {
setVisible(true);
}
genLabel.setText(GENERATION_PREFIX + generation);
stats.reset();
fieldView.preparePaint();
for (int row = 0; row < field.getDepth(); row++) {
for (int col = 0; col < field.getWidth(); col++) {
Cell cell = field.getObjectAt(row, col);
if (cell != null && cell.isAlive()) {
stats.incrementCount(cell.getClass());
fieldView.drawMark(col, row, cell.getColor());
} 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);
}
/**
* 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);
}
}
}
}
}