Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 90 additions & 37 deletions src/TimingGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.BufferedImage;
Expand All @@ -20,8 +22,9 @@
*/

public class TimingGraph extends JPanel {
private int numRuns;
// lists containing timings of each function on different sized boards
private List<List<Double>> timings;
// number of tiles for each board that was tested
private List<Integer> numTiles;

/**
Expand All @@ -33,7 +36,6 @@ public TimingGraph(List<List<Double>> timings, List<Integer> numTiles) {
this.numTiles = numTiles;
setBackground(Color.WHITE);
setOpaque(true);
numRuns = timings.get(0).size();
showAndTell();
}

Expand All @@ -44,52 +46,95 @@ public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// height and width of window
int w = getWidth();
int h = getHeight();
// Draw y-axis
g2.draw(new Line2D.Double(numRuns, numRuns, numRuns, h - numRuns));
// Draw x-axis
g2.draw(new Line2D.Double(numRuns, h - numRuns, w - numRuns, h - numRuns));
// Draw labels
// offset of the axes from the left and bottom edges of the window
int xOffset = 80;
int yOffset = 60;
// (0, 0) is in the top left corner
// Draw y-axis (extended a little past the end of the graph)
g2.draw(new Line2D.Double(xOffset, yOffset * 3/4, xOffset, h - yOffset));
// Draw x-axis (extended a little past the end of the graph)
g2.draw(new Line2D.Double(xOffset, h - yOffset, w - (xOffset * 3/4), h - yOffset));
// setup font
Font font = g2.getFont();
FontRenderContext frc = g2.getFontRenderContext();
LineMetrics lm = font.getLineMetrics("0", frc);
float sh = lm.getAscent() + lm.getDescent();
// Draw label on the y-axis
String s = "time";
float sy = numRuns + ((h - 2 * numRuns) - s.length() * sh) / 2 + lm.getAscent();
for (int i = 0; i < s.length(); i++) {
String letter = String.valueOf(s.charAt(i));
float sw = (float) font.getStringBounds(letter, frc).getWidth();
float sx = (numRuns - sw) / 2;
g2.drawString(letter, sx, sy);
sy += sh;
}
// Draw label on the x-axis
s = "board size";
sy = h - numRuns + (numRuns - sh) / 2 + lm.getAscent();
// rotate the x and y axes 90 degrees counterclockwise to draw the label sideways
AffineTransform at = new AffineTransform();
at.rotate(Math.PI / -2);
g2.setTransform(at);
String s = "time (s)";
String exampleTime = "0.002";
float sw = (float) font.getStringBounds(s, frc).getWidth();
float sx = (w - sw) / 2;
// due to the rotation, the negative x acts like the y pre-rotation
float sx = (h + sw) / -2;
// similarly, the positive y acts like the x (pre-rotation)
float sy = (xOffset - (float) font.getStringBounds(exampleTime, frc).getWidth() + lm.getAscent()) / 2;
g2.drawString(s, sx, sy);
// revert the rotation
at.rotate(Math.PI / 2);
g2.setTransform(at);
// Draw label on the x-axis
s = "board size (tiles)";
sw = (float) font.getStringBounds(s, frc).getWidth();
sx = (w - sw) / 2;
sy = h - ((yOffset - lm.getAscent()) / 2);
g2.drawString(s, sx, sy);
// Label the origin
s = "0";
sw = (float) font.getStringBounds(s, frc).getWidth();
sx = xOffset - sw;
sy = h - yOffset + lm.getAscent();
g2.drawString(s, sx, sy);
// Draw lines
double xInc = (double) (w - 2 * numRuns) / numTiles.get(numTiles.size() - 1);
double scale = (double) (h - 2 * numRuns) / getMaxTime();
g2.setPaint(Color.GREEN.darker());
// Draw the x-axis labels
float maxLabelWidth = (float) font.getStringBounds(String.format("%d", Constants.MAX_BOARD_SIZE_FOR_AUTOPLAY), frc).getWidth();
int numIntervals = w / (int) (3 * maxLabelWidth);
int intervalWidth = (w - (2 * xOffset)) / numIntervals;
for (int i = 1; i != numIntervals + 1; ++i) {
s = String.format("%d", i * (Constants.MAX_BOARD_SIZE_FOR_AUTOPLAY / numIntervals));
sw = (float) font.getStringBounds(s, frc).getWidth();
sx = xOffset + (i * intervalWidth) - (sw / 2);
sy = h - yOffset + lm.getAscent();
g2.drawString(s, sx, sy);
}
// Draw the y-axis labels
numIntervals = h / (int) (6 * lm.getAscent());
int intervalHeight = (h - (2 * yOffset)) / numIntervals;
double maxRecordedTime = getMaxTime();
for (int i = 1; i != numIntervals + 1; ++i) {
// Draw time with 3 digit precision
s = String.format("%.3f", i * (maxRecordedTime / numIntervals));
sw = (float) font.getStringBounds(s, frc).getWidth();
sx = xOffset - sw;
sy = h - yOffset - (i * intervalHeight);
g2.drawString(s, sx, sy);
}
// Draw the points and the lines connecting them
double xScale = (double) (w - (2 * xOffset)) / Constants.MAX_BOARD_SIZE_FOR_AUTOPLAY;
double yScale = (double) (h - (2 * yOffset)) / maxRecordedTime;
g2.setPaint(Color.RED);
for (List<Double> run : timings) {
for (int i = 0; i != run.size() - 1; ++i) {
double x1 = numRuns + numTiles.get(i) * xInc;
double y1 = h - numRuns - scale * run.get(i);
double x2 = numRuns + numTiles.get(i + 1) * xInc;
double y2 = h - numRuns - scale * run.get(i + 1);
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
// Mark data points with small circles
for (int i = 0; i < run.size(); i++) {
double x = numRuns + numTiles.get(i) * xInc;
double y = h - numRuns - scale * run.get(i);
s = "flood";
for (List<Double> times : timings) {
// Set the previous to be the origin
double prevX = xOffset;
double prevY = h - yOffset;
for (int i = 0; i != times.size(); ++i) {
// Draw the current point and connect it to the previous point
double x = xOffset + (numTiles.get(i) * xScale);
double y = h - yOffset - (times.get(i) * yScale);
g2.fill(new Ellipse2D.Double(x - 2, y - 2, 4, 4));
g2.draw(new Line2D.Double(prevX, prevY, x, y));
// Update the previous point
prevX = x;
prevY = y;
}
// draw the legend
g2.drawString(s, (float) prevX + 5, (float) Math.min(prevY + lm.getAscent() / 2.5, h - yOffset));
// update flood function name for legend
s = "flood1";
g2.setPaint(Color.LIGHT_GRAY);
}
}
Expand Down Expand Up @@ -122,6 +167,14 @@ private void showAndTell() {
BufferedImage image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.createGraphics();
frame.paint(g);
// repaint the frame if the size of the window changes
frame.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
super.componentResized(e);
frame.repaint();
}
});
g.dispose();
// Tell
try {
Expand Down