updates for 0.9.4
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 10 Oct 2009 13:13:32 +0000 (13:13 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 10 Oct 2009 13:13:32 +0000 (13:13 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@27 180e2498-e6e9-4542-8430-84ac67f01cd8

33 files changed:
.classpath
ChangeLog
TODO
build.xml
lib-test/hamcrest-core-1.1.jar [deleted file]
lib-test/hamcrest-library-1.1.jar [deleted file]
lib-test/jmock-2.5.1.jar [deleted file]
lib-test/jmock-junit4-2.5.1.jar [deleted file]
src/net/sf/openrocket/database/Databases.java
src/net/sf/openrocket/gui/components/DescriptionArea.java
src/net/sf/openrocket/gui/dialogs/AboutDialog.java
src/net/sf/openrocket/gui/dialogs/ComponentAnalysisDialog.java
src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java
src/net/sf/openrocket/gui/dialogs/UpdateInfoDialog.java
src/net/sf/openrocket/gui/dialogs/preferences/PreferencesDialog.java
src/net/sf/openrocket/gui/figureelements/RocketInfo.java
src/net/sf/openrocket/gui/main/BasicFrame.java
src/net/sf/openrocket/gui/main/SimulationEditDialog.java
src/net/sf/openrocket/gui/plot/PlotPanel.java [deleted file]
src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java [new file with mode: 0644]
src/net/sf/openrocket/gui/scalefigure/RocketPanel.java
src/net/sf/openrocket/rocketcomponent/Rocket.java
src/net/sf/openrocket/rocketcomponent/Transition.java
src/net/sf/openrocket/unit/DegreeUnit.java
src/net/sf/openrocket/unit/Unit.java
src/net/sf/openrocket/unit/UnitGroup.java
src/net/sf/openrocket/util/Chars.java [new file with mode: 0644]
src/net/sf/openrocket/util/ConcurrentProgressMonitor.java
src/net/sf/openrocket/util/Pair.java
src/net/sf/openrocket/util/Prefs.java
test/net/sf/openrocket/motor/ManufacturerTest.java
test/net/sf/openrocket/util/CoordinateTest.java
test/net/sf/openrocket/util/MathUtilTest.java

index fdfb5235aed67e9ad07be9409fa568d4b0dff117..a98ed5b2fcfc8b840a29f460e4eab8f0710277f3 100644 (file)
@@ -7,10 +7,6 @@
        <classpathentry kind="lib" path="/home/sampo/Projects/OpenRocket/lib/miglayout15-swing.jar"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JCommon 1.0.16"/>
        <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JFreeChart 1.0.13"/>
-       <classpathentry kind="lib" path="lib-test/hamcrest-core-1.1.jar"/>
-       <classpathentry kind="lib" path="lib-test/hamcrest-library-1.1.jar"/>
-       <classpathentry kind="lib" path="lib-test/jmock-2.5.1.jar"/>
-       <classpathentry kind="lib" path="lib-test/jmock-junit4-2.5.1.jar"/>
        <classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/>
        <classpathentry kind="lib" path="lib-test/junit-4.7.jar"/>
        <classpathentry kind="output" path="bin"/>
index 99ac81b157a70c2369757bc814da762ec12097d5..91949f53218d4314625eadb0774ff8e34d9b7569 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2009-10-10  Sampo Niskanen
+
+       * Removed non-ASCII characters from source code files
+       * Created Chars class for non-ASCII characters
+       * Added styrofoam materials
+
 2009-10-04  Sampo Niskanen
 
        * [BUG] Fixed too high configuration dialogs
diff --git a/TODO b/TODO
index 9bca110f251a49a1b1da53c35c8cd16dcab86863..9fff6762e5b88f848a5a30a3463caf02fe8089f8 100644 (file)
--- a/TODO
+++ b/TODO
@@ -10,18 +10,17 @@ Must-have:
 
 Bugs:
 
-- Simulation plot dialog forces dialog one button row too high (All/None)
 - Unit tests fail from ant script
 
 
 Maybe:
 
-- Windows executable wrapper (launch4j)
 - Inform user about software updates
 
 
 Postponed:
 
+- Windows executable wrapper (launch4j)
 - Allow only one instance of OpenRocket running (RMI communication)
 - Only schedule rocket figure update instead of each time updating it
 - Reading (writing) .RKT format
@@ -79,4 +78,5 @@ In 0.9.4:
 - JTree text is cropped unnecessarily
 - Allow editing user-defined materials
 - [BUG] All configuration dialogs too high
+- Simulation plot dialog forces dialog one button row too high (All/None)
 
index b77dd04ff00861074af6af99cdf6f43996891a61..94676a627be00d13daf5843372a55217cbba9013 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -90,7 +90,7 @@
                        <fileset dir="." includes="*">
                                <type type="file"/>
                        </fileset>
-                       <fileset dir="." includes="datafiles/ lib/ pix/ src/ test/"/>
+                       <fileset dir="." includes="datafiles/ lib/ lib-test/ pix/ src/ test/"/>
                </copy>
                <zip destfile="${dist.src}" basedir="${build.dir}" includes="${pkgname}/"/>
                <delete dir="${build.dir}/${pkgname}"/>
        
        
        <!-- DIST-BIN -->
-       <target name="dist-bin" depends="checktodo,clean,unittest,jar">
+       <target name="dist-bin" depends="check,clean,unittest,jar">
                <move file="${jar.file}" tofile="${dist.bin}"/>
        </target>
 
        </target>
        
        
+       <!-- CHECK -->
+       <target name="check" depends="checktodo,checkascii"/>
+       
        <!-- CHECK TODOs -->
        <target name="checktodo">
                <tempfile property="todo.file" prefix="checktodo-"/>
                        <fileset dir="${src.dir}">
                            <include name="**/*.java"/>
                        </fileset>
+                       <fileset dir="${src-test.dir}">
+                           <include name="**/*.java"/>
+                       </fileset>
                        <filterchain>
                                <linecontainsregexp>
                                        <regexp pattern="TODO:.*CRITICAL"/>
@@ -148,6 +154,31 @@ ${criticaltodos}</fail>
        </target>
        
        
+       <!-- CHECK TODOs -->
+       <target name="checkascii">
+               <tempfile property="ascii.file" prefix="checkascii-"/>
+               <echo>Checking project for non-ASCII characters.</echo>
+               <concat destfile="${ascii.file}">
+                       <fileset dir="${src.dir}">
+                           <include name="**/*.java"/>
+                       </fileset>
+                       <fileset dir="${src-test.dir}">
+                           <include name="**/*.java"/>
+                       </fileset>
+                       <filterchain>
+                               <linecontainsregexp>
+                                       <regexp pattern="\P{ASCII}"/>
+                               </linecontainsregexp>
+                       </filterchain>
+               </concat>
+               <loadfile srcfile="${ascii.file}" property="nonascii"/>
+               <delete file="${ascii.file}"/>
+               <fail if="nonascii">Non-ASCII characters exist in project:
+${nonascii}</fail>
+               <echo>No non-ASCII characters in project.</echo>
+       </target>
+       
+       
        <!--  Unit tests  -->
        <target name="unittest" description="Execute unit tests" depends="build">
                <echo>Building unit tests</echo>
@@ -156,7 +187,7 @@ ${criticaltodos}</fail>
                
                <echo>Running unit tests</echo>
                <mkdir dir="tmp/rawtestoutput"/>
-               <junit printsummary="true" failureproperty="junit.failure">
+               <junit fork="yes" forkmode="once" printsummary="true" failureproperty="junit.failure">
                        <classpath>
                                <path refid="test-classpath"/>
                                <path location="${basedir}"/>
@@ -164,6 +195,7 @@ ${criticaltodos}</fail>
                        <batchtest todir="tmp/rawtestoutput">
                                <fileset dir="${build-test.dir}">
                                        <include name="**/*Test*.class" />
+                                       <exclude name="**/*$*.class" />
                                        <exclude name="Test.class" />
                                </fileset>
                                <formatter type="xml"/>
@@ -173,7 +205,10 @@ ${criticaltodos}</fail>
                        <fileset dir="tmp/rawtestoutput"/>
                        <report todir="tmp/test-reports"/>
                </junitreport>
-               <fail if="junit.failure" message="Unit test(s) failed.  See reports!"/>
+               <fail if="junit.failure" message="Unit test(s) failed.  See report in ${basedir}/tmp/test-reports/index.html"/>
+               <echo>
+       Unit tests passed successfully.
+               </echo>
        </target>
     
 </project>
\ No newline at end of file
diff --git a/lib-test/hamcrest-core-1.1.jar b/lib-test/hamcrest-core-1.1.jar
deleted file mode 100644 (file)
index 5f1d5ce..0000000
Binary files a/lib-test/hamcrest-core-1.1.jar and /dev/null differ
diff --git a/lib-test/hamcrest-library-1.1.jar b/lib-test/hamcrest-library-1.1.jar
deleted file mode 100644 (file)
index 40610c9..0000000
Binary files a/lib-test/hamcrest-library-1.1.jar and /dev/null differ
diff --git a/lib-test/jmock-2.5.1.jar b/lib-test/jmock-2.5.1.jar
deleted file mode 100644 (file)
index 4415dfb..0000000
Binary files a/lib-test/jmock-2.5.1.jar and /dev/null differ
diff --git a/lib-test/jmock-junit4-2.5.1.jar b/lib-test/jmock-junit4-2.5.1.jar
deleted file mode 100644 (file)
index fb3697a..0000000
Binary files a/lib-test/jmock-junit4-2.5.1.jar and /dev/null differ
index 9ec9f589a09b17c93e7f304ce26370036ac7b231..d5d7f465147c5da8acb444b778529ed68783523c 100644 (file)
@@ -79,7 +79,7 @@ public class Databases {
                BULK_MATERIAL.add(new Material.Bulk("Cardboard",         680, false));
                BULK_MATERIAL.add(new Material.Bulk("Carbon fiber",     1780, false));
                BULK_MATERIAL.add(new Material.Bulk("Cork",                      240, false));
-               BULK_MATERIAL.add(new Material.Bulk("Depron",             40, false));
+               BULK_MATERIAL.add(new Material.Bulk("Depron (XPS)",       40, false));
                BULK_MATERIAL.add(new Material.Bulk("Fiberglass",       1850, false));
                BULK_MATERIAL.add(new Material.Bulk("Kraft phenolic",950, false));
                BULK_MATERIAL.add(new Material.Bulk("Maple",             755, false));
@@ -90,7 +90,8 @@ public class Databases {
                BULK_MATERIAL.add(new Material.Bulk("Polystyrene",  1050, false));
                BULK_MATERIAL.add(new Material.Bulk("PVC",                      1390, false));
                BULK_MATERIAL.add(new Material.Bulk("Spruce",            450, false));
-               // TODO: CRITICAL: Add styrofoam
+               BULK_MATERIAL.add(new Material.Bulk("Styrofoam generic (EPS)", 20, false));
+               BULK_MATERIAL.add(new Material.Bulk("Styrofoam / Blue Foam (XPS)", 32, false));
                BULK_MATERIAL.add(new Material.Bulk("Quantum tubing",1050, false));
                
                SURFACE_MATERIAL.add(new Material.Surface("Ripstop nylon",                      0.067, false));
index ac26ae837d7c01737c9e7c853d0fd8de1e84805d..d3d568b2dac9587e7613473f980926a2275ac26f 100644 (file)
@@ -13,11 +13,15 @@ public class DescriptionArea extends JScrollPane {
 
        private final JEditorPane editorPane;
        
+
        public DescriptionArea(int rows) {
-               this(rows, -1);
+               this("", rows, -1);
        }
-       
        public DescriptionArea(int rows, float size) {
+               this("", rows, size);
+       }
+       
+       public DescriptionArea(String text, int rows, float size) {
                super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
                
@@ -41,6 +45,7 @@ public class DescriptionArea extends JScrollPane {
                this.setPreferredSize(dim);
                
                this.setViewportView(editorPane);
+               this.setText(text);
        }
        
        public void setText(String txt) {
index 41014f49d858ab1f0665fecab2134a4e1834cbae..73d12576630fa999958303d050763c4c390eee90 100644 (file)
@@ -12,6 +12,7 @@ import javax.swing.JPanel;
 import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.gui.components.StyledLabel;
 import net.sf.openrocket.gui.components.URLLabel;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.GUIUtil;
 import net.sf.openrocket.util.Icons;
 import net.sf.openrocket.util.Prefs;
@@ -42,7 +43,7 @@ public class AboutDialog extends JDialog {
                        panel.add(new StyledLabel(" ", -1), "ax 50%, growy, wrap para");
                }
                
-               panel.add(new StyledLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"), 
+               panel.add(new StyledLabel("Copyright " + Chars.COPY +" 2007-2009 Sampo Niskanen"), 
                                "ax 50%, growy, wrap para");
                
                panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para");
index cd7d9c98b757cc42ad3674940864d3249dabb7d1..6ff42883a2a5e19873c61975863b411144d76d12 100644 (file)
@@ -1,6 +1,7 @@
 package net.sf.openrocket.gui.dialogs;
 
 import static net.sf.openrocket.unit.Unit.NOUNIT2;
+import static net.sf.openrocket.util.Chars.ALPHA;
 
 import java.awt.Color;
 import java.awt.Component;
@@ -45,8 +46,8 @@ import net.sf.openrocket.gui.adaptors.ColumnTableModel;
 import net.sf.openrocket.gui.adaptors.DoubleModel;
 import net.sf.openrocket.gui.adaptors.MotorConfigurationModel;
 import net.sf.openrocket.gui.components.BasicSlider;
-import net.sf.openrocket.gui.components.StyledLabel;
 import net.sf.openrocket.gui.components.StageSelector;
+import net.sf.openrocket.gui.components.StyledLabel;
 import net.sf.openrocket.gui.components.UnitSelector;
 import net.sf.openrocket.gui.scalefigure.RocketPanel;
 import net.sf.openrocket.rocketcomponent.Configuration;
@@ -203,7 +204,7 @@ public class ComponentAnalysisDialog extends JDialog implements ChangeListener {
                                                return unit.toString(cpData.get(row).cp.x);
                                        }
                                },
-                               new Column("<html>C<sub>N<sub>\u03b1</sub></sub>") {
+                               new Column("<html>C<sub>N<sub>"+ALPHA+"</sub></sub>") {
                                        @Override public Object getValueAt(int row) {
                                                return NOUNIT2.toString(cpData.get(row).cp.weight);
                                        }
index 550dee56d4005554bbb1554a4e4a61b4d9ec8f83..44eba2e64d448b748a41f007d0ad97d5200289f3 100644 (file)
@@ -31,6 +31,7 @@ import net.sf.openrocket.motor.Motor;
 import net.sf.openrocket.rocketcomponent.MotorMount;
 import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.GUIUtil;
 
 public class EditMotorConfigurationDialog extends JDialog {
@@ -466,7 +467,7 @@ public class EditMotorConfigurationDialog extends JDialog {
                        String str = motor.getDesignation(mount.getMotorDelay(id)); 
                        int count = mount.getMotorCount();
                        if (count > 1) {
-                               str = "" + count + "\u00d7 " + str;
+                               str = "" + count + Chars.TIMES + " " + str;
                        }
                        return str;
                }
@@ -482,15 +483,11 @@ public class EditMotorConfigurationDialog extends JDialog {
                        String name = mount.toString();
                        int count = mount.getMotorCount();
                        if (count > 1) {
-                               name = name + " (\u00d7" + count + ")";
+                               name = name + " (" + Chars.TIMES + count + ")";
                        }
                        return name;
                }
                
-               
        }
-
-       
-       
        
 }
index 518403c96e5e95d3d2b48a18337866d3c14e3df8..f288004f40f5d36d4d5281350076b030eab6399e 100644 (file)
@@ -14,6 +14,7 @@ import javax.swing.JPanel;
 import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.communication.UpdateInfo;
 import net.sf.openrocket.gui.components.URLLabel;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.ComparablePair;
 import net.sf.openrocket.util.GUIUtil;
 import net.sf.openrocket.util.Icons;
@@ -45,7 +46,8 @@ public class UpdateInfoDialog extends JDialog {
                                if (count >= 4 && n != updates.get(i).getU())
                                        break;
                                n = updates.get(i).getU();
-                               panel.add(new JLabel("   \u2022 " + updates.get(i).getV()), "wrap 0px");
+                               panel.add(new JLabel("   " + Chars.BULLET + " " + updates.get(i).getV()), 
+                                               "wrap 0px");
                                count++;
                        }
                }
index 38cc36fe7b081bd6516328728fef186b265462a1..e5f38260d9f4e66a90c37b89dfe68d0fc89b9996 100644 (file)
@@ -12,14 +12,21 @@ import java.util.List;
 import javax.swing.AbstractListModel;
 import javax.swing.ComboBoxModel;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JDialog;
 import javax.swing.JLabel;
+import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.JProgressBar;
 import javax.swing.JTabbedPane;
+import javax.swing.Timer;
 
 import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.communication.UpdateInfo;
+import net.sf.openrocket.communication.UpdateInfoRetriever;
 import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
 import net.sf.openrocket.unit.Unit;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.GUIUtil;
@@ -40,7 +47,7 @@ public class PreferencesDialog extends JDialog {
 
                tabbedPane.addTab("Units", null, unitsPane(), "Default units");
                tabbedPane.addTab("Materials", null, new MaterialEditPanel(), "Custom materials");
-               tabbedPane.addTab("Confirmation", null, confirmationPane(), "Confirmation dialog settings");
+               tabbedPane.addTab("Options", null, optionsPane(), "Miscellaneous options");
                
                
                JButton close = new JButton("Close");
@@ -68,16 +75,39 @@ public class PreferencesDialog extends JDialog {
        }
        
        
-       private JPanel confirmationPane() {
-               JPanel panel = new JPanel(new MigLayout("fill"));
+       private JPanel optionsPane() {
+               JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n"));
                
-               panel.add(new JLabel("Position to insert new body components:"));
+               
+               panel.add(new JLabel("Position to insert new body components:"), "gapright para");
                panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
-                               "Always ask", "Insert in middle", "Add to end")), "wrap para, sg combos");
+                               "Always ask", "Insert in middle", "Add to end")), "wrap para, growx, sg combos");
                
                panel.add(new JLabel("Confirm deletion of simulations:"));
                panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
-                               "Delete", "Confirm", true)), "wrap para, sg combos");
+                               "Delete", "Confirm", true)), "wrap 40lp, growx, sg combos");
+               
+               
+               final JCheckBox softwareUpdateBox = new JCheckBox("Check for software updates");
+               softwareUpdateBox.setSelected(Prefs.getCheckUpdates());
+               softwareUpdateBox.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               Prefs.setCheckUpdates(softwareUpdateBox.isSelected());
+                       }
+               });
+               panel.add(softwareUpdateBox);
+               
+               JButton button = new JButton("Check now");
+               button.setToolTipText("Check for software updates now");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               checkForUpdates();
+                       }
+               });
+               panel.add(button, "right, wrap");
+               
                
                return panel;
        }
