import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; public class GuiInterface extends JComponent implements ActionListener { // Note: I don't recommend that you use this as a starting example for other // graphics applications. I'm sacrificing a fair amount on programming style // for the sake of getting it all to fit into a single class. private static final Color EMPTY_COLOR = new Color(0xA0, 0xF0, 0x00); private static final Color HAPPY_COLOR = new Color(0x00, 0x80, 0x00); private static final Color BURNING_COLOR = new Color(0xD0, 0x50, 0x00); private JButton step; private JButton reset; private double[] prevParms; private int rows; private int cols; private FireSim sim; private int[][] prev; private GuiInterface(JButton step, JButton reset) { this.step = step; this.reset = reset; this.prevParms = null; this.sim = null; this.prev = null; setPreferredSize(new Dimension(300, 300)); setBorder(BorderFactory.createEtchedBorder()); step.addActionListener(this); reset.addActionListener(this); } protected void paintComponent(Graphics g) { int width = getWidth(); int height = getHeight(); if(sim == null) { g.setColor(Color.WHITE); g.fillRect(0, 0, width, height); } else { for(int i = 0; i < rows; i++) { for(int j = 0; j < cols; j++) { int x0 = j * width / cols; int x1 = (j + 1) * width / cols; int y0 = i * height / rows; int y1 = (i + 1) * height / rows; int w = x1 - x0; int h = y1 - y0; g.setColor(getColor(sim.getCell(i, j))); g.fillRect(x0, y0, w, h); if(prev != null) { g.setColor(getColor(prev[i][j])); g.fillRect(x0 + w / 4, y0 + h / 4, w / 2, h / 2); } } } } } private Color getColor(int state) { switch(state) { case FireSim.EMPTY: return EMPTY_COLOR; case FireSim.HAPPY: return HAPPY_COLOR; case FireSim.BURNING: return BURNING_COLOR; default: return Color.RED; } } public void actionPerformed(ActionEvent event) { if(event.getSource() == reset) { doReset(); } else if(event.getSource() == step) { saveState(); sim.step(); repaint(); } } private void doReset() { double[] parms = retrieveParameters(this, prevParms); prevParms = parms; rows = (int) parms[0]; cols = (int) parms[1]; sim = new FireSim(rows, cols, parms[2], parms[3], (int) parms[4]); prev = null; repaint(); } private void saveState() { int[][] ret = new int[rows][cols]; for(int i = 0; i < rows; i++) { for(int j = 0; j < cols; j++) ret[i][j] = sim.getCell(i, j); } prev = ret; } public static void main(String[] args) { JFrame gui = new JFrame("Fire!"); gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton step = new JButton("Step"); JButton reset = new JButton("Reset"); GuiInterface main = new GuiInterface(step, reset); JPanel buttons = new JPanel(); buttons.add(step); buttons.add(reset); Container contents = gui.getContentPane(); contents.add(main, BorderLayout.CENTER); contents.add(buttons, BorderLayout.SOUTH); gui.pack(); gui.setVisible(true); main.doReset(); } private static double[] retrieveParameters(Component parent, double[] defaults) { GridBagLayout gb = new GridBagLayout(); JPanel panel = new JPanel(gb); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.LINE_START; JLabel prompt = new JLabel("Enter the simulation parameters."); gb.setConstraints(prompt, gbc); panel.add(prompt); String[] labels = { "Rows", "Columns", "Empty Probability", "Catch Probability", "Burn Life" }; boolean[] isInt = { true, true, false, false, true }; JTextField[] fields = new JTextField[labels.length]; for(int i = 0; i < fields.length; i++) { gbc.gridwidth = 1; gbc.gridx = 0; gbc.gridy = i + 1; JLabel label = new JLabel(labels[i] + ": "); gb.setConstraints(label, gbc); panel.add(label); gbc.gridx = 1; fields[i] = new JTextField(6); if(defaults != null) { fields[i].setText(isInt[i] ? "" + (int) defaults[i] : "" + defaults[i]); } gb.setConstraints(fields[i], gbc); panel.add(fields[i]); } while(true) { int action = JOptionPane.showConfirmDialog(parent, panel, "Configure Parameters", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if(action != JOptionPane.OK_OPTION) { System.exit(0); } double[] ret = new double[fields.length]; boolean accepted = true; for(int i = 0; i < ret.length; i++) { try { if(isInt[i]) { ret[i] = Integer.parseInt(fields[i].getText()); } else { ret[i] = Double.parseDouble(fields[i].getText()); if(ret[i] < 0 || ret[i] > 1) { prompt.setText(labels[i] + " must be between 0 " + "and 1"); accepted = false; break; } } } catch(NumberFormatException e) { prompt.setText(labels[i] + " must be a valid " + (isInt[i] ? "integer" : "number")); accepted = false; break; } } if(accepted) return ret; } } }