Added localization changes supplied by Russian USS.
[debian/openrocket] / core / src / net / sf / openrocket / gui / dialogs / ComponentAnalysisDialog.java
1 package net.sf.openrocket.gui.dialogs;
2
3 import static net.sf.openrocket.unit.Unit.NOUNIT2;
4 import static net.sf.openrocket.util.Chars.ALPHA;
5
6 import java.awt.Color;
7 import java.awt.Component;
8 import java.awt.Dimension;
9 import java.awt.Font;
10 import java.awt.event.ActionEvent;
11 import java.awt.event.ActionListener;
12 import java.awt.event.WindowAdapter;
13 import java.awt.event.WindowEvent;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Vector;
18
19 import javax.swing.BorderFactory;
20 import javax.swing.JButton;
21 import javax.swing.JComboBox;
22 import javax.swing.JDialog;
23 import javax.swing.JFrame;
24 import javax.swing.JLabel;
25 import javax.swing.JList;
26 import javax.swing.JPanel;
27 import javax.swing.JScrollPane;
28 import javax.swing.JTabbedPane;
29 import javax.swing.JTable;
30 import javax.swing.JToggleButton;
31 import javax.swing.ListSelectionModel;
32 import javax.swing.SwingConstants;
33 import javax.swing.SwingUtilities;
34 import javax.swing.event.ChangeEvent;
35 import javax.swing.event.ChangeListener;
36 import javax.swing.table.TableCellRenderer;
37
38 import net.miginfocom.swing.MigLayout;
39 import net.sf.openrocket.aerodynamics.AerodynamicCalculator;
40 import net.sf.openrocket.aerodynamics.AerodynamicForces;
41 import net.sf.openrocket.aerodynamics.FlightConditions;
42 import net.sf.openrocket.aerodynamics.Warning;
43 import net.sf.openrocket.aerodynamics.WarningSet;
44 import net.sf.openrocket.gui.adaptors.Column;
45 import net.sf.openrocket.gui.adaptors.ColumnTableModel;
46 import net.sf.openrocket.gui.adaptors.DoubleModel;
47 import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
48 import net.sf.openrocket.gui.components.BasicSlider;
49 import net.sf.openrocket.gui.components.StageSelector;
50 import net.sf.openrocket.gui.components.StyledLabel;
51 import net.sf.openrocket.gui.components.UnitSelector;
52 import net.sf.openrocket.gui.scalefigure.RocketPanel;
53 import net.sf.openrocket.gui.util.GUIUtil;
54 import net.sf.openrocket.l10n.Translator;
55 import net.sf.openrocket.masscalc.BasicMassCalculator;
56 import net.sf.openrocket.masscalc.MassCalculator;
57 import net.sf.openrocket.masscalc.MassCalculator.MassCalcType;
58 import net.sf.openrocket.rocketcomponent.Configuration;
59 import net.sf.openrocket.rocketcomponent.FinSet;
60 import net.sf.openrocket.rocketcomponent.Rocket;
61 import net.sf.openrocket.rocketcomponent.RocketComponent;
62 import net.sf.openrocket.startup.Application;
63 import net.sf.openrocket.unit.Unit;
64 import net.sf.openrocket.unit.UnitGroup;
65 import net.sf.openrocket.util.Coordinate;
66 import net.sf.openrocket.util.MathUtil;
67
68 public class ComponentAnalysisDialog extends JDialog implements ChangeListener {
69         
70         private static ComponentAnalysisDialog singletonDialog = null;
71         private static final Translator trans = Application.getTranslator();
72
73
74         private final FlightConditions conditions;
75         private final Configuration configuration;
76         private final DoubleModel theta, aoa, mach, roll;
77         private final JToggleButton worstToggle;
78         private boolean fakeChange = false;
79         private AerodynamicCalculator aerodynamicCalculator;
80         private final MassCalculator massCalculator = new BasicMassCalculator();
81         
82         private final ColumnTableModel cpTableModel;
83         private final ColumnTableModel dragTableModel;
84         private final ColumnTableModel rollTableModel;
85         
86         private final JList warningList;
87         
88
89         private final List<AerodynamicForces> cpData = new ArrayList<AerodynamicForces>();
90         private final List<Coordinate> cgData = new ArrayList<Coordinate>();
91         private final List<AerodynamicForces> dragData = new ArrayList<AerodynamicForces>();
92         private double totalCD = 0;
93         private final List<AerodynamicForces> rollData = new ArrayList<AerodynamicForces>();
94         
95         
96         public ComponentAnalysisDialog(final RocketPanel rocketPanel) {
97                 ////Component analysis
98                 super(SwingUtilities.getWindowAncestor(rocketPanel), 
99                                 trans.get("componentanalysisdlg.componentanalysis"));
100                 
101                 JTable table;
102                 
103                 JPanel panel = new JPanel(new MigLayout("fill", "[][35lp::][fill][fill]"));
104                 add(panel);
105                 
106                 this.configuration = rocketPanel.getConfiguration();
107                 this.aerodynamicCalculator = rocketPanel.getAerodynamicCalculator().newInstance();
108                 
109
110                 conditions = new FlightConditions(configuration);
111                 
112                 rocketPanel.setCPAOA(0);
113                 aoa = new DoubleModel(rocketPanel, "CPAOA", UnitGroup.UNITS_ANGLE, 0, Math.PI);
114                 rocketPanel.setCPMach(Application.getPreferences().getDefaultMach());
115                 mach = new DoubleModel(rocketPanel, "CPMach", UnitGroup.UNITS_COEFFICIENT, 0);
116                 rocketPanel.setCPTheta(rocketPanel.getFigure().getRotation());
117                 theta = new DoubleModel(rocketPanel, "CPTheta", UnitGroup.UNITS_ANGLE, 0, 2 * Math.PI);
118                 rocketPanel.setCPRoll(0);
119                 roll = new DoubleModel(rocketPanel, "CPRoll", UnitGroup.UNITS_ROLL);
120                 
121                 //// Wind direction:
122                 panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.winddir")), "width 100lp!");
123                 panel.add(new UnitSelector(theta, true), "width 50lp!");
124                 BasicSlider slider = new BasicSlider(theta.getSliderModel(0, 2 * Math.PI));
125                 panel.add(slider, "growx, split 2");
126                 //// Worst button
127                 worstToggle = new JToggleButton(trans.get("componentanalysisdlg.ToggleBut.worst"));
128                 worstToggle.setSelected(true);
129                 worstToggle.addActionListener(new ActionListener() {
130                         @Override
131                         public void actionPerformed(ActionEvent e) {
132                                 stateChanged(null);
133                         }
134                 });
135                 slider.addChangeListener(new ChangeListener() {
136                         @Override
137                         public void stateChanged(ChangeEvent e) {
138                                 if (!fakeChange)
139                                         worstToggle.setSelected(false);
140                         }
141                 });
142                 panel.add(worstToggle, "");
143                 
144
145                 warningList = new JList();
146                 JScrollPane scrollPane = new JScrollPane(warningList);
147                 ////Warnings:
148                 scrollPane.setBorder(BorderFactory.createTitledBorder(trans.get("componentanalysisdlg.TitledBorder.warnings")));
149                 panel.add(scrollPane, "gap paragraph, spany 4, width 300lp!, growy 1, height :100lp:, wrap");
150                 
151                 ////Angle of attack:
152                 panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.angleofattack")), "width 100lp!");
153                 panel.add(new UnitSelector(aoa, true), "width 50lp!");
154                 panel.add(new BasicSlider(aoa.getSliderModel(0, Math.PI)), "growx, wrap");
155                 
156                 //// Mach number:
157                 panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.machnumber")), "width 100lp!");
158                 panel.add(new UnitSelector(mach, true), "width 50lp!");
159                 panel.add(new BasicSlider(mach.getSliderModel(0, 3)), "growx, wrap");
160                 
161                 //// Roll rate:
162                 panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.rollrate")), "width 100lp!");
163                 panel.add(new UnitSelector(roll, true), "width 50lp!");
164                 panel.add(new BasicSlider(roll.getSliderModel(-20 * 2 * Math.PI, 20 * 2 * Math.PI)),
165                                 "growx, wrap paragraph");
166                 
167
168                 // Stage and motor selection:
169                 //// Active stages:
170                 panel.add(new JLabel(trans.get("componentanalysisdlg.lbl.activestages")), "spanx, split, gapafter rel");
171                 panel.add(new StageSelector(configuration), "gapafter paragraph");
172                 
173                 //// Motor configuration:
174                 JLabel label = new JLabel(trans.get("componentanalysisdlg.lbl.motorconf"));
175                 label.setHorizontalAlignment(JLabel.RIGHT);
176                 panel.add(label, "growx, right");
177                 panel.add(new JComboBox(new MotorConfigurationModel(configuration)), "wrap");
178                 
179
180
181                 // Tabbed pane
182                 
183                 JTabbedPane tabbedPane = new JTabbedPane();
184                 panel.add(tabbedPane, "spanx, growx, growy");
185                 
186
187                 // Create the CP data table
188                 cpTableModel = new ColumnTableModel(
189
190                 //// Component
191                 new Column(trans.get("componentanalysisdlg.TabStability.Col.Component")) {
192                         @Override
193                         public Object getValueAt(int row) {
194                                 RocketComponent c = cpData.get(row).getComponent();
195                                 if (c instanceof Rocket) {
196                                         return "Total";
197                                 }
198                                 return c.toString();
199                         }
200                         
201                         @Override
202                         public int getDefaultWidth() {
203                                 return 200;
204                         }
205                 },
206                                 new Column(trans.get("componentanalysisdlg.TabStability.Col.CG") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
207                                         private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
208                                         
209                                         @Override
210                                         public Object getValueAt(int row) {
211                                                 return unit.toString(cgData.get(row).x);
212                                         }
213                                 },
214                                 new Column(trans.get("componentanalysisdlg.TabStability.Col.Mass") + " / " + UnitGroup.UNITS_MASS.getDefaultUnit().getUnit()) {
215                                         private Unit unit = UnitGroup.UNITS_MASS.getDefaultUnit();
216                                         
217                                         @Override
218                                         public Object getValueAt(int row) {
219                                                 return unit.toString(cgData.get(row).weight);
220                                         }
221                                 },
222                                 new Column(trans.get("componentanalysisdlg.TabStability.Col.CP") + " / " + UnitGroup.UNITS_LENGTH.getDefaultUnit().getUnit()) {
223                                         private Unit unit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
224                                         
225                                         @Override
226                                         public Object getValueAt(int row) {
227                                                 return unit.toString(cpData.get(row).getCP().x);
228                                         }
229                                 },
230                                 new Column("<html>C<sub>N<sub>" + ALPHA + "</sub></sub>") {
231                                         @Override
232                                         public Object getValueAt(int row) {
233                                                 return NOUNIT2.toString(cpData.get(row).getCP().weight);
234                                         }
235                                 }
236
237                 ) {
238                         @Override
239                         public int getRowCount() {
240                                 return cpData.size();
241                         }
242                 };
243                 
244                 table = new JTable(cpTableModel);
245                 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
246                 table.setSelectionBackground(Color.LIGHT_GRAY);
247                 table.setSelectionForeground(Color.BLACK);
248                 cpTableModel.setColumnWidths(table.getColumnModel());
249                 
250                 table.setDefaultRenderer(Object.class, new CustomCellRenderer());
251                 //              table.setShowHorizontalLines(false);
252                 //              table.setShowVerticalLines(true);
253                 
254                 JScrollPane scrollpane = new JScrollPane(table);
255                 scrollpane.setPreferredSize(new Dimension(600, 200));
256                 
257                 //// Stability and Stability information
258                 tabbedPane.addTab(trans.get("componentanalysisdlg.TabStability"), 
259                                 null, scrollpane, trans.get("componentanalysisdlg.TabStability.ttip"));
260                 
261
262
263                 // Create the drag data table
264                 dragTableModel = new ColumnTableModel(
265                                 //// Component
266                                 new Column(trans.get("componentanalysisdlg.dragTableModel.Col.Component")) {
267                                         @Override
268                                         public Object getValueAt(int row) {
269                                                 RocketComponent c = dragData.get(row).getComponent();
270                                                 if (c instanceof Rocket) {
271                                                         return "Total";
272                                                 }
273                                                 return c.toString();
274                                         }
275                                         
276                                         @Override
277                                         public int getDefaultWidth() {
278                                                 return 200;
279                                         }
280                                 },
281                                 //// <html>Pressure C<sub>D</sub>
282                                 new Column(trans.get("componentanalysisdlg.dragTableModel.Col.Pressure")) {
283                                         @Override
284                                         public Object getValueAt(int row) {
285                                                 return dragData.get(row).getPressureCD();
286                                         }
287                                 },
288                                 //// <html>Base C<sub>D</sub>
289                                 new Column(trans.get("componentanalysisdlg.dragTableModel.Col.Base")) {
290                                         @Override
291                                         public Object getValueAt(int row) {
292                                                 return dragData.get(row).getBaseCD();
293                                         }
294                                 },
295                                 //// <html>Friction C<sub>D</sub>
296                                 new Column(trans.get("componentanalysisdlg.dragTableModel.Col.friction")) {
297                                         @Override
298                                         public Object getValueAt(int row) {
299                                                 return dragData.get(row).getFrictionCD();
300                                         }
301                                 },
302                                 //// <html>Total C<sub>D</sub>
303                                 new Column(trans.get("componentanalysisdlg.dragTableModel.Col.total")) {
304                                         @Override
305                                         public Object getValueAt(int row) {
306                                                 return dragData.get(row).getCD();
307                                         }
308                                 }
309                                 ) {
310                                         @Override
311                                         public int getRowCount() {
312                                                 return dragData.size();
313                                         }
314                                 };
315                 
316
317                 table = new JTable(dragTableModel);
318                 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
319                 table.setSelectionBackground(Color.LIGHT_GRAY);
320                 table.setSelectionForeground(Color.BLACK);
321                 dragTableModel.setColumnWidths(table.getColumnModel());
322                 
323                 table.setDefaultRenderer(Object.class, new DragCellRenderer(new Color(0.5f, 1.0f, 0.5f)));
324                 //              table.setShowHorizontalLines(false);
325                 //              table.setShowVerticalLines(true);
326                 
327                 scrollpane = new JScrollPane(table);
328                 scrollpane.setPreferredSize(new Dimension(600, 200));
329                 
330                 //// Drag characteristics and Drag characteristics tooltip
331                 tabbedPane.addTab(trans.get("componentanalysisdlg.dragTabchar"), null, scrollpane,
332                                 trans.get("componentanalysisdlg.dragTabchar.ttip"));
333                 
334
335
336
337                 // Create the roll data table
338                 rollTableModel = new ColumnTableModel(
339                                 //// Component
340                                 new Column(trans.get("componentanalysisdlg.rollTableModel.Col.component")) {
341                                         @Override
342                                         public Object getValueAt(int row) {
343                                                 RocketComponent c = rollData.get(row).getComponent();
344                                                 if (c instanceof Rocket) {
345                                                         return "Total";
346                                                 }
347                                                 return c.toString();
348                                         }
349                                 },
350                                 //// Roll forcing coefficient
351                                 new Column(trans.get("componentanalysisdlg.rollTableModel.Col.rollforc")) {
352                                         @Override
353                                         public Object getValueAt(int row) {
354                                                 return rollData.get(row).getCrollForce();
355                                         }
356                                 },
357                                 //// Roll damping coefficient
358                                 new Column(trans.get("componentanalysisdlg.rollTableModel.Col.rolldamp")) {
359                                         @Override
360                                         public Object getValueAt(int row) {
361                                                 return rollData.get(row).getCrollDamp();
362                                         }
363                                 },
364                                 //// <html>Total C<sub>l</sub>
365                                 new Column(trans.get("componentanalysisdlg.rollTableModel.Col.total")) {
366                                         @Override
367                                         public Object getValueAt(int row) {
368                                                 return rollData.get(row).getCroll();
369                                         }
370                                 }
371                                 ) {
372                                         @Override
373                                         public int getRowCount() {
374                                                 return rollData.size();
375                                         }
376                                 };
377                 
378
379                 table = new JTable(rollTableModel);
380                 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
381                 table.setSelectionBackground(Color.LIGHT_GRAY);
382                 table.setSelectionForeground(Color.BLACK);
383                 rollTableModel.setColumnWidths(table.getColumnModel());
384                 
385                 scrollpane = new JScrollPane(table);
386                 scrollpane.setPreferredSize(new Dimension(600, 200));
387                 
388                 //// Roll dynamics and Roll dynamics tooltip
389                 tabbedPane.addTab(trans.get("componentanalysisdlg.rollTableModel"), null, scrollpane, 
390                                 trans.get("componentanalysisdlg.rollTableModel.ttip"));
391                 
392
393
394
395
396                 // Add the data updater to listen to changes in aoa and theta
397                 mach.addChangeListener(this);
398                 theta.addChangeListener(this);
399                 aoa.addChangeListener(this);
400                 roll.addChangeListener(this);
401                 configuration.addChangeListener(this);
402                 this.stateChanged(null);
403                 
404
405
406                 // Remove listeners when closing window
407                 this.addWindowListener(new WindowAdapter() {
408                         @Override
409                         public void windowClosed(WindowEvent e) {
410                                 //System.out.println("Closing method called: " + this);
411                                 theta.removeChangeListener(ComponentAnalysisDialog.this);
412                                 aoa.removeChangeListener(ComponentAnalysisDialog.this);
413                                 mach.removeChangeListener(ComponentAnalysisDialog.this);
414                                 roll.removeChangeListener(ComponentAnalysisDialog.this);
415                                 configuration.removeChangeListener(ComponentAnalysisDialog.this);
416                                 //System.out.println("SETTING NAN VALUES");
417                                 rocketPanel.setCPAOA(Double.NaN);
418                                 rocketPanel.setCPTheta(Double.NaN);
419                                 rocketPanel.setCPMach(Double.NaN);
420                                 rocketPanel.setCPRoll(Double.NaN);
421                                 singletonDialog = null;
422                         }
423                 });
424                 
425                 //// Reference length:
426                 panel.add(new StyledLabel(trans.get("componentanalysisdlg.lbl.reflenght"), -1),
427                                 "span, split, gapleft para, gapright rel");
428                 DoubleModel dm = new DoubleModel(conditions, "RefLength", UnitGroup.UNITS_LENGTH);
429                 UnitSelector sel = new UnitSelector(dm, true);
430                 sel.resizeFont(-1);
431                 panel.add(sel, "gapright para");
432                 
433                 //// Reference area: 
434                 panel.add(new StyledLabel(trans.get("componentanalysisdlg.lbl.refarea"), -1), "gapright rel");
435                 dm = new DoubleModel(conditions, "RefArea", UnitGroup.UNITS_AREA);
436                 sel = new UnitSelector(dm, true);
437                 sel.resizeFont(-1);
438                 panel.add(sel, "wrap");
439                 
440
441
442                 // Buttons
443                 JButton button;
444                 
445                 // TODO: LOW: printing
446                 //              button = new JButton("Print");
447                 //              button.addActionListener(new ActionListener() {
448                 //                      public void actionPerformed(ActionEvent e) {
449                 //                              try {
450                 //                                      table.print();
451                 //                              } catch (PrinterException e1) {
452                 //                                      JOptionPane.showMessageDialog(ComponentAnalysisDialog.this, 
453                 //                                                      "An error occurred while printing.", "Print error",
454                 //                                                      JOptionPane.ERROR_MESSAGE);
455                 //                              }
456                 //                      }
457                 //              });
458                 //              panel.add(button,"tag ok");
459                 
460                 //button = new JButton("Close");
461                 //Close button
462                 button = new JButton(trans.get("dlg.but.close"));
463                 button.addActionListener(new ActionListener() {
464                         public void actionPerformed(ActionEvent e) {
465                                 ComponentAnalysisDialog.this.dispose();
466                         }
467                 });
468                 panel.add(button, "span, split, tag cancel");
469                 
470
471                 this.setLocationByPlatform(true);
472                 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
473                 pack();
474                 
475                 GUIUtil.setDisposableDialogOptions(this, null);
476         }
477         
478         
479
480         /**
481          * Updates the data in the table and fires a table data change event.
482          */
483         @Override
484         public void stateChanged(ChangeEvent e) {
485                 AerodynamicForces forces;
486                 WarningSet set = new WarningSet();
487                 conditions.setAOA(aoa.getValue());
488                 conditions.setTheta(theta.getValue());
489                 conditions.setMach(mach.getValue());
490                 conditions.setRollRate(roll.getValue());
491                 conditions.setReference(configuration);
492                 
493                 if (worstToggle.isSelected()) {
494                         aerodynamicCalculator.getWorstCP(configuration, conditions, null);
495                         if (!MathUtil.equals(conditions.getTheta(), theta.getValue())) {
496                                 fakeChange = true;
497                                 theta.setValue(conditions.getTheta()); // Fires a stateChanged event
498                                 fakeChange = false;
499                                 return;
500                         }
501                 }
502                 
503                 Map<RocketComponent, AerodynamicForces> aeroData =
504                                 aerodynamicCalculator.getForceAnalysis(configuration, conditions, set);
505                 Map<RocketComponent, Coordinate> massData =
506                                 massCalculator.getCGAnalysis(configuration, MassCalcType.LAUNCH_MASS);
507                 
508
509                 cpData.clear();
510                 cgData.clear();
511                 dragData.clear();
512                 rollData.clear();
513                 for (RocketComponent c : configuration) {
514                         forces = aeroData.get(c);
515                         Coordinate cg = massData.get(c);
516                         
517                         if (forces == null)
518                                 continue;
519                         if (forces.getCP() != null) {
520                                 cpData.add(forces);
521                                 cgData.add(cg);
522                         }
523                         if (!Double.isNaN(forces.getCD())) {
524                                 dragData.add(forces);
525                         }
526                         if (c instanceof FinSet) {
527                                 rollData.add(forces);
528                         }
529                 }
530                 forces = aeroData.get(configuration.getRocket());
531                 if (forces != null) {
532                         cpData.add(forces);
533                         cgData.add(massData.get(configuration.getRocket()));
534                         dragData.add(forces);
535                         rollData.add(forces);
536                         totalCD = forces.getCD();
537                 } else {
538                         totalCD = 0;
539                 }
540                 
541                 // Set warnings
542                 if (set.isEmpty()) {
543                         warningList.setListData(new String[] {
544                                         "<html><i><font color=\"gray\">No warnings.</font></i>"
545                         });
546                 } else {
547                         warningList.setListData(new Vector<Warning>(set));
548                 }
549                 
550                 cpTableModel.fireTableDataChanged();
551                 dragTableModel.fireTableDataChanged();
552                 rollTableModel.fireTableDataChanged();
553         }
554         
555         
556         private class CustomCellRenderer extends JLabel implements TableCellRenderer {
557                 private final Font normalFont;
558                 private final Font boldFont;
559                 
560                 public CustomCellRenderer() {
561                         super();
562                         normalFont = getFont();
563                         boldFont = normalFont.deriveFont(Font.BOLD);
564                 }
565                 
566                 @Override
567                 public Component getTableCellRendererComponent(JTable table, Object value,
568                                 boolean isSelected, boolean hasFocus, int row, int column) {
569                         
570                         this.setText(value.toString());
571                         
572                         if ((row < 0) || (row >= cpData.size()))
573                                 return this;
574                         
575                         if (cpData.get(row).getComponent() instanceof Rocket) {
576                                 this.setFont(boldFont);
577                         } else {
578                                 this.setFont(normalFont);
579                         }
580                         return this;
581                 }
582         }
583         
584         
585
586         private class DragCellRenderer extends JLabel implements TableCellRenderer {
587                 private final Font normalFont;
588                 private final Font boldFont;
589                 
590                 
591                 public DragCellRenderer(Color baseColor) {
592                         super();
593                         normalFont = getFont();
594                         boldFont = normalFont.deriveFont(Font.BOLD);
595                 }
596                 
597                 @Override
598                 public Component getTableCellRendererComponent(JTable table, Object value,
599                                 boolean isSelected, boolean hasFocus, int row, int column) {
600                         
601                         if (value instanceof Double) {
602                                 
603                                 // A drag coefficient
604                                 double cd = (Double) value;
605                                 this.setText(String.format("%.2f (%.0f%%)", cd, 100 * cd / totalCD));
606                                 
607                                 float r = (float) (cd / 1.5);
608                                 
609                                 float hue = MathUtil.clamp(0.3333f * (1 - 2.0f * r), 0, 0.3333f);
610                                 float sat = MathUtil.clamp(0.8f * r + 0.1f * (1 - r), 0, 1);
611                                 float val = 1.0f;
612                                 
613                                 this.setBackground(Color.getHSBColor(hue, sat, val));
614                                 this.setOpaque(true);
615                                 this.setHorizontalAlignment(SwingConstants.CENTER);
616                                 
617                         } else {
618                                 
619                                 // Other
620                                 this.setText(value.toString());
621                                 this.setOpaque(false);
622                                 this.setHorizontalAlignment(SwingConstants.LEFT);
623                                 
624                         }
625                         
626                         if ((row < 0) || (row >= dragData.size()))
627                                 return this;
628                         
629                         if ((dragData.get(row).getComponent() instanceof Rocket) || (column == 4)) {
630                                 this.setFont(boldFont);
631                         } else {
632                                 this.setFont(normalFont);
633                         }
634                         return this;
635                 }
636         }
637         
638         
639         /////////  Singleton implementation
640         
641         public static void showDialog(RocketPanel rocketpanel) {
642                 if (singletonDialog != null)
643                         singletonDialog.dispose();
644                 singletonDialog = new ComponentAnalysisDialog(rocketpanel);
645                 singletonDialog.setVisible(true);
646         }
647         
648         public static void hideDialog() {
649                 if (singletonDialog != null)
650                         singletonDialog.dispose();
651         }
652         
653 }