@@ -350,6 +380,73 @@ public class PreferencesDialog extends JDialog {
        }
        
        
+       private void checkForUpdates() {
+               final UpdateInfoRetriever retriever = new UpdateInfoRetriever();
+               retriever.start();
+               
+               
+               // Progress dialog
+               final JDialog dialog = new JDialog(this, ModalityType.APPLICATION_MODAL);
+               JPanel panel = new JPanel(new MigLayout());
+               
+               panel.add(new JLabel("Checking for updates..."), "wrap");
+               
+               JProgressBar bar = new JProgressBar();
+               bar.setIndeterminate(true);
+               panel.add(bar, "growx, wrap para");
+               
+               JButton cancel = new JButton("Cancel");
+               cancel.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               dialog.dispose();
+                       }
+               });
+               panel.add(cancel, "right");
+               dialog.add(panel);
+               
+               GUIUtil.setDisposableDialogOptions(dialog, cancel);
+               
+               
+               // Timer to monitor progress
+               final Timer timer = new Timer(100, null);
+               final long startTime = System.currentTimeMillis();
+
+               ActionListener listener = new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (!retriever.isRunning() || startTime+10000 < System.currentTimeMillis()) {
+                                       timer.stop();
+                                       dialog.dispose();
+                               }
+                       }
+               };
+               timer.addActionListener(listener);
+               timer.start();
+               
+               
+               // Wait for action
+               dialog.setVisible(true);
+               
+               
+               // Check result
+               UpdateInfo info = retriever.getUpdateInfo();
+               if (info == null) {
+                       JOptionPane.showMessageDialog(this, 
+                                       "An error occurred while communicating with the server.", 
+                                       "Unable to retrieve update information", JOptionPane.WARNING_MESSAGE, null);
+               } else if (info.getLatestVersion() == null || 
+                               info.getLatestVersion().equals("") ||
+                               Prefs.getVersion().equalsIgnoreCase(info.getLatestVersion())) {
+                       JOptionPane.showMessageDialog(this, 
+                                       "You are running the latest version of OpenRocket.", 
+                                       "No updates available", JOptionPane.INFORMATION_MESSAGE, null);
+               } else {
+                       new UpdateInfoDialog(info).setVisible(true);
+               }
+               
+       }
+       
        
        ////////  Singleton implementation  ////////
        
index a70e87311ee8a48648a9801f5d71c02c646576bc..f573e182b2d3e93d1d0d4fcfd9c2db9664ca9277 100644 (file)
@@ -1,5 +1,7 @@
 package net.sf.openrocket.gui.figureelements;
 
+import static net.sf.openrocket.util.Chars.*;
+
 import java.awt.Color;
 import java.awt.Font;
 import java.awt.Graphics2D;
