optimization updates
[debian/openrocket] / src / net / sf / openrocket / gui / dialogs / optimization / OptimizationPlotDialog.java
1 package net.sf.openrocket.gui.dialogs.optimization;
2
3 import java.awt.Color;
4 import java.awt.Paint;
5 import java.awt.Window;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.util.ArrayList;
9 import java.util.Iterator;
10 import java.util.List;
11 import java.util.Map;
12
13 import javax.swing.BorderFactory;
14 import javax.swing.JButton;
15 import javax.swing.JDialog;
16 import javax.swing.JLabel;
17 import javax.swing.JPanel;
18
19 import net.miginfocom.swing.MigLayout;
20 import net.sf.openrocket.gui.components.StyledLabel;
21 import net.sf.openrocket.l10n.Translator;
22 import net.sf.openrocket.logging.LogHelper;
23 import net.sf.openrocket.optimization.general.Point;
24 import net.sf.openrocket.optimization.rocketoptimization.OptimizableParameter;
25 import net.sf.openrocket.optimization.rocketoptimization.SimulationModifier;
26 import net.sf.openrocket.startup.Application;
27 import net.sf.openrocket.unit.Unit;
28 import net.sf.openrocket.unit.UnitGroup;
29 import net.sf.openrocket.unit.Value;
30 import net.sf.openrocket.util.GUIUtil;
31 import net.sf.openrocket.util.LinearInterpolator;
32 import net.sf.openrocket.util.MathUtil;
33
34 import org.jfree.chart.ChartFactory;
35 import org.jfree.chart.ChartPanel;
36 import org.jfree.chart.JFreeChart;
37 import org.jfree.chart.axis.AxisLocation;
38 import org.jfree.chart.axis.NumberAxis;
39 import org.jfree.chart.labels.CustomXYToolTipGenerator;
40 import org.jfree.chart.plot.PlotOrientation;
41 import org.jfree.chart.plot.XYPlot;
42 import org.jfree.chart.renderer.PaintScale;
43 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
44 import org.jfree.chart.renderer.xy.XYShapeRenderer;
45 import org.jfree.chart.title.PaintScaleLegend;
46 import org.jfree.data.xy.DefaultXYZDataset;
47 import org.jfree.data.xy.XYSeries;
48 import org.jfree.data.xy.XYSeriesCollection;
49 import org.jfree.ui.RectangleEdge;
50
51 /**
52  * A class that plots the path of an optimization.
53  * 
54  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
55  */
56 public class OptimizationPlotDialog extends JDialog {
57         private static final LogHelper log = Application.getLogger();
58         private static final Translator trans = Application.getTranslator();
59         
60         // FIXME:  Set range to optimization range
61         
62         private static final LinearInterpolator RED = new LinearInterpolator(
63                         new double[] { 0.0, 1.0 }, new double[] { 0.0, 1.0 }
64                         );
65         private static final LinearInterpolator GREEN = new LinearInterpolator(
66                         new double[] { 0.0, 1.0 }, new double[] { 0.0, 0.0 }
67                         );
68         private static final LinearInterpolator BLUE = new LinearInterpolator(
69                         new double[] { 0.0, 1.0 }, new double[] { 1.0, 0.0 }
70                         );
71         
72         private static final Color OUT_OF_DOMAIN_COLOR = Color.BLACK;
73         
74         private static final Color PATH_COLOR = new Color(220, 0, 0);
75         
76         
77         public OptimizationPlotDialog(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
78                         List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit, Window parent) {
79                 super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
80                 
81
82                 JPanel panel = new JPanel(new MigLayout("fill"));
83                 
84                 ChartPanel chart;
85                 if (modifiers.size() == 1) {
86                         chart = create1DPlot(path, evaluations, modifiers, parameter, stabilityUnit);
87                 } else if (modifiers.size() == 2) {
88                         chart = create2DPlot(path, evaluations, modifiers, parameter, stabilityUnit);
89                 } else {
90                         throw new IllegalArgumentException("Invalid dimensionality, dim=" + modifiers.size());
91                 }
92                 chart.setBorder(BorderFactory.createLineBorder(Color.BLACK));
93                 panel.add(chart, "span, grow, wrap para");
94                 
95
96                 JLabel label = new StyledLabel(trans.get("lbl.zoomInstructions"), -2);
97                 panel.add(label, "");
98                 
99
100                 JButton close = new JButton(trans.get("button.close"));
101                 close.addActionListener(new ActionListener() {
102                         @Override
103                         public void actionPerformed(ActionEvent e) {
104                                 OptimizationPlotDialog.this.setVisible(false);
105                         }
106                 });
107                 panel.add(close, "right");
108                 
109
110                 this.add(panel);
111                 
112                 GUIUtil.setDisposableDialogOptions(this, close);
113         }
114         
115         
116
117         /**
118          * Create a 1D plot of the optimization path.
119          */
120         private ChartPanel create1DPlot(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
121                         List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit) {
122                 
123                 SimulationModifier modX = modifiers.get(0);
124                 Unit xUnit = modX.getUnitGroup().getDefaultUnit();
125                 Unit yUnit = parameter.getUnitGroup().getDefaultUnit();
126                 
127                 // Create the optimization path (with autosort)
128                 XYSeries series = new XYSeries(trans.get("plot1d.series"), true, true);
129                 List<String> tooltips = new ArrayList<String>();
130                 for (Point p : evaluations.keySet()) {
131                         FunctionEvaluationData data = evaluations.get(p);
132                         if (data != null) {
133                                 if (data.getParameterValue() != null) {
134                                         Value[] state = data.getState();
135                                         series.add(xUnit.toUnit(state[0].getValue()), yUnit.toUnit(data.getParameterValue().getValue()));
136                                         tooltips.add(getTooltip(data, parameter));
137                                 }
138                         } else {
139                                 log.error("Could not find evaluation data for point " + p);
140                         }
141                 }
142                 
143
144                 String xLabel = modX.getRelatedObject().toString() + ": " + modX.getName() + " / " + xUnit.getUnit();
145                 String yLabel = parameter.getName() + " / " + yUnit.getUnit();
146                 
147                 JFreeChart chart = ChartFactory.createXYLineChart(
148                                 trans.get("plot1d.title"),
149                                 xLabel,
150                                 yLabel,
151                                 null,
152                                 PlotOrientation.VERTICAL,
153                                 false, // Legend
154                                 true, // Tooltips
155                                 false); // Urls
156                 
157
158                 XYLineAndShapeRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
159                 lineRenderer.setBaseShapesVisible(true);
160                 lineRenderer.setSeriesShapesFilled(0, false);
161                 //lineRenderer.setSeriesShape(0, shapeRenderer.getBaseShape());
162                 lineRenderer.setSeriesOutlinePaint(0, PATH_COLOR);
163                 lineRenderer.setSeriesPaint(0, PATH_COLOR);
164                 lineRenderer.setUseOutlinePaint(true);
165                 CustomXYToolTipGenerator tooltipGenerator = new CustomXYToolTipGenerator();
166                 tooltipGenerator.addToolTipSeries(tooltips);
167                 lineRenderer.setBaseToolTipGenerator(tooltipGenerator);
168                 
169                 XYPlot plot = chart.getXYPlot();
170                 
171                 plot.setDataset(0, new XYSeriesCollection(series));
172                 plot.setRenderer(lineRenderer);
173                 
174
175
176                 return new ChartPanel(chart);
177         }
178         
179         /**
180          * Create a 2D plot of the optimization path.
181          */
182         private ChartPanel create2DPlot(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
183                         List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit) {
184                 
185                 Unit parameterUnit = parameter.getUnitGroup().getDefaultUnit();
186                 
187                 SimulationModifier modX = modifiers.get(0);
188                 SimulationModifier modY = modifiers.get(1);
189                 
190                 Unit xUnit = modX.getUnitGroup().getDefaultUnit();
191                 Unit yUnit = modY.getUnitGroup().getDefaultUnit();
192                 
193                 // Create the optimization path dataset
194                 XYSeries series = new XYSeries(trans.get("plot2d.path"), false, true);
195                 List<String> pathTooltips = new ArrayList<String>();
196                 for (Point p : path) {
197                         FunctionEvaluationData data = evaluations.get(p);
198                         if (data != null) {
199                                 Value[] state = data.getState();
200                                 series.add(xUnit.toUnit(state[0].getValue()), yUnit.toUnit(state[1].getValue()));
201                                 pathTooltips.add(getTooltip(data, parameter));
202                         } else {
203                                 log.error("Could not find evaluation data for point " + p);
204                         }
205                 }
206                 
207
208                 // Create evaluations dataset
209                 double min = Double.POSITIVE_INFINITY;
210                 double max = Double.NEGATIVE_INFINITY;
211                 double[][] evals = new double[3][evaluations.size()];
212                 List<String> evalTooltips = new ArrayList<String>();
213                 
214                 Iterator<FunctionEvaluationData> iterator = evaluations.values().iterator();
215                 for (int i = 0; i < evaluations.size(); i++) {
216                         FunctionEvaluationData data = iterator.next();
217                         Value param = data.getParameterValue();
218                         double value;
219                         if (param != null) {
220                                 value = parameterUnit.toUnit(data.getParameterValue().getValue());
221                         } else {
222                                 value = Double.NaN;
223                         }
224                         
225                         Value[] state = data.getState();
226                         evals[0][i] = xUnit.toUnit(state[0].getValue());
227                         evals[1][i] = yUnit.toUnit(state[1].getValue());
228                         evals[2][i] = value;
229                         
230                         if (value < min) {
231                                 min = value;
232                         }
233                         if (value > max) {
234                                 max = value;
235                         }
236                         
237                         evalTooltips.add(getTooltip(data, parameter));
238                 }
239                 DefaultXYZDataset evalDataset = new DefaultXYZDataset();
240                 evalDataset.addSeries(trans.get("plot2d.evals"), evals);
241                 
242
243
244                 String xLabel = modX.getRelatedObject().toString() + ": " + modX.getName() + " / " + xUnit.getUnit();
245                 String yLabel = modY.getRelatedObject().toString() + ": " + modY.getName() + " / " + yUnit.getUnit();
246                 
247                 JFreeChart chart = ChartFactory.createXYLineChart(
248                                 trans.get("plot2d.title"),
249                                 xLabel,
250                                 yLabel,
251                                 null,
252                                 //evalDataset,
253                                 PlotOrientation.VERTICAL,
254                                 true, // Legend
255                                 true, // Tooltips
256                                 false); // Urls
257                 
258
259                 chart.getXYPlot().getDomainAxis().setRange(xUnit.toUnit(modX.getMinValue()),
260                                 xUnit.toUnit(modX.getMaxValue()));
261                 
262                 chart.getXYPlot().getRangeAxis().setRange(yUnit.toUnit(modY.getMinValue()),
263                                 yUnit.toUnit(modY.getMaxValue()));
264                 
265
266                 PaintScale paintScale = new GradientScale(min, max);
267                 
268                 XYShapeRenderer shapeRenderer = new XYShapeRenderer();
269                 shapeRenderer.setPaintScale(paintScale);
270                 shapeRenderer.setUseFillPaint(true);
271                 CustomXYToolTipGenerator tooltipGenerator = new CustomXYToolTipGenerator();
272                 tooltipGenerator.addToolTipSeries(evalTooltips);
273                 shapeRenderer.setBaseToolTipGenerator(tooltipGenerator);
274                 
275
276                 shapeRenderer.getLegendItem(0, 0);
277                 
278
279                 XYLineAndShapeRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
280                 lineRenderer.setBaseShapesVisible(true);
281                 lineRenderer.setSeriesShapesFilled(0, false);
282                 lineRenderer.setSeriesShape(0, shapeRenderer.getBaseShape());
283                 lineRenderer.setSeriesOutlinePaint(0, PATH_COLOR);
284                 lineRenderer.setSeriesPaint(0, PATH_COLOR);
285                 lineRenderer.setUseOutlinePaint(true);
286                 tooltipGenerator = new CustomXYToolTipGenerator();
287                 tooltipGenerator.addToolTipSeries(pathTooltips);
288                 lineRenderer.setBaseToolTipGenerator(tooltipGenerator);
289                 
290
291                 XYPlot plot = chart.getXYPlot();
292                 
293                 plot.setDataset(0, new XYSeriesCollection(series));
294                 plot.setRenderer(lineRenderer);
295                 
296                 plot.setDataset(1, evalDataset);
297                 plot.setRenderer(1, shapeRenderer);
298                 
299
300                 // Add value scale
301                 NumberAxis numberAxis = new NumberAxis(parameter.getName() + " / " + parameterUnit.getUnit());
302                 PaintScaleLegend scale = new PaintScaleLegend(paintScale, numberAxis);
303                 scale.setPosition(RectangleEdge.RIGHT);
304                 scale.setMargin(4.0D, 4.0D, 40.0D, 4.0D);
305                 scale.setAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
306                 chart.addSubtitle(scale);
307                 
308
309                 return new ChartPanel(chart);
310         }
311         
312         
313
314         private String getTooltip(FunctionEvaluationData data, OptimizableParameter parameter) {
315                 String ttip = "<html>";
316                 if (data.getParameterValue() != null) {
317                         ttip += parameter.getName() + ": " +
318                                         parameter.getUnitGroup().getDefaultUnit().toStringUnit(data.getParameterValue().getValue());
319                         ttip += "<br>";
320                 }
321                 if (data.getDomainReference() != null) {
322                         ttip += trans.get("plot.ttip.stability") + " " + data.getDomainReference();
323                 }
324                 return ttip;
325         }
326         
327         private class GradientScale implements PaintScale {
328                 
329                 private final double min;
330                 private final double max;
331                 
332                 public GradientScale(double min, double max) {
333                         this.min = min;
334                         this.max = max;
335                 }
336                 
337                 @Override
338                 public Paint getPaint(double value) {
339                         if (Double.isNaN(value)) {
340                                 return OUT_OF_DOMAIN_COLOR;
341                         }
342                         
343                         value = MathUtil.map(value, min, max, 0.0, 1.0);
344                         value = MathUtil.clamp(value, 0.0, 1.0);
345                         
346                         float r = (float) RED.getValue(value);
347                         float g = (float) GREEN.getValue(value);
348                         float b = (float) BLUE.getValue(value);
349                         
350                         return new Color(r, g, b);
351                 }
352                 
353                 @Override
354                 public double getLowerBound() {
355                         return min;
356                 }
357                 
358                 @Override
359                 public double getUpperBound() {
360                         return max;
361                 }
362         }
363         
364 }