// Copyright (c) 2010 Anthony Towns // GPL v2 or later package altosui; import java.awt.Color; import java.util.ArrayList; import java.util.HashMap; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.TextAnchor; import altosui.AltosDataPoint; import altosui.AltosGraph; class AltosGraphTime extends AltosGraph { static interface Element { void attachGraph(AltosGraphTime g); void gotTimeData(double time, AltosDataPoint d); void addToPlot(AltosGraphTime g, XYPlot plot); } static class TimeAxis implements Element { private int axis; private Color color; private String label; private AxisLocation locn; private double min_y = Double.NaN; public TimeAxis(int axis, String label, Color color, AxisLocation locn) { this.axis = axis; this.color = color; this.label = label; this.locn = locn; } public void setLowerBound(double min_y) { this.min_y = min_y; } public void attachGraph(AltosGraphTime g) { return; } public void gotTimeData(double time, AltosDataPoint d) { return; } public void addToPlot(AltosGraphTime g, XYPlot plot) { NumberAxis numAxis = new NumberAxis(label); if (!Double.isNaN(min_y)) numAxis.setLowerBound(min_y); plot.setRangeAxis(axis, numAxis); plot.setRangeAxisLocation(axis, locn); numAxis.setLabelPaint(color); numAxis.setTickLabelPaint(color); numAxis.setAutoRangeIncludesZero(false); } } abstract static class TimeSeries implements Element { protected XYSeries series; private String axisName; private Color color; public TimeSeries(String axisName, String label, Color color) { this.series = new XYSeries(label); this.axisName = axisName; this.color = color; } public void attachGraph(AltosGraphTime g) { g.setAxis(this, axisName, color); } abstract public void gotTimeData(double time, AltosDataPoint d); public void addToPlot(AltosGraphTime g, XYPlot plot) { XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(this.series); XYItemRenderer renderer = new StandardXYItemRenderer(); renderer.setSeriesPaint(0, color); int dataNum = g.getDataNum(this); int axisNum = g.getAxisNum(this); plot.setDataset(dataNum, dataset); plot.mapDatasetToRangeAxis(dataNum, axisNum); plot.setRenderer(dataNum, renderer); } } static class StateMarker implements Element { private double val = Double.NaN; private String name; private int state; StateMarker(int state, String name) { this.state = state; this.name = name; } public void attachGraph(AltosGraphTime g) { return; } public void gotTimeData(double time, AltosDataPoint d) { if (Double.isNaN(val) || time < val) { if (d.state() == state) { val = time; } } } public void addToPlot(AltosGraphTime g, XYPlot plot) { if (Double.isNaN(val)) return; ValueMarker m = new ValueMarker(val); m.setLabel(name); m.setLabelAnchor(RectangleAnchor.TOP_RIGHT); m.setLabelTextAnchor(TextAnchor.TOP_LEFT); plot.addDomainMarker(m); } } private String title; private ArrayList elements; private HashMap axes; private HashMap datasets; private ArrayList datasetAxis; public AltosGraphTime(String title) { this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png"; this.title = title; this.elements = new ArrayList(); this.axes = new HashMap(); this.datasets = new HashMap(); this.datasetAxis = new ArrayList(); } public AltosGraphTime addElement(Element e) { e.attachGraph(this); elements.add(e); return this; } public void setAxis(Element ds, String axisName, Color color) { Integer axisNum = axes.get(axisName); int dsNum = datasetAxis.size(); if (axisNum == null) { axisNum = newAxis(axisName, color); } datasets.put(ds, dsNum); datasetAxis.add(axisNum); } public int getAxisNum(Element ds) { return datasetAxis.get( datasets.get(ds) ).intValue(); } public int getDataNum(Element ds) { return datasets.get(ds).intValue(); } private Integer newAxis(String name, Color color) { int cnt = axes.size(); AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT; if (cnt > 0) { locn = AxisLocation.TOP_OR_RIGHT; } Integer res = new Integer(cnt); axes.put(name, res); this.addElement(new TimeAxis(cnt, name, color, locn)); return res; } public void addData(AltosDataPoint d) { double time = d.time(); for (Element e : elements) { e.gotTimeData(time, d); } } public JFreeChart createChart() { NumberAxis xAxis = new NumberAxis("Time (s)"); xAxis.setAutoRangeIncludesZero(false); XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); XYPlot plot = new XYPlot(); plot.setDomainAxis(xAxis); plot.setRenderer(renderer); plot.setOrientation(PlotOrientation.VERTICAL); renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true); ChartUtilities.applyCurrentTheme(chart); plot.setDomainPannable(true); plot.setRangePannable(true); for (Element e : elements) { e.addToPlot(this, plot); } return chart; } public void toPNG() throws java.io.IOException { if (axes.size() > 1) { toPNG(800, 500); } else { toPNG(300, 500); } } }