@@ -16,7 +18,6 @@ import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.Prefs;
 
 
-
 /**
  * A <code>FigureElement</code> that draws text at different positions in the figure
  * with general data about the rocket.
@@ -166,10 +167,10 @@ public class RocketInfo implements FigureElement {
                
                at = "at M="+UnitGroup.UNITS_COEFFICIENT.getDefaultUnit().toStringUnit(mach);
                if (!Double.isNaN(aoa)) {
-                       at += " \u03b1=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
+                       at += " "+ALPHA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(aoa);
                }
                if (!Double.isNaN(theta)) {
-                       at += " \u0398=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
+                       at += " "+THETA+"=" + UnitGroup.UNITS_ANGLE.getDefaultUnit().toStringUnit(theta);
                }
                
                GlyphVector cgValue = createText(
index fcf74ca29abff7bcad5304e826090265db1e2047..f7777a0025c03cc306ef39355e832342deacdd77 100644 (file)
@@ -1248,9 +1248,15 @@ public class BasicFrame extends JFrame {
                        public void actionPerformed(ActionEvent e) {
                                if (!updateInfo.isRunning()) {
                                        timer.stop();
+                                       
+                                       String current = Prefs.getVersion();
+                                       String last = Prefs.getString(Prefs.LAST_UPDATE, "");
 
                                        UpdateInfo info = updateInfo.getUpdateInfo();
-                                       if (info != null && !Prefs.getVersion().equals(info.getLatestVersion())) {
+                                       if (info != null && info.getLatestVersion() != null &&
+                                                       !current.equals(info.getLatestVersion()) &&
+                                                       !last.equals(info.getLatestVersion())) {
+                                               Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
                                                new UpdateInfoDialog(info).setVisible(true);
                                        }
                                }
index 81234d3eaf5d03482b0de717ff5562c352aa1166..2b3829d77763a05eacddce633fd246baac66f640 100644 (file)
@@ -42,7 +42,7 @@ import net.sf.openrocket.gui.components.SimulationExportPanel;
 import net.sf.openrocket.gui.components.UnitSelector;
 import net.sf.openrocket.gui.plot.Axis;
 import net.sf.openrocket.gui.plot.PlotConfiguration;
-import net.sf.openrocket.gui.plot.PlotPanel;
+import net.sf.openrocket.gui.plot.SimulationPlotPanel;
 import net.sf.openrocket.rocketcomponent.Configuration;
 import net.sf.openrocket.simulation.FlightData;
 import net.sf.openrocket.simulation.FlightDataBranch;
@@ -52,6 +52,7 @@ import net.sf.openrocket.simulation.SimulationListener;
 import net.sf.openrocket.simulation.listeners.CSVSaveListener;
 import net.sf.openrocket.unit.Unit;
 import net.sf.openrocket.unit.UnitGroup;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.GUIUtil;
 import net.sf.openrocket.util.Icons;
 import net.sf.openrocket.util.Prefs;
@@ -393,7 +394,7 @@ public class SimulationEditDialog extends JDialog {
                spin.setToolTipText(tip);
                sub.add(spin,"w 65lp!");
                
-               label = new JLabel("\u00b0 N");
+               label = new JLabel(Chars.DEGREE + " N");
                label.setToolTipText(tip);
                sub.add(label,"growx");
                slider = new BasicSlider(m.getSliderModel(-90, 90));
@@ -745,7 +746,7 @@ public class SimulationEditDialog extends JDialog {
                
                
                if (true)
-                       return new PlotPanel(simulation);
+                       return new SimulationPlotPanel(simulation);
                
                JPanel panel = new JPanel(new MigLayout("fill"));
                
diff --git a/src/net/sf/openrocket/gui/plot/PlotPanel.java b/src/net/sf/openrocket/gui/plot/PlotPanel.java
deleted file mode 100644 (file)
index a2cbea7..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-package net.sf.openrocket.gui.plot;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.util.Arrays;
-import java.util.EnumSet;
-
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTable;
-import javax.swing.SwingUtilities;
-import javax.swing.table.AbstractTableModel;
-import javax.swing.table.TableColumn;
-import javax.swing.table.TableColumnModel;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.document.Simulation;
-import net.sf.openrocket.gui.components.StyledLabel;
-import net.sf.openrocket.gui.components.UnitSelector;
-import net.sf.openrocket.simulation.FlightDataBranch;
-import net.sf.openrocket.simulation.FlightEvent;
-import net.sf.openrocket.simulation.FlightDataBranch.Type;
-import net.sf.openrocket.unit.Unit;
-import net.sf.openrocket.util.GUIUtil;
-import net.sf.openrocket.util.Icons;
-import net.sf.openrocket.util.Pair;
-
-public class PlotPanel extends JPanel {
-       
-       // TODO: LOW: Should these be somewhere else?
-       public static final int AUTO = -1;
-       public static final int LEFT = 0;
-       public static final int RIGHT = 1;
-       
-       public static final String AUTO_NAME = "Auto";
-       public static final String LEFT_NAME = "Left";
-       public static final String RIGHT_NAME = "Right";
-       
-       private static final String CUSTOM = "Custom";
-       
-       /** The "Custom" configuration - not to be used for anything other than the title. */
-       private static final PlotConfiguration CUSTOM_CONFIGURATION;
-       static {
-               CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
-       }
-       
-       /** The array of presets for the combo box. */
-       private static final PlotConfiguration[] PRESET_ARRAY;
-       static {
-               PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS, 
-                               PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
-               PRESET_ARRAY[PRESET_ARRAY.length-1] = CUSTOM_CONFIGURATION;
-       }
-       
-       
-       
-       /** The current default configuration, set each time a plot is made. */
-       private static PlotConfiguration defaultConfiguration =
-               PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
-       
-       
-       private final Simulation simulation;
-       private final FlightDataBranch.Type[] types;
-       private PlotConfiguration configuration;
-       
-
-       private JComboBox configurationSelector;
-       
-       private JComboBox domainTypeSelector;
-       private UnitSelector domainUnitSelector;
-       
-       private JPanel typeSelectorPanel;
-       private FlightEventTableModel eventTableModel;
-       
-       
-       private int modifying = 0;
-       
-
-       public PlotPanel(final Simulation simulation) {
-               super(new MigLayout("fill"));
-               
-               this.simulation = simulation;
-               if (simulation.getSimulatedData() == null  ||
-                               simulation.getSimulatedData().getBranchCount()==0) {
-                       throw new IllegalArgumentException("Simulation contains no data.");
-               }
-               FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
-               types = branch.getTypes();
-               
-               // TODO: LOW: Revert to custom if data type is not available.
-               configuration = defaultConfiguration.clone();
-               
-               
-               ////  Configuration selector
-               
-               // Setup the combo box
-               configurationSelector = new JComboBox(PRESET_ARRAY);
-               for (PlotConfiguration config: PRESET_ARRAY) {
-                       if (config.getName().equals(configuration.getName())) {
-                               configurationSelector.setSelectedItem(config);
-                       }
-               }
-               configurationSelector.addItemListener(new ItemListener() {
-                       @Override
-                       public void itemStateChanged(ItemEvent e) {
-                               if (modifying > 0)
-                                       return;
-                               PlotConfiguration conf = (PlotConfiguration)configurationSelector.getSelectedItem();
-                               if (conf == CUSTOM_CONFIGURATION)
-                                       return;
-                               modifying++;
-                               configuration = conf.clone().resetUnits();
-                               updatePlots();
-                               modifying--;
-                       }
-               });
-               this.add(new JLabel("Preset plot configurations: "), "spanx, split");
-               this.add(configurationSelector,"growx, wrap 30lp");
-
-               
-               
-               //// X axis
-               
-               
-               this.add(new JLabel("X axis type:"), "spanx, split");
-               domainTypeSelector = new JComboBox(types);
-               domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
-               domainTypeSelector.addItemListener(new ItemListener() {
-                       @Override
-                       public void itemStateChanged(ItemEvent e) {
-                               if (modifying > 0)
-                                       return;
-                               FlightDataBranch.Type type = (Type) domainTypeSelector.getSelectedItem();
-                               configuration.setDomainAxisType(type);
-                               domainUnitSelector.setUnitGroup(type.getUnitGroup());
-                               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
-                               setToCustom();
-                       }
-               });
-               this.add(domainTypeSelector, "gapright para");
-
-               
-               this.add(new JLabel("Unit:"));
-               domainUnitSelector = new UnitSelector(configuration.getDomainAxisType().getUnitGroup());
-               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
-               domainUnitSelector.addItemListener(new ItemListener() {
-                       @Override
-                       public void itemStateChanged(ItemEvent e) {
-                               if (modifying > 0)
-                                       return;
-                               configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
-                       }
-               });
-               this.add(domainUnitSelector, "width 40lp, gapright para");
-               
-               
-               StyledLabel desc = new StyledLabel("<html><p>The data will be plotted in time order " +
-                               "even if the X axis type is not time.", -2);
-               this.add(desc, "width :0px:, growx, wrap para");
-               
-               
-               
-               //// Y axis selector panel
-               
-               this.add(new JLabel("Y axis types:"));
-               
-               this.add(new JLabel("Flight events:"), "wrap rel");
-               
-               typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
-               JScrollPane scroll = new JScrollPane(typeSelectorPanel);
-               this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
-               
-               
-               //// Flight events
-               eventTableModel = new FlightEventTableModel();
-               JTable table = new JTable(eventTableModel);
-               table.setTableHeader(null);
-               table.setShowVerticalLines(false);
-               table.setRowSelectionAllowed(false);
-               table.setColumnSelectionAllowed(false);
-               
-               TableColumnModel columnModel = table.getColumnModel();
-               TableColumn col0 = columnModel.getColumn(0);
-               int w = table.getRowHeight() + 2;
-               col0.setMinWidth(w);
-               col0.setPreferredWidth(w);
-               col0.setMaxWidth(w);
-               table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
-               this.add(new JScrollPane(table), "height 1px, width 200lp, grow 1, wrap rel");
-               
-               
-               ////  All + None buttons
-               JButton button = new JButton("All");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               for (FlightEvent.Type t: FlightEvent.Type.values())
-                                       configuration.setEvent(t, true);
-                               eventTableModel.fireTableDataChanged();
-                       }
-               });
-               this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
-               
-               button = new JButton("None");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               for (FlightEvent.Type t: FlightEvent.Type.values())
-                                       configuration.setEvent(t, false);
-                               eventTableModel.fireTableDataChanged();
-                       }
-               });
-               this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
-               
-               
-               
-               
-               button = new JButton("New Y axis plot type");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               if (configuration.getTypeCount() >= 15) {
-                                       JOptionPane.showMessageDialog(PlotPanel.this, 
-                                                       "A maximum of 15 plots is allowed.", "Cannot add plot", 
-                                                       JOptionPane.ERROR_MESSAGE);
-                                       return;
-                               }
-
-                               // Select new type smartly
-                               FlightDataBranch.Type type = null;
-                               for (FlightDataBranch.Type t: 
-                                       simulation.getSimulatedData().getBranch(0).getTypes()) {
-                                       
-                                       boolean used = false;
-                                       if (configuration.getDomainAxisType().equals(t)) {
-                                               used = true;
-                                       } else {
-                                               for (int i=0; i < configuration.getTypeCount(); i++) {
-                                                       if (configuration.getType(i).equals(t)) {
-                                                               used = true;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       
-                                       if (!used) {
-                                               type = t;
-                                               break;
-                                       }
-                               }
-                               if (type == null) {
-                                       type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
-                               }
-                               
-                               // Add new type
-                               configuration.addPlotDataType(type);
-                               setToCustom();
-                               updatePlots();
-                       }
-               });
-               this.add(button, "spanx, split");
-               
-               
-               this.add(new JPanel(), "growx");
-               
-               button = new JButton("Plot flight");
-               button.addActionListener(new ActionListener() {
-                       @Override
-                       public void actionPerformed(ActionEvent e) {
-                               defaultConfiguration = configuration.clone();
-                               PlotDialog.showPlot(SwingUtilities.getWindowAncestor(PlotPanel.this), 
-                                               simulation, configuration);
-                       }
-               });
-               this.add(button, "right");
-
-               
-               updatePlots();
-       }
-       
-       
-       private void setToCustom() {
-               modifying++;
-               configuration.setName(CUSTOM);
-               configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
-               modifying--;
-       }
-       
-       
-       private void updatePlots() {
-               domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
-               domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
-               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
-               
-               typeSelectorPanel.removeAll();
-               for (int i=0; i < configuration.getTypeCount(); i++) {
-                       FlightDataBranch.Type type = configuration.getType(i);
-                       Unit unit = configuration.getUnit(i);
-                       int axis = configuration.getAxis(i);
-                       
-                       typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
-               }
-               
-               typeSelectorPanel.repaint();
-               
-               eventTableModel.fireTableDataChanged();
-       }
-       
-       
-       
-       
-       /**
-        * A JPanel which configures a single plot of a PlotConfiguration.
-        */
-       private class PlotTypeSelector extends JPanel {
-               private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
-               
-               private final int index;
-               private JComboBox typeSelector;
-               private UnitSelector unitSelector;
-               private JComboBox axisSelector;
-               
-               
-               public PlotTypeSelector(int index, FlightDataBranch.Type type) {
-                       this (index, type, null, -1);
-               }
-               
-               public PlotTypeSelector(int plotIndex, FlightDataBranch.Type type, Unit unit, int position) {
-                       super(new MigLayout("ins 0"));
-                       
-                       this.index = plotIndex;
-                       
-                       typeSelector = new JComboBox(types);
-                       typeSelector.setSelectedItem(type);
-                       typeSelector.addItemListener(new ItemListener() {
-                               @Override
-                               public void itemStateChanged(ItemEvent e) {
-                                       if (modifying > 0)
-                                               return;
-                                       FlightDataBranch.Type type = (Type) typeSelector.getSelectedItem();
-                                       configuration.setPlotDataType(index, type);
-                                       unitSelector.setUnitGroup(type.getUnitGroup());
-                                       unitSelector.setSelectedUnit(configuration.getUnit(index));
-                                       setToCustom();
-                               }
-                       });
-                       this.add(typeSelector, "gapright para");
-                       
-                       this.add(new JLabel("Unit:"));
-                       unitSelector = new UnitSelector(type.getUnitGroup());
-                       if (unit != null)
-                               unitSelector.setSelectedUnit(unit);
-                       unitSelector.addItemListener(new ItemListener() {
-                               @Override
-                               public void itemStateChanged(ItemEvent e) {
-                                       if (modifying > 0)
-                                               return;
-                                       Unit unit = (Unit) unitSelector.getSelectedUnit();
-                                       configuration.setPlotDataUnit(index, unit);
-                               }
-                       });
-                       this.add(unitSelector, "width 40lp, gapright para");
-                       
-                       this.add(new JLabel("Axis:"));
-                       axisSelector = new JComboBox(POSITIONS);
-                       if (position == LEFT)
-                               axisSelector.setSelectedIndex(1);
-                       else if (position == RIGHT)
-                               axisSelector.setSelectedIndex(2);
-                       else
-                               axisSelector.setSelectedIndex(0);
-                       axisSelector.addItemListener(new ItemListener() {
-                               @Override
-                               public void itemStateChanged(ItemEvent e) {
-                                       if (modifying > 0)
-                                               return;
-                                       int axis = axisSelector.getSelectedIndex() - 1;
-                                       configuration.setPlotDataAxis(index, axis);
-                               }
-                       });
-                       this.add(axisSelector);
-                       
-                       
-                       JButton button = new JButton(Icons.DELETE);
-                       button.setToolTipText("Remove this plot");
-                       button.setBorderPainted(false);
-                       button.addActionListener(new ActionListener() {
-                               @Override
-                               public void actionPerformed(ActionEvent e) {
-                                       configuration.removePlotDataType(index);
-                                       setToCustom();
-                                       updatePlots();
-                               }
-                       });
-                       this.add(button, "gapright 0");
-               }
-       }
-       
-       
-       
-       private class FlightEventTableModel extends AbstractTableModel {
-               private final FlightEvent.Type[] eventTypes;
-               
-               public FlightEventTableModel() {
-                       EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
-                       for (int i=0; i < simulation.getSimulatedData().getBranchCount(); i++) {
-                               for (Pair<Double,FlightEvent> e:
-                                       simulation.getSimulatedData().getBranch(i).getEvents()) {
-                                       set.add(e.getV().getType());
-                               }
-                       }
-                       set.remove(FlightEvent.Type.ALTITUDE);
-                       int count = set.size();
-                       
-                       eventTypes = new FlightEvent.Type[count];
-                       int pos = 0;
-                       for (FlightEvent.Type t: FlightEvent.Type.values()) {
-                               if (set.contains(t)) {
-                                       eventTypes[pos] = t;
-                                       pos++;
-                               }
-                       }
-               }
-               
-               @Override
-               public int getColumnCount() {
-                       return 2;
-               }
-
-               @Override
-               public int getRowCount() {
-                       return eventTypes.length;
-               }
-               
-               @Override
-               public Class<?> getColumnClass(int column) {
-                       switch (column) {
-                       case 0:
-                               return Boolean.class;
-                               
-                       case 1:
-                               return String.class;
-                               
-                       default:
-                               throw new IndexOutOfBoundsException("column="+column);
-                       }
-               }
-
-               @Override
-               public Object getValueAt(int row, int column) {
-                       switch (column) {
-                       case 0:
-                               return new Boolean(configuration.isEventActive(eventTypes[row]));
-                               
-                       case 1:
-                               return eventTypes[row].toString();
-                               
-                       default:
-                               throw new IndexOutOfBoundsException("column="+column);
-                       }
-               }
-               
-               @Override
-               public boolean isCellEditable(int row, int column) {
-                       return column == 0;
-               }
-               
-               @Override
-               public void setValueAt(Object value, int row, int column) {
-                       if (column != 0 || !(value instanceof Boolean)) {
-                               throw new IllegalArgumentException("column="+column+", value="+value);
-                       }
-                       
-                       configuration.setEvent(eventTypes[row], (Boolean)value);
-                       this.fireTableCellUpdated(row, column);
-               }
-       }
-}
diff --git a/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java b/src/net/sf/openrocket/gui/plot/SimulationPlotPanel.java
new file mode 100644 (file)
index 0000000..cac0045
--- /dev/null
@@ -0,0 +1,486 @@
+package net.sf.openrocket.gui.plot;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.document.Simulation;
+import net.sf.openrocket.gui.components.DescriptionArea;
+import net.sf.openrocket.gui.components.UnitSelector;
+import net.sf.openrocket.simulation.FlightDataBranch;
+import net.sf.openrocket.simulation.FlightEvent;
+import net.sf.openrocket.simulation.FlightDataBranch.Type;
+import net.sf.openrocket.unit.Unit;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.Icons;
+import net.sf.openrocket.util.Pair;
+
+public class SimulationPlotPanel extends JPanel {
+       
+       // TODO: LOW: Should these be somewhere else?
+       public static final int AUTO = -1;
+       public static final int LEFT = 0;
+       public static final int RIGHT = 1;
+       
+       public static final String AUTO_NAME = "Auto";
+       public static final String LEFT_NAME = "Left";
+       public static final String RIGHT_NAME = "Right";
+       
+       private static final String CUSTOM = "Custom";
+       
+       /** The "Custom" configuration - not to be used for anything other than the title. */
+       private static final PlotConfiguration CUSTOM_CONFIGURATION;
+       static {
+               CUSTOM_CONFIGURATION = new PlotConfiguration(CUSTOM);
+       }
+       
+       /** The array of presets for the combo box. */
+       private static final PlotConfiguration[] PRESET_ARRAY;
+       static {
+               PRESET_ARRAY = Arrays.copyOf(PlotConfiguration.DEFAULT_CONFIGURATIONS, 
+                               PlotConfiguration.DEFAULT_CONFIGURATIONS.length + 1);
+               PRESET_ARRAY[PRESET_ARRAY.length-1] = CUSTOM_CONFIGURATION;
+       }
+       
+       
+       
+       /** The current default configuration, set each time a plot is made. */
+       private static PlotConfiguration defaultConfiguration =
+               PlotConfiguration.DEFAULT_CONFIGURATIONS[0].resetUnits();
+       
+       
+       private final Simulation simulation;
+       private final FlightDataBranch.Type[] types;
+       private PlotConfiguration configuration;
+       
+
+       private JComboBox configurationSelector;
+       
+       private JComboBox domainTypeSelector;
+       private UnitSelector domainUnitSelector;
+       
+       private JPanel typeSelectorPanel;
+       private FlightEventTableModel eventTableModel;
+       
+       
+       private int modifying = 0;
+       
+
+       public SimulationPlotPanel(final Simulation simulation) {
+               super(new MigLayout("fill"));
+               
+               this.simulation = simulation;
+               if (simulation.getSimulatedData() == null  ||
+                               simulation.getSimulatedData().getBranchCount()==0) {
+                       throw new IllegalArgumentException("Simulation contains no data.");
+               }
+               FlightDataBranch branch = simulation.getSimulatedData().getBranch(0);
+               types = branch.getTypes();
+               
+               // TODO: LOW: Revert to custom if data type is not available.
+               configuration = defaultConfiguration.clone();
+               
+               
+               ////  Configuration selector
+               
+               // Setup the combo box
+               configurationSelector = new JComboBox(PRESET_ARRAY);
+               for (PlotConfiguration config: PRESET_ARRAY) {
+                       if (config.getName().equals(configuration.getName())) {
+                               configurationSelector.setSelectedItem(config);
+                       }
+               }
+               configurationSelector.addItemListener(new ItemListener() {
+                       @Override
+                       public void itemStateChanged(ItemEvent e) {
+                               if (modifying > 0)
+                                       return;
+                               PlotConfiguration conf = (PlotConfiguration)configurationSelector.getSelectedItem();
+                               if (conf == CUSTOM_CONFIGURATION)
+                                       return;
+                               modifying++;
+                               configuration = conf.clone().resetUnits();
+                               updatePlots();
+                               modifying--;
+                       }
+               });
+               this.add(new JLabel("Preset plot configurations: "), "spanx, split");
+               this.add(configurationSelector,"growx, wrap 20lp");
+
+               
+               
+               //// X axis
+               
+               
+               this.add(new JLabel("X axis type:"), "spanx, split");
+               domainTypeSelector = new JComboBox(types);
+               domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
+               domainTypeSelector.addItemListener(new ItemListener() {
+                       @Override
+                       public void itemStateChanged(ItemEvent e) {
+                               if (modifying > 0)
+                                       return;
+                               FlightDataBranch.Type type = (Type) domainTypeSelector.getSelectedItem();
+                               configuration.setDomainAxisType(type);
+                               domainUnitSelector.setUnitGroup(type.getUnitGroup());
+                               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
+                               setToCustom();
+                       }
+               });
+               this.add(domainTypeSelector, "gapright para");
+
+               
+               this.add(new JLabel("Unit:"));
+               domainUnitSelector = new UnitSelector(configuration.getDomainAxisType().getUnitGroup());
+               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
+               domainUnitSelector.addItemListener(new ItemListener() {
+                       @Override
+                       public void itemStateChanged(ItemEvent e) {
+                               if (modifying > 0)
+                                       return;
+                               configuration.setDomainAxisUnit(domainUnitSelector.getSelectedUnit());
+                       }
+               });
+               this.add(domainUnitSelector, "width 40lp, gapright para");
+               
+               
+               DescriptionArea desc = new DescriptionArea("The data will be plotted in time order " +
+                               "even if the X axis type is not time.", 2, -2f);
+               desc.setViewportBorder(BorderFactory.createEmptyBorder());
+               this.add(desc, "width 1px, growx 1, wrap unrel");
+               
+               
+               
+               //// Y axis selector panel
+               
+               this.add(new JLabel("Y axis types:"));
+               
+               this.add(new JLabel("Flight events:"), "wrap rel");
+               
+               typeSelectorPanel = new JPanel(new MigLayout("gapy rel"));
+               JScrollPane scroll = new JScrollPane(typeSelectorPanel);
+               this.add(scroll, "spany 2, height 10px, grow 100, gapright para");
+               
+               
+               //// Flight events
+               eventTableModel = new FlightEventTableModel();
+               JTable table = new JTable(eventTableModel);
+               table.setTableHeader(null);
+               table.setShowVerticalLines(false);
+               table.setRowSelectionAllowed(false);
+               table.setColumnSelectionAllowed(false);
+               
+               TableColumnModel columnModel = table.getColumnModel();
+               TableColumn col0 = columnModel.getColumn(0);
+               int w = table.getRowHeight() + 2;
+               col0.setMinWidth(w);
+               col0.setPreferredWidth(w);
+               col0.setMaxWidth(w);
+               table.addMouseListener(new GUIUtil.BooleanTableClickListener(table));
+               this.add(new JScrollPane(table), "height 10px, width 200lp, grow 1, wrap rel");
+               
+               
+               ////  All + None buttons
+               JButton button = new JButton("All");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               for (FlightEvent.Type t: FlightEvent.Type.values())
+                                       configuration.setEvent(t, true);
+                               eventTableModel.fireTableDataChanged();
+                       }
+               });
+               this.add(button, "split 2, gapleft para, gapright para, growx, sizegroup buttons");
+               
+               button = new JButton("None");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               for (FlightEvent.Type t: FlightEvent.Type.values())
+                                       configuration.setEvent(t, false);
+                               eventTableModel.fireTableDataChanged();
+                       }
+               });
+               this.add(button, "gapleft para, gapright para, growx, sizegroup buttons, wrap para");
+               
+               
+               
+               
+               button = new JButton("New Y axis plot type");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (configuration.getTypeCount() >= 15) {
+                                       JOptionPane.showMessageDialog(SimulationPlotPanel.this, 
+                                                       "A maximum of 15 plots is allowed.", "Cannot add plot", 
+                                                       JOptionPane.ERROR_MESSAGE);
+                                       return;
+                               }
+
+                               // Select new type smartly
+                               FlightDataBranch.Type type = null;
+                               for (FlightDataBranch.Type t: 
+                                       simulation.getSimulatedData().getBranch(0).getTypes()) {
+                                       
+                                       boolean used = false;
+                                       if (configuration.getDomainAxisType().equals(t)) {
+                                               used = true;
+                                       } else {
+                                               for (int i=0; i < configuration.getTypeCount(); i++) {
+                                                       if (configuration.getType(i).equals(t)) {
+                                                               used = true;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       
+                                       if (!used) {
+                                               type = t;
+                                               break;
+                                       }
+                               }
+                               if (type == null) {
+                                       type = simulation.getSimulatedData().getBranch(0).getTypes()[0];
+                               }
+                               
+                               // Add new type
+                               configuration.addPlotDataType(type);
+                               setToCustom();
+                               updatePlots();
+                       }
+               });
+               this.add(button, "spanx, split");
+               
+               
+               this.add(new JPanel(), "growx");
+               
+               button = new JButton("Plot flight");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               defaultConfiguration = configuration.clone();
+                               PlotDialog.showPlot(SwingUtilities.getWindowAncestor(SimulationPlotPanel.this), 
+                                               simulation, configuration);
+                       }
+               });
+               this.add(button, "right");
+
+               
+               updatePlots();
+       }
+       
+       
+       private void setToCustom() {
+               modifying++;
+               configuration.setName(CUSTOM);
+               configurationSelector.setSelectedItem(CUSTOM_CONFIGURATION);
+               modifying--;
+       }
+       
+       
+       private void updatePlots() {
+               domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
+               domainUnitSelector.setUnitGroup(configuration.getDomainAxisType().getUnitGroup());
+               domainUnitSelector.setSelectedUnit(configuration.getDomainAxisUnit());
+               
+               typeSelectorPanel.removeAll();
+               for (int i=0; i < configuration.getTypeCount(); i++) {
+                       FlightDataBranch.Type type = configuration.getType(i);
+                       Unit unit = configuration.getUnit(i);
+                       int axis = configuration.getAxis(i);
+                       
+                       typeSelectorPanel.add(new PlotTypeSelector(i, type, unit, axis), "wrap");
+               }
+               
+               typeSelectorPanel.repaint();
+               
+               eventTableModel.fireTableDataChanged();
+       }
+       
+       
+       
+       
+       /**
+        * A JPanel which configures a single plot of a PlotConfiguration.
+        */
+       private class PlotTypeSelector extends JPanel {
+               private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
+               
+               private final int index;
+               private JComboBox typeSelector;
+               private UnitSelector unitSelector;
+               private JComboBox axisSelector;
+               
+               
+               public PlotTypeSelector(int index, FlightDataBranch.Type type) {
+                       this (index, type, null, -1);
+               }
+               
+               public PlotTypeSelector(int plotIndex, FlightDataBranch.Type type, Unit unit, int position) {
+                       super(new MigLayout("ins 0"));
+                       
+                       this.index = plotIndex;
+                       
+                       typeSelector = new JComboBox(types);
+                       typeSelector.setSelectedItem(type);
+                       typeSelector.addItemListener(new ItemListener() {
+                               @Override
+                               public void itemStateChanged(ItemEvent e) {
+                                       if (modifying > 0)
+                                               return;
+                                       FlightDataBranch.Type type = (Type) typeSelector.getSelectedItem();
+                                       configuration.setPlotDataType(index, type);
+                                       unitSelector.setUnitGroup(type.getUnitGroup());
+                                       unitSelector.setSelectedUnit(configuration.getUnit(index));
+                                       setToCustom();
+                               }
+                       });
+                       this.add(typeSelector, "gapright para");
+                       
+                       this.add(new JLabel("Unit:"));
+                       unitSelector = new UnitSelector(type.getUnitGroup());
+                       if (unit != null)
+                               unitSelector.setSelectedUnit(unit);
+                       unitSelector.addItemListener(new ItemListener() {
+                               @Override
+                               public void itemStateChanged(ItemEvent e) {
+                                       if (modifying > 0)
+                                               return;
+                                       Unit unit = (Unit) unitSelector.getSelectedUnit();
+                                       configuration.setPlotDataUnit(index, unit);
+                               }
+                       });
+                       this.add(unitSelector, "width 40lp, gapright para");
+                       
+                       this.add(new JLabel("Axis:"));
+                       axisSelector = new JComboBox(POSITIONS);
+                       if (position == LEFT)
+                               axisSelector.setSelectedIndex(1);
+                       else if (position == RIGHT)
+                               axisSelector.setSelectedIndex(2);
+                       else
+                               axisSelector.setSelectedIndex(0);
+                       axisSelector.addItemListener(new ItemListener() {
+                               @Override
+                               public void itemStateChanged(ItemEvent e) {
+                                       if (modifying > 0)
+                                               return;
+                                       int axis = axisSelector.getSelectedIndex() - 1;
+                                       configuration.setPlotDataAxis(index, axis);
+                               }
+                       });
+                       this.add(axisSelector);
+                       
+                       
+                       JButton button = new JButton(Icons.DELETE);
+                       button.setToolTipText("Remove this plot");
+                       button.setBorderPainted(false);
+                       button.addActionListener(new ActionListener() {
+                               @Override
+                               public void actionPerformed(ActionEvent e) {
+                                       configuration.removePlotDataType(index);
+                                       setToCustom();
+                                       updatePlots();
+                               }
+                       });
+                       this.add(button, "gapright 0");
+               }
+       }
+       
+       
+       
+       private class FlightEventTableModel extends AbstractTableModel {
+               private final FlightEvent.Type[] eventTypes;
+               
+               public FlightEventTableModel() {
+                       EnumSet<FlightEvent.Type> set = EnumSet.noneOf(FlightEvent.Type.class);
+                       for (int i=0; i < simulation.getSimulatedData().getBranchCount(); i++) {
+                               for (Pair<Double,FlightEvent> e:
+                                       simulation.getSimulatedData().getBranch(i).getEvents()) {
+                                       set.add(e.getV().getType());
+                               }
+                       }
+                       set.remove(FlightEvent.Type.ALTITUDE);
+                       int count = set.size();
+                       
+                       eventTypes = new FlightEvent.Type[count];
+                       int pos = 0;
+                       for (FlightEvent.Type t: FlightEvent.Type.values()) {
+                               if (set.contains(t)) {
+                                       eventTypes[pos] = t;
+                                       pos++;
+                               }
+                       }
+               }
+               
+               @Override
+               public int getColumnCount() {
+                       return 2;
+               }
+
+               @Override
+               public int getRowCount() {
+                       return eventTypes.length;
+               }
+               
+               @Override
+               public Class<?> getColumnClass(int column) {
+                       switch (column) {
+                       case 0:
+                               return Boolean.class;
+                               
+                       case 1:
+                               return String.class;
+                               
+                       default:
+                               throw new IndexOutOfBoundsException("column="+column);
+                       }
+               }
+
+               @Override
+               public Object getValueAt(int row, int column) {
+                       switch (column) {
+                       case 0:
+                               return new Boolean(configuration.isEventActive(eventTypes[row]));
+                               
+                       case 1:
+                               return eventTypes[row].toString();
+                               
+                       default:
+                               throw new IndexOutOfBoundsException("column="+column);
+                       }
+               }
+               
+               @Override
+               public boolean isCellEditable(int row, int column) {
+                       return column == 0;
+               }
+               
+               @Override
+               public void setValueAt(Object value, int row, int column) {
+                       if (column != 0 || !(value instanceof Boolean)) {
+                               throw new IllegalArgumentException("column="+column+", value="+value);
+                       }
+                       
+                       configuration.setEvent(eventTypes[row], (Boolean)value);
+                       this.fireTableCellUpdated(row, column);
+               }
+       }
+}
index e40ba387c85f40d44070070a969883441973e55e..716943c1c22561cf98a8cdc59b361fbe09aa4c13 100644 (file)
@@ -59,6 +59,7 @@ import net.sf.openrocket.simulation.listeners.ApogeeEndListener;
 import net.sf.openrocket.simulation.listeners.InterruptListener;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.ChangeSource;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 import net.sf.openrocket.util.Prefs;
