package net.sf.openrocket.gui.dialogs.motor.thrustcurve; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.Font; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JLayeredPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.title.TextTitle; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; class MotorInformationPanel extends JPanel { private static final int ZOOM_ICON_POSITION_NEGATIVE_X = 50; private static final int ZOOM_ICON_POSITION_POSITIVE_Y = 12; private static final Color NO_COMMENT_COLOR = Color.GRAY; private static final Color WITH_COMMENT_COLOR = Color.BLACK; private static final Translator trans = Application.getTranslator(); // Motors in set private List<ThrustCurveMotor> selectedMotorSet; // Selected motor private ThrustCurveMotor selectedMotor; private final JLabel totalImpulseLabel; private final JLabel classificationLabel; private final JLabel avgThrustLabel; private final JLabel maxThrustLabel; private final JLabel burnTimeLabel; private final JLabel launchMassLabel; private final JLabel emptyMassLabel; private final JLabel dataPointsLabel; private final JLabel digestLabel; private final JTextArea comment; private final Font noCommentFont; private final Font withCommentFont; private final JFreeChart chart; private final ChartPanel chartPanel; private final JLabel zoomIcon; public MotorInformationPanel() { super(new MigLayout("fill")); // Thrust curve info //// Total impulse: { this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Totalimpulse"))); totalImpulseLabel = new JLabel(); this.add(totalImpulseLabel, "split"); classificationLabel = new JLabel(); classificationLabel.setEnabled(false); // Gray out this.add(classificationLabel, "gapleft unrel, wrap"); //// Avg. thrust: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Avgthrust"))); avgThrustLabel = new JLabel(); this.add(avgThrustLabel, "wrap"); //// Max. thrust: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Maxthrust"))); maxThrustLabel = new JLabel(); this.add(maxThrustLabel, "wrap"); //// Burn time: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Burntime"))); burnTimeLabel = new JLabel(); this.add(burnTimeLabel, "wrap"); //// Launch mass: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Launchmass"))); launchMassLabel = new JLabel(); this.add(launchMassLabel, "wrap"); //// Empty mass: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Emptymass"))); emptyMassLabel = new JLabel(); this.add(emptyMassLabel, "wrap"); //// Data points: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Datapoints"))); dataPointsLabel = new JLabel(); this.add(dataPointsLabel, "wrap para"); if (System.getProperty("openrocket.debug.motordigest") != null) { //// Digest: this.add(new JLabel(trans.get("TCMotorSelPan.lbl.Digest"))); digestLabel = new JLabel(); this.add(digestLabel, "w :300:, wrap para"); } else { digestLabel = null; } comment = new JTextArea(5, 5); GUIUtil.changeFontSize(comment, -2); withCommentFont = comment.getFont(); noCommentFont = withCommentFont.deriveFont(Font.ITALIC); comment.setLineWrap(true); comment.setWrapStyleWord(true); comment.setEditable(false); JScrollPane scrollpane = new JScrollPane(comment); this.add(scrollpane, "spanx, growx, wrap para"); } // Thrust curve plot { chart = ChartFactory.createXYLineChart( null, // title null, // xAxisLabel null, // yAxisLabel null, // dataset PlotOrientation.VERTICAL, false, // legend false, // tooltips false // urls ); // Add the data and formatting to the plot XYPlot plot = chart.getXYPlot(); changeLabelFont(plot.getRangeAxis(), -2); changeLabelFont(plot.getDomainAxis(), -2); //// Thrust curve: chart.setTitle(new TextTitle(trans.get("TCMotorSelPan.title.Thrustcurve"), this.getFont())); chart.setBackgroundPaint(this.getBackground()); plot.setBackgroundPaint(Color.WHITE); plot.setDomainGridlinePaint(Color.LIGHT_GRAY); plot.setRangeGridlinePaint(Color.LIGHT_GRAY); chartPanel = new ChartPanel(chart, false, // properties false, // save false, // print false, // zoom false); // tooltips chartPanel.setMouseZoomable(false); chartPanel.setPopupMenu(null); chartPanel.setMouseWheelEnabled(false); chartPanel.setRangeZoomable(false); chartPanel.setDomainZoomable(false); chartPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); chartPanel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (selectedMotor == null || selectedMotorSet == null) return; if (e.getButton() == MouseEvent.BUTTON1) { // Open plot dialog ThrustCurveMotorPlotDialog plotDialog = new ThrustCurveMotorPlotDialog(selectedMotorSet, selectedMotorSet.indexOf(selectedMotor), SwingUtilities.getWindowAncestor(MotorInformationPanel.this)); plotDialog.setVisible(true); } } }); JLayeredPane layer = new CustomLayeredPane(); layer.setBorder(BorderFactory.createLineBorder(Color.BLUE)); layer.add(chartPanel, (Integer) 0); zoomIcon = new JLabel(Icons.ZOOM_IN); zoomIcon.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); layer.add(zoomIcon, (Integer) 1); this.add(layer, "width 300:300:, height 180:180:, grow, spanx"); } } public void clearData() { selectedMotor = null; selectedMotorSet = null; totalImpulseLabel.setText(""); totalImpulseLabel.setToolTipText(null); classificationLabel.setText(""); classificationLabel.setToolTipText(null); avgThrustLabel.setText(""); maxThrustLabel.setText(""); burnTimeLabel.setText(""); launchMassLabel.setText(""); emptyMassLabel.setText(""); dataPointsLabel.setText(""); if (digestLabel != null) { digestLabel.setText(""); } setComment(""); chart.getXYPlot().setDataset(new XYSeriesCollection()); } public void updateData( List<ThrustCurveMotor> motors, ThrustCurveMotor selectedMotor ) { if ( selectedMotor == null ) { clearData(); return; } this.selectedMotorSet = motors; this.selectedMotor = selectedMotor; // Update thrust curve data double impulse = selectedMotor.getTotalImpulseEstimate(); MotorClass mc = MotorClass.getMotorClass(impulse); totalImpulseLabel.setText(UnitGroup.UNITS_IMPULSE.getDefaultUnit().toStringUnit(impulse)); classificationLabel.setText("(" + mc.getDescription(impulse) + ")"); totalImpulseLabel.setToolTipText(mc.getClassDescription()); classificationLabel.setToolTipText(mc.getClassDescription()); avgThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit( selectedMotor.getAverageThrustEstimate())); maxThrustLabel.setText(UnitGroup.UNITS_FORCE.getDefaultUnit().toStringUnit( selectedMotor.getMaxThrustEstimate())); burnTimeLabel.setText(UnitGroup.UNITS_SHORT_TIME.getDefaultUnit().toStringUnit( selectedMotor.getBurnTimeEstimate())); launchMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( selectedMotor.getLaunchCG().weight)); emptyMassLabel.setText(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( selectedMotor.getEmptyCG().weight)); dataPointsLabel.setText("" + (selectedMotor.getTimePoints().length - 1)); if (digestLabel != null) { digestLabel.setText(selectedMotor.getDigest()); } setComment(selectedMotor.getDescription()); // Update the plot XYPlot plot = chart.getXYPlot(); final int index = motors.indexOf(selectedMotor); XYSeriesCollection dataset = new XYSeriesCollection(); for (int i = 0; i < motors.size(); i++) { ThrustCurveMotor m = motors.get(i); //// Thrust XYSeries series = new XYSeries(trans.get("TCMotorSelPan.title.Thrust") + " (" + i + ")"); double[] time = m.getTimePoints(); double[] thrust = m.getThrustPoints(); for (int j = 0; j < time.length; j++) { series.add(time[j], thrust[j]); } dataset.addSeries(series); boolean selected = (i == index); plot.getRenderer().setSeriesStroke(i, new BasicStroke(selected ? 3 : 1)); plot.getRenderer().setSeriesPaint(i, ThrustCurveMotorSelectionPanel.getColor(i)); } plot.setDataset(dataset); } private void setComment(String s) { s = s.trim(); if (s.length() == 0) { //// No description available. comment.setText(trans.get("TCMotorSelPan.noDescription")); comment.setFont(noCommentFont); comment.setForeground(NO_COMMENT_COLOR); } else { comment.setText(s); comment.setFont(withCommentFont); comment.setForeground(WITH_COMMENT_COLOR); } comment.setCaretPosition(0); } void changeLabelFont(ValueAxis axis, float size) { Font font = axis.getTickLabelFont(); font = font.deriveFont(font.getSize2D() + size); axis.setTickLabelFont(font); } /** * Custom layered pane that sets the bounds of the components on every layout. */ public class CustomLayeredPane extends JLayeredPane { @Override public void doLayout() { synchronized (getTreeLock()) { int w = getWidth(); int h = getHeight(); chartPanel.setBounds(0, 0, w, h); zoomIcon.setBounds(w - ZOOM_ICON_POSITION_NEGATIVE_X, ZOOM_ICON_POSITION_POSITIVE_Y, 50, 50); } } } }