import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.io.*;
import javax.swing.colorchooser.*;
public class DartsApplet extends JApplet implements MouseMotionListener, ChangeListener {
private JTextArea textArea;
private JButton createHeatMapButton;
private JLabel legendLabel;
private JButton changeColorsButton;
private DartsHeatMap heatMap;
private JFrame changeColorsFrame;
private JTextField regionField, expScoreField;
private JCheckBox showBoardCheckBox, showArgmaxCheckBox;
private JPanel gridPanel;
private JTextField maxExpScoreField, aimForField;
private JLabel sigmaXLabel, sigmaYLabel, rhoLabel;
private JTextField sigmaXField, sigmaYField, rhoField;
private JLabel numScoresLabel;
private JRadioButton simpleButton, generalButton;
private JProgressBar progressBar;
private JLabel progressLabel;
private boolean showBoard = true, showOptimum = true;
private boolean simpleModel = true;
public void init() {
/////////////////////////////////
// the west panel
/////////////////////////////////
// the instructions label
JPanel instructionsPanel = new JPanel();
instructionsPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Instructions"),
BorderFactory.createEmptyBorder(5,5,0,5)));
JLabel instructionsLabel = new JLabel("" +
"1. Throw 50 or so darts, aim-
" +
"ing at the double bullseye.
" +
"2. Enter the scores of each
throw below, " +
"separated
by commas.
" +
"3. Click \"Create heat map!\"
to see a " +
"personalized
heat map." +
"");
instructionsPanel.add(instructionsLabel);
// text area to enter the scores
textArea = new JTextArea();
textArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(200,300));
scrollPane.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Scores"),
BorderFactory.createEmptyBorder(5,5,5,5)));
// create heat map button
createHeatMapButton = new JButton("Create heat map!");
createHeatMapButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int r = createHeatMap();
if (r > 0) {
String message = failMessages[r];
if (r == INVALID_NUMBERS_FAIL) {
message += invalidNumbers;
}
JOptionPane.showMessageDialog(DartsApplet.this,
message, "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
createHeatMapButton.setAlignmentX(JComponent.CENTER_ALIGNMENT);
// put it together
JPanel westPanel = new JPanel();
westPanel.setPreferredSize(new Dimension(200,500));
westPanel.setLayout(new BoxLayout(westPanel, BoxLayout.Y_AXIS));
westPanel.add(instructionsPanel);
westPanel.add(scrollPane);
westPanel.add(Box.createVerticalStrut(5));
westPanel.add(createHeatMapButton);
westPanel.add(Box.createVerticalStrut(10));
add(westPanel, BorderLayout.WEST);
/////////////////////////////////
// the main panel
/////////////////////////////////
// legend
JPanel legendPanel = new JPanel();
legendPanel.setLayout(new BoxLayout(legendPanel, BoxLayout.Y_AXIS));
legendLabel = new JLabel();
legendLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
legendLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
legendPanel.add(legendLabel);
JLabel lowHighLabel = new JLabel("Low High");
lowHighLabel.setAlignmentX(JComponent.CENTER_ALIGNMENT);
legendPanel.add(lowHighLabel);
// change colors
changeColorsButton = new JButton("Change");
changeColorsButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
changeColorsFrame.setVisible(true);
}
});
buildChangeColorsFrame();
JPanel changeColorsPanel = new JPanel();
changeColorsPanel.setLayout(new BoxLayout(changeColorsPanel, BoxLayout.Y_AXIS));
changeColorsPanel.add(changeColorsButton);
changeColorsPanel.add(new JLabel(" "));
// heat map
heatMap = new DartsHeatMap();
heatMap.setPreferredSize(new Dimension((int)(2*Stats.R),(int)(2*Stats.R)));
heatMap.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// pay attention to the mouse
heatMap.addMouseMotionListener(this);
// a bit of a hack to make sure that the heat map size doesn't change
JPanel heatMapPanel = new JPanel();
heatMapPanel.add(heatMap);
// set the color scheme
setColorScheme(CLASSIC);
JPanel colorSchemePanel = new JPanel();
colorSchemePanel.add(new JLabel("Color legend:
"));
colorSchemePanel.add(legendPanel);
colorSchemePanel.add(Box.createHorizontalStrut(1));
colorSchemePanel.add(changeColorsPanel);
// mouse info panel
JPanel mouseInfoPanel = new JPanel();
mouseInfoPanel.add(new JLabel("Region:"));
regionField = new JTextField(3);
regionField.setEditable(false);
mouseInfoPanel.add(regionField);
mouseInfoPanel.add(Box.createHorizontalStrut(10));
mouseInfoPanel.add(new JLabel("Expected score:"));
expScoreField = new JTextField(4);
expScoreField.setEditable(false);
mouseInfoPanel.add(expScoreField);
// options panel
JPanel optionsPanel = new JPanel();
showBoardCheckBox = new JCheckBox();
showBoardCheckBox.setSelected(showBoard);
showBoardCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showBoard = !showBoard;
heatMap.repaint();
}
});
optionsPanel.add(showBoardCheckBox);
optionsPanel.add(new JLabel("Show dartboard"));
optionsPanel.add(Box.createHorizontalStrut(10));
showArgmaxCheckBox = new JCheckBox();
showArgmaxCheckBox.setSelected(showOptimum);
showArgmaxCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showOptimum = !showOptimum;
if (E != null) {
// smart repaint (for speed!)
// assume 1 mm per pixel
heatMap.repaint(0,imax-3,(int)(2*Stats.R)-1-jmax-3,6,6);
}
}
});
optionsPanel.add(showArgmaxCheckBox);
optionsPanel.add(new JLabel("Show optimum place to aim"));
// put it all together
JPanel mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(400,500));
mainPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Heat map"),
BorderFactory.createEmptyBorder(5,5,5,5)));
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.add(colorSchemePanel);
mainPanel.add(heatMapPanel);
mainPanel.add(mouseInfoPanel);
mainPanel.add(Box.createVerticalGlue());
mainPanel.add(optionsPanel);
add(mainPanel, BorderLayout.CENTER);
/////////////////////////////////
// the east panel
/////////////////////////////////
// the help panel
JPanel helpPanel = new JPanel();
helpPanel.setPreferredSize(new Dimension(200,180));
helpPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Help"),
BorderFactory.createEmptyBorder(5,5,0,5)));
helpPanel.add(new JLabel("" +
"First look at the color legend
to see which colors " +
"corres-
pond to high expected scores.
Then look at the heat " +
"map
to see which aiming locations
are assigned these " +
"colors.
" +
"(Note: the \u03c3 parameters are
measured " +
"in mm; for refer-
ence, the dartboard's " +
"radius
is 170 mm.)" +
""));
// the stats panel
JPanel statsPanel = new JPanel();
statsPanel.setPreferredSize(new Dimension(200,240));
statsPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Stats"),
BorderFactory.createEmptyBorder(5,5,0,5)));
// the stats
maxExpScoreField = new JTextField(4);
maxExpScoreField.setEditable(false);
maxExpScoreField.setForeground(Color.RED);
aimForField = new JTextField(4);
aimForField.setEditable(false);
aimForField.setForeground(Color.RED);
sigmaXLabel = new JLabel("\u03c3 = ");
sigmaXField = new JTextField(4);
sigmaXField.setEditable(false);
sigmaXField.setForeground(Color.RED);
sigmaYLabel = new JLabel("\u03c3_y = ");
sigmaYField = new JTextField(4);
sigmaYField.setEditable(false);
sigmaYField.setForeground(Color.RED);
rhoLabel = new JLabel("\u03c1 = ");
rhoField = new JTextField(4);
rhoField.setEditable(false);
rhoField.setForeground(Color.RED);
gridPanel = new JPanel();
gridPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.LINE_START;
c.gridx = 0; c.gridy = 0;
gridPanel.add(new JLabel("Max expected score: "),c);
c.gridx = 1; c.gridy = 0;
gridPanel.add(maxExpScoreField,c);
c.gridx = 0; c.gridy = 1;
gridPanel.add(new JLabel("Aim for: "),c);
c.gridx = 1; c.gridy = 1;
gridPanel.add(aimForField,c);
c.gridx = 0; c.gridy = 2;
gridPanel.add(sigmaXLabel,c);
c.gridx = 1; c.gridy = 2;
gridPanel.add(sigmaXField,c);
// number of scores label
numScoresLabel = new JLabel("
");
c.insets = new Insets(7,0,0,0);
c.gridx = 0; c.gridy = 7; c.gridwidth=2;
gridPanel.add(numScoresLabel,c);
// simple or general button
simpleButton = new JRadioButton("Simple model");
simpleButton.setSelected(true);
simpleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!simpleModel) {
simpleModel = true;
sigmaXLabel.setText("\u03c3 = ");
// clear all the fields
maxExpScoreField.setText("");
aimForField.setText("");
sigmaXField.setText("");
sigmaYField.setText("");
rhoField.setText("");
// rearrange the grid panel
gridPanel.remove(sigmaYLabel);
gridPanel.remove(sigmaYField);
gridPanel.remove(rhoLabel);
gridPanel.remove(rhoField);
// clear the num scores label
numScoresLabel.setText("");
}
}
});
generalButton = new JRadioButton("General model");
generalButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (simpleModel) {
simpleModel = false;
sigmaXLabel.setText("\u03c3_x = ");
// clear all the fields
maxExpScoreField.setText("");
aimForField.setText("");
sigmaXField.setText("");
sigmaYField.setText("");
rhoField.setText("");
// rearrange the grid panel
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.LINE_START;
c.gridx = 0; c.gridy = 3;
gridPanel.add(sigmaYLabel,c);
c.gridx = 1; c.gridy = 3;
gridPanel.add(sigmaYField,c);
c.gridx = 0; c.gridy = 4;
gridPanel.add(rhoLabel,c);
c.gridx = 1; c.gridy = 4;
gridPanel.add(rhoField,c);
// clear the num scores label
numScoresLabel.setText("");
}
}
});
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(simpleButton);
buttonGroup.add(generalButton);
c.insets = new Insets(7,0,0,0);
c.gridx = 0; c.gridy = 8; c.gridwidth=2;
gridPanel.add(simpleButton,c);
c.insets = new Insets(0,0,0,0);
c.gridx = 0; c.gridy = 9; c.gridwidth=2;
gridPanel.add(generalButton,c);
statsPanel.add(gridPanel);
// the progress panel
JPanel progressPanel = new JPanel();
progressPanel.setPreferredSize(new Dimension(200,70));
progressPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Progress"),
BorderFactory.createEmptyBorder(5,5,5,5)));
progressPanel.setLayout(new BoxLayout(progressPanel, BoxLayout.Y_AXIS));
// the progress label and bar
progressLabel = new JLabel("
");
progressPanel.add(progressLabel);
progressBar = new JProgressBar(0,112);
progressBar.setValue(0);
progressBar.setStringPainted(true);
JPanel progressBarPanel = new JPanel();
progressBarPanel.add(progressBar);
progressPanel.add(progressBarPanel);
// put it together
JPanel eastPanel = new JPanel();
eastPanel.setPreferredSize(new Dimension(200,500));
eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.Y_AXIS));
eastPanel.add(helpPanel);
eastPanel.add(statsPanel);
eastPanel.add(progressPanel);
add(eastPanel, BorderLayout.EAST);
}
// pay attention to the mouse over the heat map
// (we have to implement MouseMotionListener and add this as a separate function,
// instead of the usual way, because otherwise this won't run on a JVM <= 1.5)
public void mouseMoved(MouseEvent e) {
if (E != null) {
// assume 1 mm per pixel
int i = e.getX();
int j = (int)(2*Stats.R)-1-e.getY();
int n = E.length;
regionField.setText(Stats.getRegion(i,j,n,n));
if (0 <= i && i < n && 0 <= j && j < n) {
expScoreField.setText(Double.toString(roundTo2(E[i][j])));
}
else {
expScoreField.setText("-----");
}
}
}
public void mouseDragged(MouseEvent e) {}
private static double roundTo2(double d) {
return Math.round(100*d)/100.0;
}
// the frame to change the color scheme
JRadioButton classicButton, grayscaleButton, rainbowButton, customButton;
JLabel[] customLabels;
int numCustomColors;
JColorChooser chooser;
private void buildChangeColorsFrame() {
changeColorsFrame = new JFrame("Change colors");
JPanel choosePanel = new JPanel();
choosePanel.add(new JLabel("Choose a color scheme:"));
// classic
classicButton = new JRadioButton("Classic");
classicButton.setSelected(true);
JPanel classicPreview = new JPanel();
JLabel[] classicLabels = new JLabel[CLASSIC.length];
for (int i=0; i max) max = E[i][j];
}
}
// draw the heat map
for (int i=0; i 0) return INVALID_NUMBERS_FAIL;
// everything passed, so make a new task
task = new StatsTask(scores);
task.start();
return 0;
}
private static void checkNumbers(int[] scores) {
Set invalids = new TreeSet();
boolean atCapacity = false;
for (int i=0; i it = invalids.iterator();
StringBuffer sb = new StringBuffer();
while (it.hasNext()) {
sb.append(Integer.toString(it.next())+", ");
}
if (sb.length() > 0) {
// remove the last ", "
sb.delete(sb.length()-2,sb.length());
if (atCapacity) sb.append(" ...");
else sb.append(".");
}
invalidNumbers = sb.toString();
}
private double[][] E;
private double s1, s2, s3;
private int imax, jmax;
private class StatsTask extends Thread {
private int[] scores;
public StatsTask(int[] scores) {
this.scores = new int[scores.length];
for (int i=0; iRunning EM
algorithm...