@@ -222,7 +223,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
                
                // Add rotation slider
                // Minimum size to fit "360deg"
-               JLabel l = new JLabel("360\u00b0");
+               JLabel l = new JLabel("360" + Chars.DEGREE);
                Dimension d = l.getPreferredSize();
                
                add(new BasicSlider(theta.getSliderModel(0,2*Math.PI),JSlider.VERTICAL,true),
index 3bd4dcdabd33ed5c246d36f08ee819f67363b2ec..145e4cf0456d947ac6471031bc245b91d25b7b52 100644 (file)
@@ -13,6 +13,7 @@ import javax.swing.event.ChangeListener;
 import javax.swing.event.EventListenerList;
 
 import net.sf.openrocket.motor.Motor;
+import net.sf.openrocket.util.Chars;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 
@@ -694,7 +695,7 @@ public class Rocket extends RocketComponent {
                                        if (previous != null) {
                                                String s = "";
                                                if (count > 1) {
-                                                       s = "" + count + "\u00d7" + previous;
+                                                       s = "" + count + Chars.TIMES + previous;
                                                } else {
                                                        s = previous;
                                                }
@@ -713,7 +714,7 @@ public class Rocket extends RocketComponent {
                        if (previous != null) {
                                String s = "";
                                if (count > 1) {
-                                       s = "" + count + "\u00d7" + previous;
+                                       s = "" + count + Chars.TIMES + previous;
                                } else {
                                        s = previous;
                                }
index 7155a7988311fc941186aa8dee88718d4e3b889c..5478545fe00f0cc74168b2a4aa1ba980c0b3fa28 100644 (file)
@@ -1,9 +1,8 @@
 package net.sf.openrocket.rocketcomponent;
 
-import static java.lang.Math.sin;
-import static java.lang.Math.sqrt;
-import static net.sf.openrocket.util.MathUtil.pow2;
-import static net.sf.openrocket.util.MathUtil.pow3;
+import static java.lang.Math.*;
+import static net.sf.openrocket.util.Chars.*;
+import static net.sf.openrocket.util.MathUtil.*;
 
 import java.util.Collection;
 
@@ -594,14 +593,14 @@ public class Transition extends SymmetricComponent {
                                "<i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)" +
                                "<sup><i>k</i></sup> "+
                                "where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 this is a "+
-                               "<b>½-power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a "+
-                               "<b>¾-power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.",
+                               "<b>" + FRAC12 +"-power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a "+
+                               "<b>" + FRAC34 +"-power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.",
                                "A power series transition has a profile of "+
                                "<i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)" +
                                "<sup><i>k</i></sup> "+
                                "where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 the transition is "+
-                               "<b>½-power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a <b>¾-power</b>, and for " +
-                               "<i>k</i>=1 <b>conical</b>.",true) {
+                               "<b>" + FRAC12 + "-power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a " +
+                               "<b>" + FRAC34 + "-power</b>, and for <i>k</i>=1 <b>conical</b>.",true) {
                        @Override
                        public boolean usesParameter() {  // Range 0...1
                                return true;
index e694aa69e95b0507a3cdff275685846aceef7f0c..240e0b8496ac807e148778331ce30262ca2e7ce7 100644 (file)
@@ -2,10 +2,12 @@ package net.sf.openrocket.unit;
 
 import java.text.DecimalFormat;
 
+import net.sf.openrocket.util.Chars;
+
 public class DegreeUnit extends GeneralUnit {
 
        public DegreeUnit() {
-               super(Math.PI/180.0,"\u00b0");
+               super(Math.PI/180.0, ""+Chars.DEGREE);
        }
 
        @Override
index 55b4f160d786c5e88ab43d13b049e7a0e8572d2f..53e19c8038df14310a7aa42267473ca9f72a86dd 100644 (file)
@@ -2,10 +2,12 @@ package net.sf.openrocket.unit;
 
 import java.text.DecimalFormat;
 
+import net.sf.openrocket.util.Chars;
+
 public abstract class Unit {
        
        /** No unit with 2 digit precision */
-       public static final Unit NOUNIT2 = new GeneralUnit(1,"\u200b", 2);  // zero-width space
+       public static final Unit NOUNIT2 = new GeneralUnit(1,""+Chars.ZWSP, 2);
 
        protected final double multiplier;   // meters = units * multiplier
        protected final String unit;
index 6b6d4997675052a5ad1ad33bf110f5ce17d6a799..ffe80f226547b3a0ee57078be52b5fa0251a5523 100644 (file)
@@ -1,5 +1,6 @@
 package net.sf.openrocket.unit;
 
+import static net.sf.openrocket.util.Chars.*;
 import static net.sf.openrocket.util.MathUtil.pow2;
 
 import java.util.ArrayList;
@@ -89,11 +90,11 @@ public class UnitGroup {
                UNITS_DISTANCE.addUnit(new GeneralUnit(1609.344,"mi"));
                
                UNITS_AREA = new UnitGroup();
-               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001),"mm\u00b2"));
-               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01),"cm\u00b2"));
-               UNITS_AREA.addUnit(new GeneralUnit(1,"m\u00b2"));
-               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254),"in\u00b2"));
-               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048),"ft\u00b2"));
+               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.001),"mm" + SQUARED));
+               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.01),"cm" + SQUARED));
+               UNITS_AREA.addUnit(new GeneralUnit(1,"m" + SQUARED));
+               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.0254),"in" + SQUARED));
+               UNITS_AREA.addUnit(new GeneralUnit(pow2(0.3048),"ft" + SQUARED));
                UNITS_AREA.setDefaultUnit(1);
                
                
