1 package net.sf.openrocket.gui.dialogs.optimization;
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;
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;
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;
34 import org.jfree.chart.ChartFactory;
35 import org.jfree.chart.ChartPanel;
36 import org.jfree.chart.JFreeChart;
37 import org.jfree.chart.annotations.XYBoxAnnotation;
38 import org.jfree.chart.annotations.XYLineAnnotation;
39 import org.jfree.chart.annotations.XYPointerAnnotation;
40 import org.jfree.chart.axis.AxisLocation;
41 import org.jfree.chart.axis.NumberAxis;
42 import org.jfree.chart.labels.CustomXYToolTipGenerator;
43 import org.jfree.chart.plot.PlotOrientation;
44 import org.jfree.chart.plot.XYPlot;
45 import org.jfree.chart.renderer.PaintScale;
46 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
47 import org.jfree.chart.renderer.xy.XYShapeRenderer;
48 import org.jfree.chart.title.PaintScaleLegend;
49 import org.jfree.data.xy.DefaultXYZDataset;
50 import org.jfree.data.xy.XYSeries;
51 import org.jfree.data.xy.XYSeriesCollection;
52 import org.jfree.ui.RectangleEdge;
53 import org.jfree.ui.TextAnchor;
56 * A class that plots the path of an optimization.
58 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
60 public class OptimizationPlotDialog extends JDialog {
61 private static final LogHelper log = Application.getLogger();
62 private static final Translator trans = Application.getTranslator();
65 private static final LinearInterpolator RED = new LinearInterpolator(
66 new double[] { 0.0, 1.0 }, new double[] { 0.0, 1.0 }
68 private static final LinearInterpolator GREEN = new LinearInterpolator(
69 new double[] { 0.0, 1.0 }, new double[] { 0.0, 0.0 }
71 private static final LinearInterpolator BLUE = new LinearInterpolator(
72 new double[] { 0.0, 1.0 }, new double[] { 1.0, 0.0 }
75 private static final Color OUT_OF_DOMAIN_COLOR = Color.BLACK;
77 private static final Color PATH_COLOR = new Color(220, 0, 0);
80 public OptimizationPlotDialog(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
81 List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit, Window parent) {
82 super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
85 JPanel panel = new JPanel(new MigLayout("fill"));
88 if (modifiers.size() == 1) {
89 chart = create1DPlot(path, evaluations, modifiers, parameter, stabilityUnit);
90 } else if (modifiers.size() == 2) {
91 chart = create2DPlot(path, evaluations, modifiers, parameter, stabilityUnit);
93 throw new IllegalArgumentException("Invalid dimensionality, dim=" + modifiers.size());
95 chart.setBorder(BorderFactory.createLineBorder(Color.BLACK));
96 panel.add(chart, "span, grow, wrap para");
99 JLabel label = new StyledLabel(trans.get("lbl.zoomInstructions"), -2);
100 panel.add(label, "");
103 JButton close = new JButton(trans.get("button.close"));
104 close.addActionListener(new ActionListener() {
106 public void actionPerformed(ActionEvent e) {
107 OptimizationPlotDialog.this.setVisible(false);
110 panel.add(close, "right");
115 GUIUtil.setDisposableDialogOptions(this, close);
116 GUIUtil.rememberWindowSize(this);
122 * Create a 1D plot of the optimization path.
124 private ChartPanel create1DPlot(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
125 List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit) {
127 SimulationModifier modX = modifiers.get(0);
128 Unit xUnit = modX.getUnitGroup().getDefaultUnit();
129 Unit yUnit = parameter.getUnitGroup().getDefaultUnit();
131 // Create the optimization path (with autosort)
132 XYSeries series = new XYSeries(trans.get("plot1d.series"), true, true);
133 List<String> tooltips = new ArrayList<String>();
134 for (Point p : evaluations.keySet()) {
135 FunctionEvaluationData data = evaluations.get(p);
137 if (data.getParameterValue() != null) {
138 Value[] state = data.getState();
139 series.add(xUnit.toUnit(state[0].getValue()), yUnit.toUnit(data.getParameterValue().getValue()));
140 tooltips.add(getTooltip(data, parameter));
143 log.error("Could not find evaluation data for point " + p);
148 String xLabel = modX.getRelatedObject().toString() + ": " + modX.getName() + " / " + xUnit.getUnit();
149 String yLabel = parameter.getName() + " / " + yUnit.getUnit();
151 JFreeChart chart = ChartFactory.createXYLineChart(
152 trans.get("plot1d.title"),
156 PlotOrientation.VERTICAL,
162 // Set the scale of the plot to the limits
163 double x1 = xUnit.toUnit(modX.getMinValue());
164 double x2 = xUnit.toUnit(modX.getMaxValue());
166 if (x1 < x2 - 0.0001) {
167 log.debug("Setting 1D plot domain axis x1=" + x1 + " x2=" + x2);
168 chart.getXYPlot().getDomainAxis().setRange(x1, x2);
170 log.warn("1D plot domain singular x1=" + x1 + " x2=" + x2 + ", not setting");
173 // Add lines to show optimization limits
174 XYLineAnnotation line = new XYLineAnnotation(x1, -1e19, x1, 1e19);
175 chart.getXYPlot().addAnnotation(line);
176 line = new XYLineAnnotation(x2, -1e19, x2, 1e19);
177 chart.getXYPlot().addAnnotation(line);
179 // Mark the optimum point
180 Point optimum = path.get(path.size() - 1);
181 FunctionEvaluationData data = evaluations.get(optimum);
183 if (data.getParameterValue() != null) {
184 Value[] state = data.getState();
185 double x = xUnit.toUnit(state[0].getValue());
186 double y = yUnit.toUnit(data.getParameterValue().getValue());
188 XYPointerAnnotation text = new XYPointerAnnotation(trans.get("plot.label.optimum"),
190 text.setTextAnchor(TextAnchor.TOP_LEFT);
191 chart.getXYPlot().addAnnotation(text);
194 log.error("Could not find evaluation data for point " + optimum);
198 XYLineAndShapeRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
199 lineRenderer.setBaseShapesVisible(true);
200 lineRenderer.setSeriesShapesFilled(0, false);
201 //lineRenderer.setSeriesShape(0, shapeRenderer.getBaseShape());
202 lineRenderer.setSeriesOutlinePaint(0, PATH_COLOR);
203 lineRenderer.setSeriesPaint(0, PATH_COLOR);
204 lineRenderer.setUseOutlinePaint(true);
205 CustomXYToolTipGenerator tooltipGenerator = new CustomXYToolTipGenerator();
206 tooltipGenerator.addToolTipSeries(tooltips);
207 lineRenderer.setBaseToolTipGenerator(tooltipGenerator);
209 XYPlot plot = chart.getXYPlot();
211 plot.setDataset(0, new XYSeriesCollection(series));
212 plot.setRenderer(lineRenderer);
216 return new ChartPanel(chart);
220 * Create a 2D plot of the optimization path.
222 private ChartPanel create2DPlot(List<Point> path, Map<Point, FunctionEvaluationData> evaluations,
223 List<SimulationModifier> modifiers, OptimizableParameter parameter, UnitGroup stabilityUnit) {
225 Unit parameterUnit = parameter.getUnitGroup().getDefaultUnit();
227 SimulationModifier modX = modifiers.get(0);
228 SimulationModifier modY = modifiers.get(1);
230 Unit xUnit = modX.getUnitGroup().getDefaultUnit();
231 Unit yUnit = modY.getUnitGroup().getDefaultUnit();
233 // Create the optimization path dataset
234 XYSeries pathSeries = new XYSeries(trans.get("plot2d.path"), false, true);
235 List<String> pathTooltips = new ArrayList<String>();
236 for (Point p : path) {
237 FunctionEvaluationData data = evaluations.get(p);
239 Value[] state = data.getState();
240 pathSeries.add(xUnit.toUnit(state[0].getValue()), yUnit.toUnit(state[1].getValue()));
241 pathTooltips.add(getTooltip(data, parameter));
243 log.error("Could not find evaluation data for point " + p);
248 // Create evaluations dataset
249 double min = Double.POSITIVE_INFINITY;
250 double max = Double.NEGATIVE_INFINITY;
251 double[][] evals = new double[3][evaluations.size()];
252 List<String> evalTooltips = new ArrayList<String>();
254 Iterator<FunctionEvaluationData> iterator = evaluations.values().iterator();
255 for (int i = 0; i < evaluations.size(); i++) {
256 FunctionEvaluationData data = iterator.next();
257 Value param = data.getParameterValue();
260 value = parameterUnit.toUnit(data.getParameterValue().getValue());
265 Value[] state = data.getState();
266 evals[0][i] = xUnit.toUnit(state[0].getValue());
267 evals[1][i] = yUnit.toUnit(state[1].getValue());
277 evalTooltips.add(getTooltip(data, parameter));
279 DefaultXYZDataset evalDataset = new DefaultXYZDataset();
280 evalDataset.addSeries(trans.get("plot2d.evals"), evals);
284 String xLabel = modX.getRelatedObject().toString() + ": " + modX.getName() + " / " + xUnit.getUnit();
285 String yLabel = modY.getRelatedObject().toString() + ": " + modY.getName() + " / " + yUnit.getUnit();
287 JFreeChart chart = ChartFactory.createXYLineChart(
288 trans.get("plot2d.title"),
293 PlotOrientation.VERTICAL,
299 // Set the scale of the plot to the limits
300 double x1 = xUnit.toUnit(modX.getMinValue());
301 double x2 = xUnit.toUnit(modX.getMaxValue());
302 double y1 = yUnit.toUnit(modY.getMinValue());
303 double y2 = yUnit.toUnit(modY.getMaxValue());
305 if (x1 < x2 - 0.0001) {
306 log.debug("Setting 2D plot domain axis to x1=" + x1 + " x2=" + x2);
307 chart.getXYPlot().getDomainAxis().setRange(x1, x2);
309 log.warn("2D plot has singular domain axis: x1=" + x1 + " x2=" + x2);
312 if (y1 < y2 - 0.0001) {
313 log.debug("Setting 2D plot range axis to y1=" + y1 + " y2=" + y2);
314 chart.getXYPlot().getRangeAxis().setRange(y1, y2);
316 log.warn("2D plot has singular range axis: y1=" + y1 + " y2=" + y2);
319 XYBoxAnnotation box = new XYBoxAnnotation(x1, y1, x2, y2);
320 chart.getXYPlot().addAnnotation(box);
322 int n = pathSeries.getItemCount();
323 XYPointerAnnotation text = new XYPointerAnnotation(trans.get("plot.label.optimum"),
324 (Double) pathSeries.getX(n - 1), (Double) pathSeries.getY(n - 1), -Math.PI / 5);
325 text.setTextAnchor(TextAnchor.BASELINE_LEFT);
326 chart.getXYPlot().addAnnotation(text);
329 if (min < max - 0.0001) {
330 log.debug("Setting gradient scale range to min=" + min + " max=" + max);
332 log.warn("2D plot has singular gradient scale, resetting to (0,1): min=" + min + " max=" + max);
337 PaintScale paintScale = new GradientScale(min, max);
339 XYShapeRenderer shapeRenderer = new XYShapeRenderer();
340 shapeRenderer.setPaintScale(paintScale);
341 shapeRenderer.setUseFillPaint(true);
342 CustomXYToolTipGenerator tooltipGenerator = new CustomXYToolTipGenerator();
343 tooltipGenerator.addToolTipSeries(evalTooltips);
344 shapeRenderer.setBaseToolTipGenerator(tooltipGenerator);
347 shapeRenderer.getLegendItem(0, 0);
350 XYLineAndShapeRenderer lineRenderer = new XYLineAndShapeRenderer(true, true);
351 lineRenderer.setBaseShapesVisible(true);
352 lineRenderer.setSeriesShapesFilled(0, false);
353 lineRenderer.setSeriesShape(0, shapeRenderer.getBaseShape());
354 lineRenderer.setSeriesOutlinePaint(0, PATH_COLOR);
355 lineRenderer.setSeriesPaint(0, PATH_COLOR);
356 lineRenderer.setUseOutlinePaint(true);
357 tooltipGenerator = new CustomXYToolTipGenerator();
358 tooltipGenerator.addToolTipSeries(pathTooltips);
359 lineRenderer.setBaseToolTipGenerator(tooltipGenerator);
362 XYPlot plot = chart.getXYPlot();
364 plot.setDataset(0, new XYSeriesCollection(pathSeries));
365 plot.setRenderer(lineRenderer);
367 plot.setDataset(1, evalDataset);
368 plot.setRenderer(1, shapeRenderer);
372 NumberAxis numberAxis = new NumberAxis(parameter.getName() + " / " + parameterUnit.getUnit());
373 PaintScaleLegend scale = new PaintScaleLegend(paintScale, numberAxis);
374 scale.setPosition(RectangleEdge.RIGHT);
375 scale.setMargin(4.0D, 4.0D, 40.0D, 4.0D);
376 scale.setAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
377 chart.addSubtitle(scale);
380 return new ChartPanel(chart);
385 private String getTooltip(FunctionEvaluationData data, OptimizableParameter parameter) {
386 String ttip = "<html>";
387 if (data.getParameterValue() != null) {
388 ttip += parameter.getName() + ": " +
389 parameter.getUnitGroup().getDefaultUnit().toStringUnit(data.getParameterValue().getValue());
392 if (data.getDomainReference() != null) {
393 ttip += trans.get("plot.ttip.stability") + " " + data.getDomainReference();
398 private class GradientScale implements PaintScale {
400 private final double min;
401 private final double max;
403 public GradientScale(double min, double max) {
409 public Paint getPaint(double value) {
410 if (Double.isNaN(value)) {
411 return OUT_OF_DOMAIN_COLOR;
414 value = MathUtil.map(value, min, max, 0.0, 1.0);
415 value = MathUtil.clamp(value, 0.0, 1.0);
417 float r = (float) RED.getValue(value);
418 float g = (float) GREEN.getValue(value);
419 float b = (float) BLUE.getValue(value);
421 return new Color(r, g, b);
425 public double getLowerBound() {
430 public double getUpperBound() {