diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java index dc5b026d8..a254275c1 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -29,9 +29,13 @@ import net.sf.openrocket.util.LinearInterpolator; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; +import org.jfree.chart.LegendItemSource; import org.jfree.chart.annotations.XYImageAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.block.LineBorder; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotOrientation; @@ -39,6 +43,7 @@ import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.TextTitle; import org.jfree.data.Range; import org.jfree.data.xy.XYSeries; @@ -46,8 +51,15 @@ import org.jfree.data.xy.XYSeriesCollection; import org.jfree.text.TextUtilities; import org.jfree.ui.LengthAdjustmentType; import org.jfree.ui.RectangleAnchor; +import org.jfree.ui.RectangleEdge; +import org.jfree.ui.RectangleInsets; import org.jfree.ui.TextAnchor; +/* + * It should be possible to simplify this code quite a bit by using a single Renderer instance for + * both datasets and the legend. But for now, the renderers are queried for the line color information + * and this is held in the Legend. + */ public class SimulationPlot { private static final float PLOT_STROKE_WIDTH = 1.5f; @@ -61,6 +73,8 @@ public class SimulationPlot { private final List eventList; private final List renderers = new ArrayList(); + private final LegendItems legendItems; + int branchCount; void setShowPoints(boolean showPoints) { @@ -86,6 +100,7 @@ public class SimulationPlot { SimulationPlot(Simulation simulation, PlotConfiguration config, boolean initialShowPoints) { this.simulation = simulation; this.config = config; + this.branchCount = simulation.getSimulatedData().getBranchCount(); this.chart = ChartFactory.createXYLineChart( //// Simulated flight @@ -94,14 +109,20 @@ public class SimulationPlot { /*yAxisLabel*/null, /*dataset*/null, /*orientation*/PlotOrientation.VERTICAL, - /*legend*/true, + /*legend*/false, /*tooltips*/true, /*urls*/false ); - chart.addSubtitle(new TextTitle(config.getName())); + this.legendItems = new LegendItems(); + LegendTitle legend = new LegendTitle(legendItems); + legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); + legend.setFrame(new LineBorder()); + legend.setBackgroundPaint(Color.white); + legend.setPosition(RectangleEdge.BOTTOM); + chart.addSubtitle(legend); - this.branchCount = simulation.getSimulatedData().getBranchCount(); + chart.addSubtitle(new TextTitle(config.getName())); // Fill the auto-selections based on first branch selected. FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0); @@ -131,6 +152,7 @@ public class SimulationPlot { Unit unit = filled.getUnit(i); int axis = filled.getAxis(i); String name = getLabel(type, unit); + this.legendItems.lineLabels.add(name); List seriesNames = Util.generateSeriesLabels(simulation); @@ -217,13 +239,24 @@ public class SimulationPlot { // Add data and map to the axis plot.setDataset(axisno, data[i]); ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(branchCount); + renderers.add(r); + plot.setRenderer(axisno, r); r.setBaseShapesVisible(initialShowPoints); r.setBaseShapesFilled(true); for (int j = 0; j < data[i].getSeriesCount(); j++) { - r.setSeriesStroke(j, new BasicStroke(PLOT_STROKE_WIDTH)); + Stroke lineStroke = new BasicStroke(PLOT_STROKE_WIDTH); + r.setSeriesStroke(j, lineStroke); } - renderers.add(r); - plot.setRenderer(axisno, r); + // Now we pull the colors for the legend. + for (int j = 0; j < data[i].getSeriesCount(); j += branchCount) { + Paint linePaint = r.lookupSeriesPaint(j); + this.legendItems.linePaints.add(linePaint); + Shape itemShape = r.lookupSeriesShape(j); + this.legendItems.pointShapes.add(itemShape); + Stroke lineStroke = r.getSeriesStroke(j); + this.legendItems.lineStrokes.add(lineStroke); + } + plot.mapDatasetToRangeAxis(axisno, axisno); axisno++; } @@ -410,6 +443,47 @@ public class SimulationPlot { } + private static class LegendItems implements LegendItemSource { + + private final List lineLabels = new ArrayList(); + private final List linePaints = new ArrayList(); + private final List lineStrokes = new ArrayList(); + private final List pointShapes = new ArrayList(); + + @Override + public LegendItemCollection getLegendItems() { + LegendItemCollection c = new LegendItemCollection(); + int i = 0; + for (String s : lineLabels) { + String label = s; + String description = s; + String toolTipText = null; + String urlText = null; + boolean shapeIsVisible = false; + Shape shape = pointShapes.get(i); + boolean shapeIsFilled = false; + Paint fillPaint = linePaints.get(i); + boolean shapeOutlineVisible = false; + Paint outlinePaint = linePaints.get(i); + Stroke outlineStroke = lineStrokes.get(i); + boolean lineVisible = true; + Stroke lineStroke = lineStrokes.get(i); + Paint linePaint = linePaints.get(i); + + Shape legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); + + LegendItem result = new LegendItem(label, description, toolTipText, + urlText, shapeIsVisible, shape, shapeIsFilled, fillPaint, + shapeOutlineVisible, outlinePaint, outlineStroke, lineVisible, + legendLine, lineStroke, linePaint); + + c.add(result); + i++; + } + return c; + } + } + /** * A modification to the standard renderer that renders the domain marker * labels vertically instead of horizontally.