@@ -111,8 +112,8 @@ public class UnitGroup {
                UNITS_VELOCITY.addUnit(new GeneralUnit(0.44704, "mph"));
                
                UNITS_ACCELERATION = new UnitGroup();
-               UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s\u00b2"));
-               UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s\u00b2"));
+               UNITS_ACCELERATION.addUnit(new GeneralUnit(1, "m/s" + SQUARED));
+               UNITS_ACCELERATION.addUnit(new GeneralUnit(0.3048, "ft/s" + SQUARED));
                
 
                UNITS_MASS = new UnitGroup();
@@ -126,18 +127,18 @@ public class UnitGroup {
                UNITS_ANGLE.addUnit(new FixedPrecisionUnit("rad",0.01));
                
                UNITS_DENSITY_BULK = new UnitGroup();
-               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000,"g/cm\u00b3"));
-               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1,"kg/m\u00b3"));
-               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404,"oz/in\u00b3"));
-               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634,"lb/ft\u00b3"));
+               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1000,"g/cm" + CUBED));
+               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1,"kg/m" + CUBED));
+               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(1729.99404,"oz/in" + CUBED));
+               UNITS_DENSITY_BULK.addUnit(new GeneralUnit(16.0184634,"lb/ft" + CUBED));
 
                UNITS_DENSITY_SURFACE = new UnitGroup();
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10,"g/cm\u00b2"));
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001,"g/m\u00b2"));
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1,"kg/m\u00b2"));
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487,"oz/in\u00b2"));
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727,"oz/ft\u00b2"));
-               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764,"lb/ft\u00b2"));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(10,"g/cm" + SQUARED));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.001,"g/m" + SQUARED));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(1,"kg/m" + SQUARED));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(43.9418487,"oz/in" + SQUARED));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(0.305151727,"oz/ft" + SQUARED));
+               UNITS_DENSITY_SURFACE.addUnit(new GeneralUnit(4.88242764,"lb/ft" + SQUARED));
                UNITS_DENSITY_SURFACE.setDefaultUnit(1);
 
                UNITS_DENSITY_LINE = new UnitGroup();
@@ -152,7 +153,7 @@ public class UnitGroup {
 
                UNITS_IMPULSE = new UnitGroup();
                UNITS_IMPULSE.addUnit(new GeneralUnit(1,"Ns"));
-               UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf\u00b7s"));
+               UNITS_IMPULSE.addUnit(new GeneralUnit(4.44822162, "lbf"+DOT+"s"));
 
                UNITS_TIME_STEP = new UnitGroup();
                UNITS_TIME_STEP.addUnit(new FixedPrecisionUnit("ms", 1, 0.001));
@@ -174,8 +175,8 @@ public class UnitGroup {
 
                UNITS_TEMPERATURE = new UnitGroup();
                UNITS_TEMPERATURE.addUnit(new FixedPrecisionUnit("K", 1));
-               UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, "\u00b0C"));
-               UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0/9.0, 459.67, "\u00b0F"));
+               UNITS_TEMPERATURE.addUnit(new TemperatureUnit(1, 273.15, DEGREE+"C"));
+               UNITS_TEMPERATURE.addUnit(new TemperatureUnit(5.0/9.0, 459.67, DEGREE+"F"));
                UNITS_TEMPERATURE.setDefaultUnit(1);
                
                UNITS_PRESSURE = new UnitGroup();
@@ -188,18 +189,19 @@ public class UnitGroup {
                UNITS_PRESSURE.addUnit(new GeneralUnit(1, "Pa"));
 
                UNITS_RELATIVE = new UnitGroup();
-               UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("\u200b", 0.01));
+               UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01, 1.0));
                UNITS_RELATIVE.addUnit(new FixedPrecisionUnit("%", 1, 0.01));
+               UNITS_RELATIVE.addUnit(new FixedPrecisionUnit(""+PERMILLE, 1, 0.001));
                UNITS_RELATIVE.setDefaultUnit(1);
 
                
                UNITS_ROUGHNESS = new UnitGroup();
-               UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, "\u03bcm"));
+               UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.000001, MICRO+"m"));
                UNITS_ROUGHNESS.addUnit(new GeneralUnit(0.0000254, "mil"));
                
                
                UNITS_COEFFICIENT = new UnitGroup();
-               UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit("\u200b", 0.01));  // zero-width space
+               UNITS_COEFFICIENT.addUnit(new FixedPrecisionUnit(""+ZWSP, 0.01));  // zero-width space
                
 
                HashMap<String,UnitGroup> map = new HashMap<String,UnitGroup>();
@@ -235,45 +237,45 @@ public class UnitGroup {
                UNITS_LENGTH.setDefaultUnit("cm");
                UNITS_MOTOR_DIMENSIONS.setDefaultUnit("mm");
                UNITS_DISTANCE.setDefaultUnit("m");
-               UNITS_AREA.setDefaultUnit("cm\u00b2");
+               UNITS_AREA.setDefaultUnit("cm"+SQUARED);
                UNITS_STABILITY.setDefaultUnit("cal");
                UNITS_VELOCITY.setDefaultUnit("m/s");
-               UNITS_ACCELERATION.setDefaultUnit("m/s\u00b2");
+               UNITS_ACCELERATION.setDefaultUnit("m/s"+SQUARED);
                UNITS_MASS.setDefaultUnit("g");
-               UNITS_ANGLE.setDefaultUnit(0);
-               UNITS_DENSITY_BULK.setDefaultUnit("g/cm\u00b3");
-               UNITS_DENSITY_SURFACE.setDefaultUnit("g/m\u00b2");
+               UNITS_ANGLE.setDefaultUnit(""+DEGREE);
+               UNITS_DENSITY_BULK.setDefaultUnit("g/cm"+CUBED);
+               UNITS_DENSITY_SURFACE.setDefaultUnit("g/m"+SQUARED);
                UNITS_DENSITY_LINE.setDefaultUnit("g/m");
                UNITS_FORCE.setDefaultUnit("N");
                UNITS_IMPULSE.setDefaultUnit("Ns");
                UNITS_TIME_STEP.setDefaultUnit("s");
                UNITS_FLIGHT_TIME.setDefaultUnit("s");
                UNITS_ROLL.setDefaultUnit("r/s");
-               UNITS_TEMPERATURE.setDefaultUnit(1);
+               UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"C");
                UNITS_PRESSURE.setDefaultUnit("mbar");
                UNITS_RELATIVE.setDefaultUnit("%");
-               UNITS_ROUGHNESS.setDefaultUnit("\u03bcm");
+               UNITS_ROUGHNESS.setDefaultUnit(MICRO+"m");
        }
        
        public static void setDefaultImperialUnits() {
                UNITS_LENGTH.setDefaultUnit("in");
                UNITS_MOTOR_DIMENSIONS.setDefaultUnit("in");
                UNITS_DISTANCE.setDefaultUnit("ft");
-               UNITS_AREA.setDefaultUnit("in\u00b2");
+               UNITS_AREA.setDefaultUnit("in"+SQUARED);
                UNITS_STABILITY.setDefaultUnit("cal");
                UNITS_VELOCITY.setDefaultUnit("ft/s");
-               UNITS_ACCELERATION.setDefaultUnit("ft/s\u00b2");
+               UNITS_ACCELERATION.setDefaultUnit("ft/s"+SQUARED);
                UNITS_MASS.setDefaultUnit("oz");
-               UNITS_ANGLE.setDefaultUnit(0);
-               UNITS_DENSITY_BULK.setDefaultUnit("oz/in\u00b3");
-               UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft\u00b2");
+               UNITS_ANGLE.setDefaultUnit(""+DEGREE);
+               UNITS_DENSITY_BULK.setDefaultUnit("oz/in"+CUBED);
+               UNITS_DENSITY_SURFACE.setDefaultUnit("oz/ft"+SQUARED);
                UNITS_DENSITY_LINE.setDefaultUnit("oz/ft");
                UNITS_FORCE.setDefaultUnit("N");
                UNITS_IMPULSE.setDefaultUnit("Ns");
                UNITS_TIME_STEP.setDefaultUnit("s");
                UNITS_FLIGHT_TIME.setDefaultUnit("s");
                UNITS_ROLL.setDefaultUnit("r/s");
-               UNITS_TEMPERATURE.setDefaultUnit(2);
+               UNITS_TEMPERATURE.setDefaultUnit(DEGREE+"F");
                UNITS_PRESSURE.setDefaultUnit("mbar");
                UNITS_RELATIVE.setDefaultUnit("%");
                UNITS_ROUGHNESS.setDefaultUnit("mil");
@@ -337,24 +339,20 @@ public class UnitGroup {
        }
        
        /**
-        * Set the default unit based on the unit name.  Does nothing if the name
-        * does not match any of the units.
+        * Set the default unit based on the unit name.  Throws an exception if a
+        * unit with the provided name is not available.
         * 
-        * @param name  the unit name (<code>null</code> ok).
-        * @return              <code>true</code> if the the default was set, 
-        *                              <code>false</code> if a matching unit was not found.  
+        * @param   name        the unit name.
+        * @throws  IllegalArgumentException    if the corresponding unit is not found in the group.
         */
-       public boolean setDefaultUnit(String name) {
-               if (name == null)
-                       return false;
-               
+       public void setDefaultUnit(String name) throws IllegalArgumentException {
                for (int i=0; i < units.size(); i++) {
-                       if (name.equals(units.get(i).getUnit())) {
+                       if (units.get(i).getUnit().equals(name)) {
                                setDefaultUnit(i);
-                               return true;
+                               return;
                        }
                }
-               return false;
+               throw new IllegalArgumentException("name="+name);
        }
 
        
diff --git a/src/net/sf/openrocket/util/Chars.java b/src/net/sf/openrocket/util/Chars.java
new file mode 100644 (file)
index 0000000..08d2f82
--- /dev/null
@@ -0,0 +1,50 @@
+package net.sf.openrocket.util;
+
+/**
+ * A class defining various non-ASCII characters for easier use. 
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class Chars {
+
+       /** The fraction 1/2 */
+       public static final char FRAC12 = '\u00BD';
+       /** The fraction 1/4 */
+       public static final char FRAC14 = '\u00BC';
+       /** The fraction 3/4 */
+       public static final char FRAC34 = '\u00BE';
+       
+       /** Degree sign */
+       public static final char DEGREE = '\u00B0';
+       
+       /** Squared, superscript 2 */
+       public static final char SQUARED = '\u00B2';
+       /** Cubed, superscript 3 */
+       public static final char CUBED = '\u00B3';
+       
+       /** Per mille sign */
+       public static final char PERMILLE = '\u2030';
+       
+       /** Middle dot, multiplication */
+       public static final char DOT = '\u00B7';
+       /** Multiplication sign, cross */
+       public static final char TIMES = '\u00D7';
+       
+       /** No-break space */
+       public static final char NBSP = '\u00A0';
+       /** Zero-width space */
+       public static final char ZWSP = '\u200B';
+       
+       /** Micro sign (Greek letter mu) */
+       public static final char MICRO = '\u00B5';
+
+       /** Alpha */
+       public static final char ALPHA = '\u03b1';
+       /** Theta */
+       public static final char THETA = '\u0398';
+       
+       /** Copyright symbol */
+       public static final char COPY = '\u00A9';
+       /** A centered bullet */
+       public static final char BULLET = '\u2022';
+}
index 21017940176bdb680af1d27111c02b84b9466643..7253c890431bcc11407ffafa2897dcba210c0d45 100644 (file)
@@ -10,7 +10,7 @@ import javax.swing.SwingUtilities;
  * A thread-safe <code>ProgressMonitor</code>.  This class may be instantiated
  * and the method {@link #setProgress(int)} called safely from any thread.
  * <p>
- * Why the FSCK&!¤#&%¤ isn't the default API version thread-safe?!?!
+ * Why the FSCK&!#&% isn't the default API version thread-safe?!?!
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
index e13b9b94660f854f095c54a37e82fe072f253093..e95587b5c90906e3f78e5f199b92c3333d9eb7ca 100644 (file)
@@ -26,4 +26,40 @@ public class Pair<U,V> {
                return v;
        }
        
+       
+       /**
+        * Compare both components of the Pair to another object.
+        * The pair is equal iff both items are equal (or null).
+        */
+       @SuppressWarnings("unchecked")
+       @Override
+       public boolean equals(Object other) {
+               if (!(other instanceof Pair))
+                       return false;
+               Object otherU = ((Pair)other).getU();
+               Object otherV = ((Pair)other).getV();
+               
+               if (otherU == null) {
+                       if (this.u != null)
+                               return false;
+               } else {
+                       if (!otherU.equals(this.u))
+                               return false;
+               }
+
+               if (otherV == null) {
+                       if (this.v != null)
+                               return false;
+               } else {
+                       if (!otherV.equals(this.v))
+                               return false;
+               }
+               return true;
+       }
+       
+       @Override
+       public int hashCode() {
+               return ((u != null) ? u.hashCode() : 0) + ((v != null) ? v.hashCode() : 0);
+       }
+       
 }
index f4f26133d81beb2d3fe35b4e291ae04a6746673b..dc3e0db405bdc31a07ba6d006010e493e00d9cc1 100644 (file)
@@ -52,50 +52,62 @@ public class Prefs {
         */
        public static final String NODENAME = (DEBUG?"OpenRocket-debug":"OpenRocket");
        
-       
-       
-       private static final String BUILD_VERSION;
-       private static final String BUILD_SOURCE;
+
        public static final String DEFAULT_BUILD_SOURCE = "default";
-       private static final boolean DEFAULT_CHECK_UPDATES;
+
        
-       static {
-               try {
-                       InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
-                       if (is == null) {
-                               throw new MissingResourceException(
-                                               "build.properties not found, distribution built wrong" + 
-                                               "   path:"+System.getProperty("java.class.path"),
-                                               "build.properties", "build.version");
-                       }
-                       
-                       Properties props = new Properties();
-                       props.load(is);
-                       is.close();
-                       
-                       BUILD_VERSION = props.getProperty("build.version");
-                       if (BUILD_VERSION == null) {
+       /*
+        * Load property file only when necessary.
+        */
+       private static class BuildPropertyHolder {
+
+               public static final String BUILD_VERSION;
+               public static final String BUILD_SOURCE;
+               public static final boolean DEFAULT_CHECK_UPDATES;
+
+               static {
+                       try {
+                               InputStream is = ClassLoader.getSystemResourceAsStream("build.properties");
+                               if (is == null) {
+                                       throw new MissingResourceException(
+                                                       "build.properties not found, distribution built wrong" + 
+                                                       "   classpath:"+System.getProperty("java.class.path"),
+                                                       "build.properties", "build.version");
+                               }
+
+                               Properties props = new Properties();
+                               props.load(is);
+                               is.close();
+
+                               String version = props.getProperty("build.version");
+                               if (version == null) {
+                                       throw new MissingResourceException(
+                                                       "build.version not found in property file",
+                                                       "build.properties", "build.version");
+                               }
+                               BUILD_VERSION = version.trim();
+
+                               BUILD_SOURCE = props.getProperty("build.source");
+                               if (BUILD_SOURCE == null) {
+                                       throw new MissingResourceException(
+                                                       "build.source not found in property file",
+                                                       "build.properties", "build.source");
+                               }
+
+                               String value = props.getProperty("build.checkupdates");
+                               if (value != null)
+                                       DEFAULT_CHECK_UPDATES = Boolean.parseBoolean(value);
+                               else
+                                       DEFAULT_CHECK_UPDATES = true;
+
+                       } catch (IOException e) {
                                throw new MissingResourceException(
-                                               "build.version not found in property file",
+                                               "Error reading build.properties",
                                                "build.properties", "build.version");
                        }
-                       
-                       BUILD_SOURCE = props.getProperty("build.source");
-                       
-                       String value = props.getProperty("build.checkupdates");
-                       if (value != null)
-                               DEFAULT_CHECK_UPDATES = Boolean.parseBoolean(value);
-                       else
-                               DEFAULT_CHECK_UPDATES = true;
-                       
-               } catch (IOException e) {
-                       throw new MissingResourceException(
-                                       "Error reading build.properties",
-                                       "build.properties", "build.version");
                }
        }
        
-       
        public static final String BODY_COMPONENT_INSERT_POSITION_KEY = "BodyComponentInsertPosition";
        
        
@@ -111,6 +123,7 @@ public class Prefs {
        public static final String PLOT_SHOW_POINTS = "ShowPlotPoints";
        
        private static final String CHECK_UPDATES = "CheckUpdates";
+       public static final String LAST_UPDATE = "LastUpdateVersion";
        
        /**
         * Node to this application's preferences.
@@ -178,12 +191,12 @@ public class Prefs {
        
        
        public static String getVersion() {
-               return BUILD_VERSION;
+               return BuildPropertyHolder.BUILD_VERSION;
        }
        
        
        public static String getBuildSource() {
-               return BUILD_SOURCE;
+               return BuildPropertyHolder.BUILD_SOURCE;
        }
        
        
@@ -256,7 +269,7 @@ public class Prefs {
        
        
        public static boolean getCheckUpdates() {
-               return PREFNODE.getBoolean(CHECK_UPDATES, DEFAULT_CHECK_UPDATES);
+               return PREFNODE.getBoolean(CHECK_UPDATES, BuildPropertyHolder.DEFAULT_CHECK_UPDATES);
        }
        
        public static void setCheckUpdates(boolean check) {
@@ -502,7 +515,9 @@ public class Prefs {
                                if (group == null)
                                        continue;
                                
-                               group.setDefaultUnit(prefs.get(key, null));
+                               try {
+                                       group.setDefaultUnit(prefs.get(key, null));
+                               } catch (IllegalArgumentException ignore) { }
                        }
                        
                } catch (BackingStoreException e) {
index f9b676f0be873aefb6c07eea5ff01f8f8a1a28e3..6870c5f2c2429adcc6ef03b3a221ced57b57975c 100644 (file)
@@ -73,7 +73,7 @@ public class ManufacturerTest {
                assertTrue(m1.matches("a/"));
                assertTrue(m1.matches("a/rcs"));
                assertTrue(m1.matches("a/rms"));
-               assertTrue(m1.matches("aerotech  ...-/%¤#_!"));
+               assertTrue(m1.matches("aerotech  ...-/%#_!"));
                assertTrue(m1.matches(" .isp/"));
                
                assertFalse(m1.matches("aero/tech"));
index 97c78a7a59dddea996c21ab468222d7ff9ad81ef..0c74a3a1e24eaa9016818227dc03ad906fc3bdd3 100644 (file)
@@ -38,11 +38,11 @@ public class CoordinateTest {
 
                assertCoordinateEquals(new Coordinate(2,4,6,8), y.multiply(2));
                
-               assertEquals(1+2+3, y.dot(x));
-               assertEquals(1+2+3, x.dot(y));
-               assertEquals(1+2+3, Coordinate.dot(x,y));
-               assertEquals(x.dot(x), x.length2());
-               assertEquals(y.dot(y), y.length2());
+               assertEquals(1+2+3, y.dot(x), EPS);
+               assertEquals(1+2+3, x.dot(y), EPS);
+               assertEquals(1+2+3, Coordinate.dot(x,y), EPS);
+               assertEquals(x.dot(x), x.length2(), EPS);
+               assertEquals(y.dot(y), y.length2(), EPS);
                assertEquals(Math.sqrt(1+4+9), y.length(), EPS);
                assertEquals(1, y.normalize().length(), EPS);
                
index 8c135c8abdf50cb06747d1704296c79459e17798..3f410acb5eee554663d3fc38ebae6ef42ce18dd0 100644 (file)
@@ -17,24 +17,24 @@ public class MathUtilTest {
                assertEquals(PI*PI*PI, MathUtil.pow3(PI), EPS);
                assertEquals(PI*PI*PI*PI, MathUtil.pow4(PI), EPS);
                
-               assertEquals(1.0, MathUtil.clamp(0.9999, 1.0, 2.0));
-               assertEquals(1.23, MathUtil.clamp(1.23, 1.0, 2.0));
-               assertEquals(2.0, MathUtil.clamp(2 + EPS/100, 1.0, 2.0));
+               assertEquals(1.0, MathUtil.clamp(0.9999, 1.0, 2.0), 0);
+               assertEquals(1.23, MathUtil.clamp(1.23, 1.0, 2.0), 0);
+               assertEquals(2.0, MathUtil.clamp(2 + EPS/100, 1.0, 2.0), 0);
                
-               assertEquals(1.0f, MathUtil.clamp(0.9999f, 1.0f, 2.0f));
-               assertEquals(1.23f, MathUtil.clamp(1.23f, 1.0f, 2.0f));
-               assertEquals(2.0f, MathUtil.clamp(2.0001f, 1.0f, 2.0f));
+               assertEquals(1.0f, MathUtil.clamp(0.9999f, 1.0f, 2.0f), 0);
+               assertEquals(1.23f, MathUtil.clamp(1.23f, 1.0f, 2.0f), 0);
+               assertEquals(2.0f, MathUtil.clamp(2.0001f, 1.0f, 2.0f), 0);
                
                assertEquals(1, MathUtil.clamp(-3, 1, 5));
                assertEquals(3, MathUtil.clamp(3, 1, 5));
                assertEquals(5, MathUtil.clamp(6, 1, 5));
                
-               assertEquals(-1.0, MathUtil.sign(Double.NEGATIVE_INFINITY));
-               assertEquals(-1.0, MathUtil.sign(-100));
-               assertEquals(-1.0, MathUtil.sign(Math.nextAfter(0.0, -1.0)));
-               assertEquals( 1.0, MathUtil.sign(Math.nextUp(0.0)));
-               assertEquals( 1.0, MathUtil.sign(100));
-               assertEquals( 1.0, MathUtil.sign(Double.POSITIVE_INFINITY));
+               assertEquals(-1.0, MathUtil.sign(Double.NEGATIVE_INFINITY), EPS);
+               assertEquals(-1.0, MathUtil.sign(-100), EPS);
+               assertEquals(-1.0, MathUtil.sign(Math.nextAfter(0.0, -1.0)), EPS);
+               assertEquals( 1.0, MathUtil.sign(Math.nextUp(0.0)), EPS);
+               assertEquals( 1.0, MathUtil.sign(100), EPS);
+               assertEquals( 1.0, MathUtil.sign(Double.POSITIVE_INFINITY), EPS);
        }
        
        @Test
@@ -68,33 +68,33 @@ public class MathUtilTest {
        
        @Test
        public void minmaxTest() {
-               assertEquals(1.0, MathUtil.min(1.0, Math.nextUp(1.0)));
-               assertEquals(1.0, MathUtil.min(1.0, Double.POSITIVE_INFINITY));
-               assertEquals(1.0, MathUtil.min(NaN, 1.0));
-               assertEquals(1.0, MathUtil.min(1.0, NaN));
-               assertEquals(NaN, MathUtil.min(NaN, NaN));
+               assertEquals(1.0, MathUtil.min(1.0, Math.nextUp(1.0)), 0);
+               assertEquals(1.0, MathUtil.min(1.0, Double.POSITIVE_INFINITY), 0);
+               assertEquals(1.0, MathUtil.min(NaN, 1.0), 0);
+               assertEquals(1.0, MathUtil.min(1.0, NaN), 0);
+               assertEquals(NaN, MathUtil.min(NaN, NaN), 0);
 
-               assertEquals(Math.nextUp(1.0), MathUtil.max(1.0, Math.nextUp(1.0)));
-               assertEquals(Double.POSITIVE_INFINITY, MathUtil.max(1.0, Double.POSITIVE_INFINITY));
-               assertEquals(1.0, MathUtil.max(NaN, 1.0));
-               assertEquals(1.0, MathUtil.max(1.0, NaN));
-               assertEquals(NaN, MathUtil.max(NaN, NaN));
+               assertEquals(Math.nextUp(1.0), MathUtil.max(1.0, Math.nextUp(1.0)), 0);
+               assertEquals(Double.POSITIVE_INFINITY, MathUtil.max(1.0, Double.POSITIVE_INFINITY), 0);
+               assertEquals(1.0, MathUtil.max(NaN, 1.0), 0);
+               assertEquals(1.0, MathUtil.max(1.0, NaN), 0);
+               assertEquals(NaN, MathUtil.max(NaN, NaN), 0);
                
-               assertEquals(1.0, MathUtil.min(1.0, 2.0, 3.0));
-               assertEquals(1.0, MathUtil.min(1.0, NaN, NaN));
-               assertEquals(1.0, MathUtil.min(NaN, 1.0, NaN));
-               assertEquals(1.0, MathUtil.min(NaN, NaN, 1.0));
-               assertEquals(1.0, MathUtil.min(2.0, NaN, 1.0));
-               assertEquals(1.0, MathUtil.min(1.0, 2.0, NaN));
-               assertEquals(1.0, MathUtil.min(NaN, 2.0, 1.0));
+               assertEquals(1.0, MathUtil.min(1.0, 2.0, 3.0), 0);
+               assertEquals(1.0, MathUtil.min(1.0, NaN, NaN), 0);
+               assertEquals(1.0, MathUtil.min(NaN, 1.0, NaN), 0);
+               assertEquals(1.0, MathUtil.min(NaN, NaN, 1.0), 0);
+               assertEquals(1.0, MathUtil.min(2.0, NaN, 1.0), 0);
+               assertEquals(1.0, MathUtil.min(1.0, 2.0, NaN), 0);
+               assertEquals(1.0, MathUtil.min(NaN, 2.0, 1.0), 0);
                
-               assertEquals(3.0, MathUtil.max(1.0, 3.0, 2.0));
-               assertEquals(1.0, MathUtil.max(1.0, NaN, NaN));
-               assertEquals(1.0, MathUtil.max(NaN, 1.0, NaN));
-               assertEquals(1.0, MathUtil.max(NaN, NaN, 1.0));
-               assertEquals(2.0, MathUtil.max(2.0, NaN, 1.0));
-               assertEquals(2.0, MathUtil.max(1.0, 2.0, NaN));
-               assertEquals(2.0, MathUtil.max(NaN, 2.0, 1.0));
+               assertEquals(3.0, MathUtil.max(1.0, 3.0, 2.0), 0);
+               assertEquals(1.0, MathUtil.max(1.0, NaN, NaN), 0);
+               assertEquals(1.0, MathUtil.max(NaN, 1.0, NaN), 0);
+               assertEquals(1.0, MathUtil.max(NaN, NaN, 1.0), 0);
+               assertEquals(2.0, MathUtil.max(2.0, NaN, 1.0), 0);
+               assertEquals(2.0, MathUtil.max(1.0, 2.0, NaN), 0);
+               assertEquals(2.0, MathUtil.max(NaN, 2.0, 1.0), 0);
        }
        
        @Test