From dfc10c016c5f1bb4b7714dff414562f2f32e2866 Mon Sep 17 00:00:00 2001 From: plaa Date: Sun, 30 Aug 2009 15:46:18 +0000 Subject: [PATCH] updates for 0.9.3 git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@20 180e2498-e6e9-4542-8430-84ac67f01cd8 --- .classpath | 2 + ChangeLog | 10 + TODO | 7 +- build.properties | 4 + .../eventicons/event-ejection-charge.xcf.gz | Bin 0 -> 4766 bytes pix-src/eventicons/event-ground-hit.xcf.gz | Bin 0 -> 735 bytes pix-src/eventicons/event-launch.xcf.gz | Bin 0 -> 2141 bytes .../event-recovery-device-deployment.xcf.gz | Bin 0 -> 4702 bytes pix/eventicons/copyright.txt | 5 +- pix/eventicons/event-ejection-charge.png | Bin 0 -> 466 bytes pix/eventicons/event-ground-hit.png | Bin 0 -> 305 bytes pix/eventicons/event-launch.png | Bin 0 -> 623 bytes .../event-recovery-device-deployment.png | Bin 0 -> 636 bytes pix/icon/icon-about.png | Bin 0 -> 40086 bytes .../aerodynamics/AerodynamicCalculator.java | 2 +- src/net/sf/openrocket/database/Databases.java | 4 +- .../openrocket/file/GeneralMotorLoader.java | 2 +- src/net/sf/openrocket/file/MotorLoader.java | 184 +------------- .../sf/openrocket/file/OpenRocketLoader.java | 4 +- .../sf/openrocket/file/RASPMotorLoader.java | 10 +- .../openrocket/file/RockSimMotorLoader.java | 10 +- .../file/openrocket/RocketComponentSaver.java | 4 +- .../gui/configdialog/MotorConfig.java | 4 +- .../openrocket/gui/dialogs/AboutDialog.java | 24 +- .../gui/dialogs/BugReportDialog.java | 2 +- .../dialogs/EditMotorConfigurationDialog.java | 2 +- .../gui/dialogs/MotorChooserDialog.java | 4 +- .../sf/openrocket/gui/plot/PlotDialog.java | 12 +- .../gui/scalefigure/RocketFigure.java | 2 +- src/net/sf/openrocket/motor/Manufacturer.java | 226 ++++++++++++++++++ .../{rocketcomponent => motor}/Motor.java | 11 +- .../ThrustCurveMotor.java | 4 +- .../openrocket/rocketcomponent/BodyTube.java | 1 + .../openrocket/rocketcomponent/InnerTube.java | 1 + .../rocketcomponent/MotorMount.java | 1 + .../sf/openrocket/rocketcomponent/Rocket.java | 1 + .../simulation/FlightSimulator.java | 2 +- src/net/sf/openrocket/util/Coordinate.java | 80 ++++--- src/net/sf/openrocket/util/Icons.java | 4 +- src/net/sf/openrocket/util/MathUtil.java | 46 +--- src/net/sf/openrocket/util/Prefs.java | 1 + src/net/sf/openrocket/util/Rotation2D.java | 14 -- src/net/sf/openrocket/util/Test.java | 2 +- src/net/sf/openrocket/util/TextUtil.java | 133 ++++++++--- .../openrocket/{util => utils}/Analysis.java | 3 +- src/net/sf/openrocket/utils/MotorCheck.java | 9 +- src/net/sf/openrocket/utils/MotorCompare.java | 9 +- src/net/sf/openrocket/utils/MotorPrinter.java | 4 +- test/Test.java | 32 +++ .../sf/openrocket/motor/ManufacturerTest.java | 88 +++++++ .../sf/openrocket/util/CoordinateTest.java | 64 +++++ test/net/sf/openrocket/util/MathUtilTest.java | 148 ++++++++++++ .../sf/openrocket/util/Rotation2DTest.java | 31 +++ test/net/sf/openrocket/util/TextUtilTest.java | 210 ++++++++++++++++ 54 files changed, 1057 insertions(+), 366 deletions(-) create mode 100644 pix-src/eventicons/event-ejection-charge.xcf.gz create mode 100644 pix-src/eventicons/event-ground-hit.xcf.gz create mode 100644 pix-src/eventicons/event-launch.xcf.gz create mode 100644 pix-src/eventicons/event-recovery-device-deployment.xcf.gz create mode 100644 pix/eventicons/event-ejection-charge.png create mode 100644 pix/eventicons/event-ground-hit.png create mode 100644 pix/eventicons/event-launch.png create mode 100644 pix/eventicons/event-recovery-device-deployment.png create mode 100644 pix/icon/icon-about.png create mode 100644 src/net/sf/openrocket/motor/Manufacturer.java rename src/net/sf/openrocket/{rocketcomponent => motor}/Motor.java (98%) rename src/net/sf/openrocket/{rocketcomponent => motor}/ThrustCurveMotor.java (96%) rename src/net/sf/openrocket/{util => utils}/Analysis.java (98%) create mode 100644 test/Test.java create mode 100644 test/net/sf/openrocket/motor/ManufacturerTest.java create mode 100644 test/net/sf/openrocket/util/CoordinateTest.java create mode 100644 test/net/sf/openrocket/util/MathUtilTest.java create mode 100644 test/net/sf/openrocket/util/Rotation2DTest.java create mode 100644 test/net/sf/openrocket/util/TextUtilTest.java diff --git a/.classpath b/.classpath index 290b5a5e..fc1bd663 100644 --- a/.classpath +++ b/.classpath @@ -2,10 +2,12 @@ + + diff --git a/ChangeLog b/ChangeLog index 75658dd2..44f2149e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2009-08-29 Sampo Niskanen + + * Extracted motor manufacturer into separate class + * Started writing unit tests + +2009-08-28 Sampo Niskanen + + * Added icon and source info to About dialog + * Finalized flight event plot icons + 2009-08-27 Sampo Niskanen * Allow clicking on label to toggle checkbox in two tables diff --git a/TODO b/TODO index 6bcc1be1..0309f3e2 100644 --- a/TODO +++ b/TODO @@ -4,15 +4,11 @@ Feature roadmap for OpenRocket 1.0 Must-have: -- Draw remaining event icons (for 0.9.3) -- Change automatic exception reporting success code to 202 (for 0.9.3) -- Test automatic exception reporting (for 0.9.3) - Allow editing user-defined materials - Go through thrust curves and correct errors - Add styrofoam and depron materials - Through-the-wall fins -- Update "About" dialog with icon and source info Bugs: @@ -56,4 +52,7 @@ Done: - Read more thrust curve formats - Showing events in plots - Table boolean selecting by clicking label +- Test automatic exception reporting (for 0.9.3) +- Draw remaining event icons (for 0.9.3) +- Update "About" dialog with icon and source info diff --git a/build.properties b/build.properties index a4010f5a..4bf3b9e3 100644 --- a/build.properties +++ b/build.properties @@ -1,8 +1,12 @@ # The OpenRocket build version + build.version=0.9.3pre + # The source of the package. When building a package for a specific # distribution (Debian, Fedora etc.), this should be changed appropriately! +# This is displayed in the About dialog and included in bug reports. + build.source=default diff --git a/pix-src/eventicons/event-ejection-charge.xcf.gz b/pix-src/eventicons/event-ejection-charge.xcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..206593ed84aa9d18f3ecdabb30783fc5fe36884b GIT binary patch literal 4766 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 TDc)|UKTrM*3b80qpAG;3z_K)b literal 0 HcmV?d00001 diff --git a/pix-src/eventicons/event-recovery-device-deployment.xcf.gz b/pix-src/eventicons/event-recovery-device-deployment.xcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..c33abef4b395ede83f491cf037fb843c7e2c9e17 GIT binary patch literal 4702 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 literal 0 HcmV?d00001 diff --git a/pix/eventicons/copyright.txt b/pix/eventicons/copyright.txt index 9fa8d1b0..50170ef5 100644 --- a/pix/eventicons/copyright.txt +++ b/pix/eventicons/copyright.txt @@ -1,5 +1,6 @@ +event-simulation-end.png from famfamfam Silk icon set +(http://www.famfamfam.com/lab/icons/silk/) -event-simulation-end.png from famfamfam icon set -All others custom-drawn. +All others custom-drawn by Sampo Niskanen. diff --git a/pix/eventicons/event-ejection-charge.png b/pix/eventicons/event-ejection-charge.png new file mode 100644 index 0000000000000000000000000000000000000000..d58eb8e12d7d167f0181499eb9efdae8e4268c4a GIT binary patch literal 466 zcmV;@0WJQCP)Px#24YJ`L;(K){{a7>y{D4^00DYQL_t(I%Y~9pE3{D<#((!Pm>Er)dgWi#EKo{H zy)Vgvl9Xa;uY3aE#8<#ZVx^|cWI;A>p&^Qe@;72cG>>>Li-(M7o*7r2#X09X*ZJM& z+(@GQz$AJ~?S}}`p8YA9wE}~bQeg5sb6RHNlfac&C?M*Uh?XY^@l2Feb}R`P2TXt^ zz-ECY=;hlQtbLk!;FcpEfD>Xs3aAItKkxEu-fDm>Q4A4g8{q;^gn>iQeffaDjFD!Q zG+}mucZ@u+L>s&0imUajoeUuo)Dq|0D71Cl@nl ziC5Tozq3j5CF8f7q#q0adb@_38WQQzfljqF#w`}9vswQ*?YS2rUosh#z*CFJgM zgOfwuibcZaIB(>gH{!8so1W^^>G}P@?{8qjbulo&qZ7iFEVyO-o%wZ8;Dvx;_)9Gr zV8}aYDhL&3fdIw7AnX{lMgg&5;1L}E8ek|3w&*9oBgp#wAM{tx8>@$CDF6Tf07*qo IM6N<$g696wmH+?% literal 0 HcmV?d00001 diff --git a/pix/eventicons/event-ground-hit.png b/pix/eventicons/event-ground-hit.png new file mode 100644 index 0000000000000000000000000000000000000000..583e402ac27638aedd80cebf29a30920c2fa8ade GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&)pdb&7_R4QfF0136hSeK5cQEJb zyxFpKD~CpgAZz!Ez;&&TPJiTiR?0|JoH;XhXK{P@q=+L?Vq03JrxfqXy%u=uPLA5* zof0{!i__Q5-%*v2pU1|$OP|L+vMKeCe45pcP8vEU|Y&)BRhYOuvg;RU5|y< z>rQLxmn=|QFtd%_wy%6rx#t(p`ExeSKVlPS)xPP|58J2vj_I@{4BP`2XK0{x)}}2A6x$3ZN$$JYD@<);T3K0RVyd Be1ZS~ literal 0 HcmV?d00001 diff --git a/pix/eventicons/event-launch.png b/pix/eventicons/event-launch.png new file mode 100644 index 0000000000000000000000000000000000000000..85d714dfe0f896e1a02cb8f3b4aed755fa39e332 GIT binary patch literal 623 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 JPDHLkV1kr^d%yqy literal 0 HcmV?d00001 diff --git a/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java b/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java index efdd14b1..35d53a18 100644 --- a/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java +++ b/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java @@ -5,8 +5,8 @@ import static net.sf.openrocket.util.MathUtil.pow2; import java.util.Iterator; import java.util.Map; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; diff --git a/src/net/sf/openrocket/database/Databases.java b/src/net/sf/openrocket/database/Databases.java index 3f4d8e20..f3fa4bcf 100644 --- a/src/net/sf/openrocket/database/Databases.java +++ b/src/net/sf/openrocket/database/Databases.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import net.sf.openrocket.file.GeneralMotorLoader; import net.sf.openrocket.material.Material; import net.sf.openrocket.material.MaterialStorage; -import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.util.JarUtil; import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.Prefs; @@ -229,7 +229,7 @@ public class Databases { boolean match = true; if (type != null && type != m.getMotorType()) match = false; - else if (manufacturer != null && !manufacturer.equalsIgnoreCase(m.getManufacturer())) + else if (manufacturer != null && !m.getManufacturer().matches(manufacturer)) match = false; else if (designation != null && !designation.equalsIgnoreCase(m.getDesignation())) match = false; diff --git a/src/net/sf/openrocket/file/GeneralMotorLoader.java b/src/net/sf/openrocket/file/GeneralMotorLoader.java index cd4ec007..5981c2d0 100644 --- a/src/net/sf/openrocket/file/GeneralMotorLoader.java +++ b/src/net/sf/openrocket/file/GeneralMotorLoader.java @@ -6,7 +6,7 @@ import java.io.Reader; import java.nio.charset.Charset; import java.util.List; -import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.motor.Motor; /** * A motor loader class that detects the file type based on the file name extension. diff --git a/src/net/sf/openrocket/file/MotorLoader.java b/src/net/sf/openrocket/file/MotorLoader.java index 2988a03e..004b14f1 100644 --- a/src/net/sf/openrocket/file/MotorLoader.java +++ b/src/net/sf/openrocket/file/MotorLoader.java @@ -8,174 +8,15 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.util.MathUtil; public abstract class MotorLoader implements Loader { - /** - * Manufacturer codes to expand. These are general translations that are used - * both in RASP and RockSim engine files. - */ - private static final Map MANUFACTURER_CODES = - new HashMap(); - static { - - /* - * TODO: CRITICAL: Should names have Inc. LLC. etc? - */ - - // AeroTech has many name combinations... - for (String s: new String[] { "A", "AT", "AERO", "AEROT", "AEROTECH" }) { - MANUFACTURER_CODES.put(s, "AeroTech"); - MANUFACTURER_CODES.put(s+"-RMS", "AeroTech"); - MANUFACTURER_CODES.put(s+"/RMS", "AeroTech"); - MANUFACTURER_CODES.put(s+"-RCS", "AeroTech"); - MANUFACTURER_CODES.put(s+"/RCS", "AeroTech"); - MANUFACTURER_CODES.put("RCS-" + s, "AeroTech"); - MANUFACTURER_CODES.put("RCS/" + s, "AeroTech"); - MANUFACTURER_CODES.put(s+"-APOGEE", "AeroTech"); - MANUFACTURER_CODES.put(s+"/APOGEE", "AeroTech"); - } - MANUFACTURER_CODES.put("ISP", "AeroTech"); - - MANUFACTURER_CODES.put("AHR", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRID", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRIDS", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRIDS ROCKETRY", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY LLC", "Alpha Hybrid Rocketry LLC"); - MANUFACTURER_CODES.put("ALPHA HYBRID ROCKETRY, LLC", "Alpha Hybrid Rocketry LLC"); - - MANUFACTURER_CODES.put("AMW", "Animal Motor Works"); - MANUFACTURER_CODES.put("AW", "Animal Motor Works"); - MANUFACTURER_CODES.put("ANIMAL", "Animal Motor Works"); - MANUFACTURER_CODES.put("ANIMAL MOTOR WORKS", "Animal Motor Works"); - - MANUFACTURER_CODES.put("AP", "Apogee"); - MANUFACTURER_CODES.put("APOG", "Apogee"); - MANUFACTURER_CODES.put("APOGEE", "Apogee"); - MANUFACTURER_CODES.put("P", "Apogee"); - - MANUFACTURER_CODES.put("CES", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CESARONI", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CESARONI TECHNOLOGY", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INC", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INC.", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CESARONI TECHNOLOGY INCORPORATED", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CTI", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CS", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("CSR", "Cesaroni Technology Inc."); - MANUFACTURER_CODES.put("PRO38", "Cesaroni Technology Inc."); - - MANUFACTURER_CODES.put("CR", "Contrail Rockets"); - MANUFACTURER_CODES.put("CONTR", "Contrail Rockets"); - MANUFACTURER_CODES.put("CONTRAIL", "Contrail Rockets"); - MANUFACTURER_CODES.put("CONTRAIL ROCKET", "Contrail Rockets"); - MANUFACTURER_CODES.put("CONTRAIL ROCKETS", "Contrail Rockets"); - - MANUFACTURER_CODES.put("E", "Estes"); - MANUFACTURER_CODES.put("ES", "Estes"); - MANUFACTURER_CODES.put("ESTES", "Estes"); - - MANUFACTURER_CODES.put("EM", "Ellis Mountain"); - MANUFACTURER_CODES.put("ELLIS", "Ellis Mountain"); - MANUFACTURER_CODES.put("ELLIS MOUNTAIN", "Ellis Mountain"); - MANUFACTURER_CODES.put("ELLIS MOUNTAIN ROCKET", "Ellis Mountain"); - MANUFACTURER_CODES.put("ELLIS MOUNTAIN ROCKETS", "Ellis Mountain"); - - MANUFACTURER_CODES.put("GR", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA ROCKET", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA ROCKETS", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA MOTOR", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA MOTORS", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA ROCKET MOTOR", "Gorilla Rocket Motors"); - MANUFACTURER_CODES.put("GORILLA ROCKET MOTORS", "Gorilla Rocket Motors"); - - MANUFACTURER_CODES.put("H", "HyperTEK"); - MANUFACTURER_CODES.put("HT", "HyperTEK"); - MANUFACTURER_CODES.put("HYPER", "HyperTEK"); - MANUFACTURER_CODES.put("HYPERTEK", "HyperTEK"); - - MANUFACTURER_CODES.put("K", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KBA", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("K/AT", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("K-AT", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOS", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON/AT", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON-AT", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON/AEROTECH", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON-AEROTECH", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON-BY-AEROTECH", "Kosdon by AeroTech"); - MANUFACTURER_CODES.put("KOSDON BY AEROTECH", "Kosdon by AeroTech"); - - MANUFACTURER_CODES.put("LOKI", "Loki Research"); - MANUFACTURER_CODES.put("LOKI RESEARCH", "Loki Research"); - MANUFACTURER_CODES.put("LR", "Loki Research"); - - MANUFACTURER_CODES.put("PM", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PML", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES LTD", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES, LTD", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES LTD.", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES, LTD.", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES LIMITED", "Public Missiles, Ltd."); - MANUFACTURER_CODES.put("PUBLIC MISSILES, LIMITED", "Public Missiles, Ltd."); - - MANUFACTURER_CODES.put("PP", "Propulsion Polymers"); - MANUFACTURER_CODES.put("PROP", "Propulsion Polymers"); - MANUFACTURER_CODES.put("PROPULSION", "Propulsion Polymers"); - MANUFACTURER_CODES.put("PROPULSION-POLYMERS", "Propulsion Polymers"); - MANUFACTURER_CODES.put("PROPULSION POLYMERS", "Propulsion Polymers"); - - MANUFACTURER_CODES.put("Q", "Quest"); - MANUFACTURER_CODES.put("QU", "Quest"); - MANUFACTURER_CODES.put("QUEST", "Quest"); - - MANUFACTURER_CODES.put("RATT", "RATT Works"); - MANUFACTURER_CODES.put("RATT WORKS", "RATT Works"); - MANUFACTURER_CODES.put("RT", "RATT Works"); - MANUFACTURER_CODES.put("RTW", "RATT Works"); - - MANUFACTURER_CODES.put("RR", "Roadrunner Rocketry"); - MANUFACTURER_CODES.put("ROADRUNNER", "Roadrunner Rocketry"); - MANUFACTURER_CODES.put("ROADRUNNER ROCKETRY", "Roadrunner Rocketry"); - - MANUFACTURER_CODES.put("RV", "Rocketvision"); - MANUFACTURER_CODES.put("ROCKETVISION", "Rocketvision"); - - MANUFACTURER_CODES.put("SR", "Sky Ripper Systems"); - MANUFACTURER_CODES.put("SRS", "Sky Ripper Systems"); - MANUFACTURER_CODES.put("SKYR", "Sky Ripper Systems"); - MANUFACTURER_CODES.put("SKYRIPPER", "Sky Ripper Systems"); - MANUFACTURER_CODES.put("SKYRIPPER SYSTEMS", "Sky Ripper Systems"); - MANUFACTURER_CODES.put("SKY RIPPER SYSTEMS", "Sky Ripper Systems"); - - MANUFACTURER_CODES.put("WCH", "West Coast Hybrids"); - MANUFACTURER_CODES.put("WCR", "West Coast Hybrids"); - MANUFACTURER_CODES.put("WEST COAST HYBRIDS", "West Coast Hybrids"); - - MANUFACTURER_CODES.put("SF", "WECO Feuerwerk"); // Previously Sachsen Feuerwerks - MANUFACTURER_CODES.put("SACHSEN FEUERWERK", "WECO Feuerwerk"); - MANUFACTURER_CODES.put("SACHSEN FEUERWERKS", "WECO Feuerwerk"); - MANUFACTURER_CODES.put("WECO", "WECO Feuerwerk"); - MANUFACTURER_CODES.put("WECO FEUERWERK", "WECO Feuerwerk"); - MANUFACTURER_CODES.put("WECO FEUERWERKS", "WECO Feuerwerk"); - } - - - - /** * Load motors from the specified InputStream. The file is read using * the default charset returned by {@link #getDefaultCharset()}. @@ -330,28 +171,7 @@ public abstract class MotorLoader implements Loader { } while (index < primary.size()-1); } - - /** - * Convert a manufacturer string. This should be done for all manufacturers - * for overall consistency. This includes both RASP name expansions and some - * general renaming. This should also be performed when loading designs in order - * to identify manufacturers correctly. - * - * @param mfg the original manufacturer / manufacturer code. - * @return the manufacturer name. - */ - public static String convertManufacturer(String mfg) { - // Replace underscore and trim - mfg = mfg.replace('_', ' ').trim(); - - // Check for conversion - String conv = MANUFACTURER_CODES.get(mfg.toUpperCase()); - if (conv != null) - return conv; - else - return mfg; - } - + @SuppressWarnings("unchecked") protected static void finalizeThrustCurve(List time, List thrust, diff --git a/src/net/sf/openrocket/file/OpenRocketLoader.java b/src/net/sf/openrocket/file/OpenRocketLoader.java index f7b270fc..1d33fd18 100644 --- a/src/net/sf/openrocket/file/OpenRocketLoader.java +++ b/src/net/sf/openrocket/file/OpenRocketLoader.java @@ -20,6 +20,7 @@ import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.SimpleSAX; import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.BodyComponent; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.Bulkhead; @@ -37,7 +38,6 @@ import net.sf.openrocket.rocketcomponent.InternalComponent; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; import net.sf.openrocket.rocketcomponent.MassObject; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.Parachute; @@ -1030,7 +1030,7 @@ class MotorHandler extends ElementHandler { } else if (element.equals("manufacturer")) { // Manufacturer - manufacturer = MotorLoader.convertManufacturer(content); + manufacturer = content; } else if (element.equals("designation")) { diff --git a/src/net/sf/openrocket/file/RASPMotorLoader.java b/src/net/sf/openrocket/file/RASPMotorLoader.java index 4de79825..558e7cd8 100644 --- a/src/net/sf/openrocket/file/RASPMotorLoader.java +++ b/src/net/sf/openrocket/file/RASPMotorLoader.java @@ -8,8 +8,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import net.sf.openrocket.rocketcomponent.Motor; -import net.sf.openrocket.rocketcomponent.ThrustCurveMotor; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.util.Coordinate; public class RASPMotorLoader extends MotorLoader { @@ -118,7 +119,7 @@ public class RASPMotorLoader extends MotorLoader { propW = Double.parseDouble(pieces[4]); totalW = Double.parseDouble(pieces[5]); - manufacturer = convertManufacturer(pieces[6]); + manufacturer = pieces[6]; if (propW > totalW) { throw new IOException("Propellant weight exceeds total weight in " + @@ -196,7 +197,8 @@ public class RASPMotorLoader extends MotorLoader { try { - return new ThrustCurveMotor(manufacturer, designation, comment, Motor.Type.UNKNOWN, + return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), + designation, comment, Motor.Type.UNKNOWN, delays, diameter, length, timeArray, thrustArray, cgArray); } catch (IllegalArgumentException e) { diff --git a/src/net/sf/openrocket/file/RockSimMotorLoader.java b/src/net/sf/openrocket/file/RockSimMotorLoader.java index eef74ac9..b39a9218 100644 --- a/src/net/sf/openrocket/file/RockSimMotorLoader.java +++ b/src/net/sf/openrocket/file/RockSimMotorLoader.java @@ -12,8 +12,9 @@ import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.NullElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.file.simplesax.SimpleSAX; -import net.sf.openrocket.rocketcomponent.Motor; -import net.sf.openrocket.rocketcomponent.ThrustCurveMotor; +import net.sf.openrocket.motor.Manufacturer; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.motor.ThrustCurveMotor; import net.sf.openrocket.util.Coordinate; import org.xml.sax.InputSource; @@ -147,7 +148,7 @@ public class RockSimMotorLoader extends MotorLoader { str = attributes.get("mfg"); if (str == null) throw new SAXException("Manufacturer missing"); - manufacturer = convertManufacturer(str); + manufacturer = str; // Designation str = attributes.get("code"); @@ -339,7 +340,8 @@ public class RockSimMotorLoader extends MotorLoader { } try { - return new ThrustCurveMotor(manufacturer, designation, description, type, + return new ThrustCurveMotor(Manufacturer.getManufacturer(manufacturer), + designation, description, type, delays, diameter, length, timeArray, thrustArray, cgArray); } catch (IllegalArgumentException e) { throw new SAXException("Illegal motor data", e); diff --git a/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java b/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java index 1a1a3156..a9da9b2f 100644 --- a/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java +++ b/src/net/sf/openrocket/file/openrocket/RocketComponentSaver.java @@ -7,8 +7,8 @@ import java.util.List; import net.sf.openrocket.file.RocketSaver; import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.ComponentAssembly; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -121,7 +121,7 @@ public class RocketComponentSaver { if (motor.getMotorType() != Motor.Type.UNKNOWN) { elements.add(" " + motor.getMotorType().name().toLowerCase() + ""); } - elements.add(" " + RocketSaver.escapeXML(motor.getManufacturer()) + ""); + elements.add(" " + RocketSaver.escapeXML(motor.getManufacturer().getSimpleName()) + ""); elements.add(" " + RocketSaver.escapeXML(motor.getDesignation()) + ""); elements.add(" " + motor.getDiameter() + ""); elements.add(" " + motor.getLength() + ""); diff --git a/src/net/sf/openrocket/gui/configdialog/MotorConfig.java b/src/net/sf/openrocket/gui/configdialog/MotorConfig.java index 7e0afef3..c4210e6c 100644 --- a/src/net/sf/openrocket/gui/configdialog/MotorConfig.java +++ b/src/net/sf/openrocket/gui/configdialog/MotorConfig.java @@ -25,8 +25,8 @@ import net.sf.openrocket.gui.adaptors.MotorConfigurationModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.UnitSelector; import net.sf.openrocket.gui.dialogs.MotorChooserDialog; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -186,7 +186,7 @@ public class MotorConfig extends JPanel { if (m == null) motorLabel.setText("None"); else - motorLabel.setText(m.getManufacturer() + " " + + motorLabel.setText(m.getManufacturer().getDisplayName() + " " + m.getDesignation(mount.getMotorDelay(id))); } diff --git a/src/net/sf/openrocket/gui/dialogs/AboutDialog.java b/src/net/sf/openrocket/gui/dialogs/AboutDialog.java index 3cc99f5c..3b151f70 100644 --- a/src/net/sf/openrocket/gui/dialogs/AboutDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/AboutDialog.java @@ -6,12 +6,14 @@ import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JLabel; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.components.ResizeLabel; import net.sf.openrocket.gui.components.URLLabel; import net.sf.openrocket.util.GUIUtil; +import net.sf.openrocket.util.Icons; import net.sf.openrocket.util.Prefs; public class AboutDialog extends JDialog { @@ -26,12 +28,24 @@ public class AboutDialog extends JDialog { JPanel panel = new JPanel(new MigLayout("fill")); - panel.add(new ResizeLabel("OpenRocket", 20), "ax 50%, wrap para"); - panel.add(new ResizeLabel("Version " + version, 3), "ax 50%, wrap 30lp"); + panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), + "spany 5, top"); - panel.add(new ResizeLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"), "ax 50%, wrap para"); + panel.add(new ResizeLabel("OpenRocket", 20), "ax 50%, growy, wrap para"); + panel.add(new ResizeLabel("Version " + version, 3), "ax 50%, growy, wrap rel"); - panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, wrap para"); + String source = Prefs.getBuildSource(); + if (!Prefs.DEFAULT_BUILD_SOURCE.equalsIgnoreCase(source)) { + panel.add(new ResizeLabel("Distributed by " + source, -1), + "ax 50%, growy, wrap para"); + } else { + panel.add(new ResizeLabel(" ", -1), "ax 50%, growy, wrap para"); + } + + panel.add(new ResizeLabel("Copyright \u00A9 2007-2009 Sampo Niskanen"), + "ax 50%, growy, wrap para"); + + panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para"); JButton close = new JButton("Close"); @@ -41,7 +55,7 @@ public class AboutDialog extends JDialog { AboutDialog.this.dispose(); } }); - panel.add(close, "right"); + panel.add(close, "spanx, right"); this.add(panel); this.setTitle("OpenRocket " + version); diff --git a/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java b/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java index add91327..b58d48a3 100644 --- a/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/BugReportDialog.java @@ -143,7 +143,7 @@ public class BugReportDialog extends JDialog { this.pack(); this.setLocationRelativeTo(parent); GUIUtil.installEscapeCloseOperation(this); -// GUIUtil.setDefaultButton(close); + GUIUtil.setDefaultButton(send); } diff --git a/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java b/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java index e554c413..a42d8a94 100644 --- a/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/EditMotorConfigurationDialog.java @@ -27,7 +27,7 @@ import javax.swing.table.TableColumnModel; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.main.BasicFrame; -import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; diff --git a/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java b/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java index 3a5a154c..95b2aad6 100644 --- a/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java +++ b/src/net/sf/openrocket/gui/dialogs/MotorChooserDialog.java @@ -38,7 +38,7 @@ import javax.swing.table.TableRowSorter; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.database.Databases; import net.sf.openrocket.gui.components.ResizeLabel; -import net.sf.openrocket.rocketcomponent.Motor; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.GUIUtil; import net.sf.openrocket.util.Prefs; @@ -382,7 +382,7 @@ public class MotorChooserDialog extends JDialog { MANUFACTURER("Manufacturer",100) { @Override public String getValue(Motor m) { - return m.getManufacturer(); + return m.getManufacturer().getDisplayName(); } // @Override // public String getToolTipText(Motor m) { diff --git a/src/net/sf/openrocket/gui/plot/PlotDialog.java b/src/net/sf/openrocket/gui/plot/PlotDialog.java index 0b8c4acd..a40c841a 100644 --- a/src/net/sf/openrocket/gui/plot/PlotDialog.java +++ b/src/net/sf/openrocket/gui/plot/PlotDialog.java @@ -81,16 +81,18 @@ public class PlotDialog extends JDialog { private static final Map EVENT_IMAGES = new HashMap(); static { - loadImage(FlightEvent.Type.LAUNCH, "pix/spheres/red-16x16.png"); + loadImage(FlightEvent.Type.LAUNCH, "pix/eventicons/event-launch.png"); loadImage(FlightEvent.Type.LIFTOFF, "pix/eventicons/event-liftoff.png"); loadImage(FlightEvent.Type.LAUNCHROD, "pix/eventicons/event-launchrod.png"); loadImage(FlightEvent.Type.IGNITION, "pix/eventicons/event-ignition.png"); loadImage(FlightEvent.Type.BURNOUT, "pix/eventicons/event-burnout.png"); - loadImage(FlightEvent.Type.EJECTION_CHARGE, "pix/spheres/green-16x16.png"); - loadImage(FlightEvent.Type.STAGE_SEPARATION, "pix/eventicons/event-stage-separation.png"); + loadImage(FlightEvent.Type.EJECTION_CHARGE,"pix/eventicons/event-ejection-charge.png"); + loadImage(FlightEvent.Type.STAGE_SEPARATION, + "pix/eventicons/event-stage-separation.png"); loadImage(FlightEvent.Type.APOGEE, "pix/eventicons/event-apogee.png"); - loadImage(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, "pix/spheres/blue-16x16.png"); - loadImage(FlightEvent.Type.GROUND_HIT, "pix/spheres/gray-16x16.png"); + loadImage(FlightEvent.Type.RECOVERY_DEVICE_DEPLOYMENT, + "pix/eventicons/event-recovery-device-deployment.png"); + loadImage(FlightEvent.Type.GROUND_HIT, "pix/eventicons/event-ground-hit.png"); loadImage(FlightEvent.Type.SIMULATION_END, "pix/eventicons/event-simulation-end.png"); } diff --git a/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index f6d7d9de..35158def 100644 --- a/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -20,8 +20,8 @@ import java.util.Iterator; import java.util.LinkedHashSet; import net.sf.openrocket.gui.figureelements.FigureElement; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.Coordinate; diff --git a/src/net/sf/openrocket/motor/Manufacturer.java b/src/net/sf/openrocket/motor/Manufacturer.java new file mode 100644 index 00000000..3e7365be --- /dev/null +++ b/src/net/sf/openrocket/motor/Manufacturer.java @@ -0,0 +1,226 @@ +package net.sf.openrocket.motor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Manufacturer { + + private static Set manufacturers = new HashSet(); + static { + + // AeroTech has many name combinations... + List names = new ArrayList(); + for (String s: new String[] { "A", "AT", "AERO", "AEROT", "AEROTECH" }) { + names.add(s); + names.add(s+"-RMS"); + names.add(s+"-RCS"); + names.add("RCS-" + s); + names.add(s+"-APOGEE"); + } + names.add("ISP"); + + manufacturers.add(new Manufacturer("AeroTech", "AeroTech", + names.toArray(new String[0]))); + + manufacturers.add(new Manufacturer("Alpha Hybrid Rocketry LLC", + "Alpha Hybrid Rocketry", + "AHR", "ALPHA", "ALPHA HYBRID", "ALPHA HYBRIDS", "ALPHA HYBRIDS ROCKETRY")); + + manufacturers.add(new Manufacturer("Animal Motor Works","Animal Motor Works", + "AMW", "AW", "ANIMAL")); + + manufacturers.add(new Manufacturer("Apogee","Apogee", + "AP", "APOG", "P")); + + manufacturers.add(new Manufacturer("Cesaroni Technology Inc.", + "Cesaroni Technology", + "CES", "CESARONI", "CESARONI TECHNOLOGY INCORPORATED", "CTI", + "CS", "CSR", "PRO38")); + + manufacturers.add(new Manufacturer("Contrail Rockets","Contrail Rockets", + "CR", "CONTR", "CONTRAIL", "CONTRAIL ROCKET")); + + manufacturers.add(new Manufacturer("Estes","Estes", + "E", "ES")); + + manufacturers.add(new Manufacturer("Ellis Mountain","Ellis Mountain", + "EM", "ELLIS", "ELLIS MOUNTAIN ROCKET", "ELLIS MOUNTAIN ROCKETS")); + + manufacturers.add(new Manufacturer("Gorilla Rocket Motors", + "Gorilla Rocket Motors", + "GR", "GORILLA", "GORILLA ROCKET", "GORILLA ROCKETS", "GORILLA MOTOR", + "GORILLA MOTORS", "GORILLA ROCKET MOTOR")); + + manufacturers.add(new Manufacturer("HyperTEK", "HyperTEK", + "H", "HT", "HYPER")); + + manufacturers.add(new Manufacturer("Kosdon by AeroTech", "Kosdon by AeroTech", + "K", "KBA", "K-AT", "KOS", "KOSDON", "KOSDON/AT", "KOSDON/AEROTECH")); + + manufacturers.add(new Manufacturer("Loki Research", "Loki Research", + "LOKI", "LR")); + + manufacturers.add(new Manufacturer("Public Missiles, Ltd.", "Public Missiles", + "PM", "PML", "PUBLIC MISSILES LIMITED")); + + manufacturers.add(new Manufacturer("Propulsion Polymers", "Propulsion Polymers", + "PP", "PROP", "PROPULSION")); + + manufacturers.add(new Manufacturer("Quest", "Quest", + "Q", "QU")); + + manufacturers.add(new Manufacturer("RATT Works", "RATT Works", + "RATT", "RT", "RTW")); + + manufacturers.add(new Manufacturer("Roadrunner Rocketry","Roadrunner Rocketry", + "RR", "ROADRUNNER")); + + manufacturers.add(new Manufacturer("Rocketvision", "Rocketvision", + "RV", "ROCKET VISION")); + + manufacturers.add(new Manufacturer("Sky Ripper Systems","Sky Ripper Systems", + "SR", "SRS", "SKYR", "SKYRIPPER", "SKY RIPPER", "SKYRIPPER SYSTEMS")); + + manufacturers.add(new Manufacturer("West Coast Hybrids", "West Coast Hybrids", + "WCH", "WCR", "WEST COAST", "WEST COAST HYBRID")); + + // German WECO Feuerwerk, previously Sachsen Feuerwerk + manufacturers.add(new Manufacturer("WECO Feuerwerk", "WECO Feuerwerk", + "WECO", "WECO FEUERWERKS", "SF", "SACHSEN", "SACHSEN FEUERWERK", + "SACHSEN FEUERWERKS")); + + + // Check that no duplicates have appeared + for (Manufacturer m1: manufacturers) { + for (Manufacturer m2: manufacturers) { + if (m1 == m2) + continue; + for (String name: m1.getAllNames()) { + if (m2.matches(name)) { + throw new IllegalStateException("Manufacturer name clash between " + + "manufacturers " + m1 + " and " + m2 + " name " + name); + } + } + } + } + } + + + + private final String displayName; + private final String simpleName; + private final Set allNames; + private final Set searchNames; + + + private Manufacturer(String displayName, String simpleName, String... alternateNames) { + this.displayName = displayName; + this.simpleName = simpleName; + + Set all = new HashSet(); + Set search = new HashSet(); + + all.add(displayName); + all.add(simpleName); + search.add(generateSearchString(displayName)); + search.add(generateSearchString(simpleName)); + + for (String name: alternateNames) { + all.add(name); + search.add(generateSearchString(name)); + } + + this.allNames = Collections.unmodifiableSet(all); + this.searchNames = Collections.unmodifiableSet(search); + } + + + /** + * Returns the display name of the manufacturer. This is the value that + * should be presented in the UI to the user. + * + * @return the display name + */ + public String getDisplayName() { + return displayName; + } + + + /** + * Returns the simple name of the manufacturer. This should be used for example + * when saving the manufacturer for compatibility. + * + * @return the simple name. + */ + public String getSimpleName() { + return simpleName; + } + + + /** + * Return all names of the manufacturer. This includes all kinds of + * codes that correspond to the manufacturer (for example "A" for AeroTech). + * + * @return an unmodifiable set of the alternative names. + */ + public Set getAllNames() { + return allNames; + } + + + /** + * Check whether the display, simple or any of the alternative names matches the + * specified name. Matching is performed case insensitively and ignoring any + * non-letter and non-number characters. + * + * @param name the name to search for. + * @return whether this manufacturer matches the request. + */ + public boolean matches(String name) { + if (name == null) + return false; + return this.searchNames.contains(generateSearchString(name)); + } + + + /** + * Return the display name of the manufacturer. + */ + @Override + public String toString() { + return displayName; + } + + + /** + * Returns a manufacturer for the given name. The manufacturer is searched for + * within the manufacturers and if a match is found the corresponding + * object is returned. If not, a new manufacturer is returned with display and + * simple name the name specified. Subsequent requests for the same (or corresponding) + * manufacturer name will return the same object. + * + * @param name the manufacturer name to search for. + * @return the Manufacturer object corresponding the name. + */ + public static synchronized Manufacturer getManufacturer(String name) { + for (Manufacturer m: manufacturers) { + if (m.matches(name)) + return m; + } + + Manufacturer m = new Manufacturer(name.trim(), name.trim()); + manufacturers.add(m); + return m; + } + + + + + private String generateSearchString(String str) { + return str.toLowerCase().replaceAll("[^a-zA-Z0-9]+", " ").trim(); + } + +} diff --git a/src/net/sf/openrocket/rocketcomponent/Motor.java b/src/net/sf/openrocket/motor/Motor.java similarity index 98% rename from src/net/sf/openrocket/rocketcomponent/Motor.java rename to src/net/sf/openrocket/motor/Motor.java index 3eef45a1..0da9c3b4 100644 --- a/src/net/sf/openrocket/rocketcomponent/Motor.java +++ b/src/net/sf/openrocket/motor/Motor.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.rocketcomponent; +package net.sf.openrocket.motor; import java.text.Collator; import java.util.Comparator; @@ -94,7 +94,7 @@ public abstract class Motor implements Comparable { - private final String manufacturer; + private final Manufacturer manufacturer; private final String designation; private final String description; private final Type motorType; @@ -125,7 +125,7 @@ public abstract class Motor implements Comparable { * @param diameter maximum diameter of the motor * @param length length of the motor */ - protected Motor(String manufacturer, String designation, String description, + protected Motor(Manufacturer manufacturer, String designation, String description, Type type, double[] delays, double diameter, double length) { if (manufacturer == null || designation == null || description == null || @@ -355,7 +355,7 @@ public abstract class Motor implements Comparable { * * @return the manufacturer */ - public String getManufacturer() { + public Manufacturer getManufacturer() { return manufacturer; } @@ -539,7 +539,8 @@ public abstract class Motor implements Comparable { int value; // 1. Manufacturer - value = COLLATOR.compare(this.manufacturer, other.manufacturer); + value = COLLATOR.compare(this.manufacturer.getDisplayName(), + other.manufacturer.getDisplayName()); if (value != 0) return value; diff --git a/src/net/sf/openrocket/rocketcomponent/ThrustCurveMotor.java b/src/net/sf/openrocket/motor/ThrustCurveMotor.java similarity index 96% rename from src/net/sf/openrocket/rocketcomponent/ThrustCurveMotor.java rename to src/net/sf/openrocket/motor/ThrustCurveMotor.java index 9519aa3e..e495defe 100644 --- a/src/net/sf/openrocket/rocketcomponent/ThrustCurveMotor.java +++ b/src/net/sf/openrocket/motor/ThrustCurveMotor.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.rocketcomponent; +package net.sf.openrocket.motor; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; @@ -33,7 +33,7 @@ public class ThrustCurveMotor extends Motor { * @param thrust thrust at the time points. * @param cg cg at the time points. */ - public ThrustCurveMotor(String manufacturer, String designation, String description, + public ThrustCurveMotor(Manufacturer manufacturer, String designation, String description, Motor.Type type, double[] delays, double diameter, double length, double[] time, double[] thrust, Coordinate[] cg) { super(manufacturer, designation, description, type, delays, diameter, length); diff --git a/src/net/sf/openrocket/rocketcomponent/BodyTube.java b/src/net/sf/openrocket/rocketcomponent/BodyTube.java index d22ab67d..d3368f1f 100644 --- a/src/net/sf/openrocket/rocketcomponent/BodyTube.java +++ b/src/net/sf/openrocket/rocketcomponent/BodyTube.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; diff --git a/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/src/net/sf/openrocket/rocketcomponent/InnerTube.java index 7561c21e..901b5cb6 100644 --- a/src/net/sf/openrocket/rocketcomponent/InnerTube.java +++ b/src/net/sf/openrocket/rocketcomponent/InnerTube.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; diff --git a/src/net/sf/openrocket/rocketcomponent/MotorMount.java b/src/net/sf/openrocket/rocketcomponent/MotorMount.java index b6dbb5fb..20f3c82e 100644 --- a/src/net/sf/openrocket/rocketcomponent/MotorMount.java +++ b/src/net/sf/openrocket/rocketcomponent/MotorMount.java @@ -1,5 +1,6 @@ package net.sf.openrocket.rocketcomponent; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.util.ChangeSource; diff --git a/src/net/sf/openrocket/rocketcomponent/Rocket.java b/src/net/sf/openrocket/rocketcomponent/Rocket.java index 48904f89..3bd4dcda 100644 --- a/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -12,6 +12,7 @@ import java.util.UUID; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; diff --git a/src/net/sf/openrocket/simulation/FlightSimulator.java b/src/net/sf/openrocket/simulation/FlightSimulator.java index 2b67530e..163b7ff2 100644 --- a/src/net/sf/openrocket/simulation/FlightSimulator.java +++ b/src/net/sf/openrocket/simulation/FlightSimulator.java @@ -11,9 +11,9 @@ import net.sf.openrocket.aerodynamics.AtmosphericConditions; import net.sf.openrocket.aerodynamics.AtmosphericModel; import net.sf.openrocket.aerodynamics.Warning; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.Clusterable; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RecoveryDevice; import net.sf.openrocket.rocketcomponent.RocketComponent; diff --git a/src/net/sf/openrocket/util/Coordinate.java b/src/net/sf/openrocket/util/Coordinate.java index 5b88f08a..099074ec 100644 --- a/src/net/sf/openrocket/util/Coordinate.java +++ b/src/net/sf/openrocket/util/Coordinate.java @@ -10,10 +10,51 @@ import java.io.Serializable; * @author Sampo Niskanen */ public final class Coordinate implements Serializable { + + //////// Debug section + /* + * Debugging info. If openrocket.debug.coordinatecount is defined, a line is + * printed every 1000000 instantiations (or as many as defined). + */ + private static final boolean COUNT_DEBUG; + private static final int COUNT_DIFF; + static { + String str = System.getProperty("openrocket.debug.coordinatecount", null); + int diff = 0; + if (str == null) { + COUNT_DEBUG = false; + COUNT_DIFF = 0; + } else { + COUNT_DEBUG = true; + try { + diff = Integer.parseInt(str); + } catch (NumberFormatException ignore) { } + if (diff < 1000) + diff = 1000000; + COUNT_DIFF = diff; + } + } + + private static int count = 0; + { + // Debug count + if (COUNT_DEBUG) { + count++; + if ((count % COUNT_DIFF) == 0) { + System.out.println("Coordinate instantiated " + count + " times."); + } + } + } + + //////// End debug section + + + + public static final Coordinate NUL = new Coordinate(0,0,0,0); public static final Coordinate NaN = new Coordinate(Double.NaN,Double.NaN, Double.NaN,Double.NaN); - public static final double COMPARISON_DELTA = 0.000001; + public final double x,y,z; public final double weight; @@ -21,15 +62,6 @@ public final class Coordinate implements Serializable { private double length = -1; /* Cached when calculated */ - /* Count and report the number of times a Coordinate is constructed: */ -// private static int count=0; -// { -// count++; -// if ((count % 1000) == 0) { -// System.err.println("Coordinate instantiated "+count+" times"); -// } -// } - public Coordinate() { @@ -52,6 +84,7 @@ public final class Coordinate implements Serializable { this.y = y; this.z = z; this.weight=w; + } @@ -261,31 +294,4 @@ public final class Coordinate implements Serializable { } - - public static void main(String[] arg) { - double a=1.2; - double x; - Coordinate c; - long t1, t2; - - x = 0; - t1 = System.nanoTime(); - for (int i=0; i < 100000000; i++) { - x = x + a; - } - t2 = System.nanoTime(); - System.out.println("Value: "+x); - System.out.println("Plain addition: "+ ((t2-t1+500000)/1000000) + " ms"); - - c = Coordinate.NUL; - t1 = System.nanoTime(); - for (int i=0; i < 100000000; i++) { - c = c.add(a,0,0); - } - t2 = System.nanoTime(); - System.out.println("Value: "+c.x); - System.out.println("Coordinate addition: "+ ((t2-t1+500000)/1000000) + " ms"); - - } - } diff --git a/src/net/sf/openrocket/util/Icons.java b/src/net/sf/openrocket/util/Icons.java index a97487d3..fa2c25e7 100644 --- a/src/net/sf/openrocket/util/Icons.java +++ b/src/net/sf/openrocket/util/Icons.java @@ -57,7 +57,9 @@ public class Icons { public static final Icon DELETE = loadImageIcon("pix/icons/delete.png", "Delete"); - private static ImageIcon loadImageIcon(String file, String name) { + + + public static ImageIcon loadImageIcon(String file, String name) { URL url = ClassLoader.getSystemResource(file); if (url == null) { System.err.println("Resource "+file+" not found! Ignoring..."); diff --git a/src/net/sf/openrocket/util/MathUtil.java b/src/net/sf/openrocket/util/MathUtil.java index a714f812..a40742cb 100644 --- a/src/net/sf/openrocket/util/MathUtil.java +++ b/src/net/sf/openrocket/util/MathUtil.java @@ -73,7 +73,7 @@ public class MathUtil { if (equals(toMin, toMax)) return toMin; if (equals(fromMin, fromMax)) { - throw new IllegalArgumentException("from range is singular an to range is not."); + throw new IllegalArgumentException("from range is singular and to range is not."); } return (value - fromMin)/(fromMax-fromMin) * (toMax - toMin) + toMin; } @@ -168,6 +168,16 @@ public class MathUtil { return Math.abs(a-b) < EPSILON*absb; } + + /** + * Return the sign of the number. This corresponds to Math.signum, but ignores + * the special cases of zero and NaN. The value returned for those is arbitrary. + *

+ * This method is about 4 times faster than Math.signum(). + * + * @param x the checked value. + * @return -1.0 if x<0; 1.0 if x>0; otherwise either -1.0 or 1.0. + */ public static double sign(double x) { return (x<0) ? -1.0 : 1.0; } @@ -180,38 +190,4 @@ public class MathUtil { */ - public static void main(String[] arg) { - double nan = Double.NaN; - System.out.println("min(5,6) = " + min(5, 6)); - System.out.println("min(5,nan) = " + min(5, nan)); - System.out.println("min(nan,6) = " + min(nan, 6)); - System.out.println("min(nan,nan) = " + min(nan, nan)); - System.out.println(); - System.out.println("max(5,6) = " + max(5, 6)); - System.out.println("max(5,nan) = " + max(5, nan)); - System.out.println("max(nan,6) = " + max(nan, 6)); - System.out.println("max(nan,nan) = " + max(nan, nan)); - System.out.println(); - System.out.println("min(5,6,7) = " + min(5, 6, 7)); - System.out.println("min(5,6,nan) = " + min(5, 6, nan)); - System.out.println("min(5,nan,7) = " + min(5, nan, 7)); - System.out.println("min(5,nan,nan) = " + min(5, nan, nan)); - System.out.println("min(nan,6,7) = " + min(nan, 6, 7)); - System.out.println("min(nan,6,nan) = " + min(nan, 6, nan)); - System.out.println("min(nan,nan,7) = " + min(nan, nan, 7)); - System.out.println("min(nan,nan,nan) = " + min(nan, nan, nan)); - System.out.println(); - System.out.println("max(5,6,7) = " + max(5, 6, 7)); - System.out.println("max(5,6,nan) = " + max(5, 6, nan)); - System.out.println("max(5,nan,7) = " + max(5, nan, 7)); - System.out.println("max(5,nan,nan) = " + max(5, nan, nan)); - System.out.println("max(nan,6,7) = " + max(nan, 6, 7)); - System.out.println("max(nan,6,nan) = " + max(nan, 6, nan)); - System.out.println("max(nan,nan,7) = " + max(nan, nan, 7)); - System.out.println("max(nan,nan,nan) = " + max(nan, nan, nan)); - System.out.println(); - - - } - } diff --git a/src/net/sf/openrocket/util/Prefs.java b/src/net/sf/openrocket/util/Prefs.java index 0ca273f6..049b5468 100644 --- a/src/net/sf/openrocket/util/Prefs.java +++ b/src/net/sf/openrocket/util/Prefs.java @@ -55,6 +55,7 @@ public class Prefs { private static final String BUILD_VERSION; private static final String BUILD_SOURCE; + public static final String DEFAULT_BUILD_SOURCE = "default"; static { try { diff --git a/src/net/sf/openrocket/util/Rotation2D.java b/src/net/sf/openrocket/util/Rotation2D.java index f8f063e8..1672a011 100644 --- a/src/net/sf/openrocket/util/Rotation2D.java +++ b/src/net/sf/openrocket/util/Rotation2D.java @@ -41,18 +41,4 @@ public class Rotation2D { return new Coordinate(cos*c.x + sin*c.y, cos*c.y - sin*c.x, c.z, c.weight); } - - - public static void main(String arg[]) { - Coordinate c = new Coordinate(1,1,1,2.5); - Rotation2D rot = new Rotation2D(Math.PI/4); - - System.out.println("X: "+rot.rotateX(c)); - System.out.println("Y: "+rot.rotateY(c)); - System.out.println("Z: "+rot.rotateZ(c)); - System.out.println("invX: "+rot.invRotateX(c)); - System.out.println("invY: "+rot.invRotateY(c)); - System.out.println("invZ: "+rot.invRotateZ(c)); - } - } diff --git a/src/net/sf/openrocket/util/Test.java b/src/net/sf/openrocket/util/Test.java index 4d150d42..c0e37c7a 100644 --- a/src/net/sf/openrocket/util/Test.java +++ b/src/net/sf/openrocket/util/Test.java @@ -2,6 +2,7 @@ package net.sf.openrocket.util; import net.sf.openrocket.database.Databases; import net.sf.openrocket.material.Material; +import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.Bulkhead; import net.sf.openrocket.rocketcomponent.CenteringRing; @@ -10,7 +11,6 @@ import net.sf.openrocket.rocketcomponent.IllegalFinPointException; import net.sf.openrocket.rocketcomponent.InnerTube; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.MassComponent; -import net.sf.openrocket.rocketcomponent.Motor; import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.Stage; diff --git a/src/net/sf/openrocket/util/TextUtil.java b/src/net/sf/openrocket/util/TextUtil.java index fdbf9912..7d2ee853 100644 --- a/src/net/sf/openrocket/util/TextUtil.java +++ b/src/net/sf/openrocket/util/TextUtil.java @@ -1,16 +1,13 @@ package net.sf.openrocket.util; -import java.util.Locale; public class TextUtil { /** - * Return a string of the double value with suitable precision. + * Return a string of the double value with suitable precision (5 digits). * The string is the shortest representation of the value including the * required precision. * - * TODO: MEDIUM: Extra zeros are added unnecessarily to the end of the string. - * * @param d the value to present. * @return a representation with suitable precision. */ @@ -31,46 +28,106 @@ public class TextUtil { } + final String sign = (d < 0) ? "-" : ""; double abs = Math.abs(d); - if (abs < 0.001) { - // Compact exponential notation - int exp = 0; - - while (abs < 1.0) { - abs *= 10; - exp++; - } - - String sign = (d < 0) ? "-" : ""; - return sign + String.format((Locale)null, "%.4fe-%d", abs, exp); + // Small and large values always in exponential notation + if (abs < 0.001 || abs >= 100000000) { + return sign + exponentialFormat(abs); } - if (abs < 0.01) - return String.format((Locale)null, "%.7f", d); - if (abs < 0.1) - return String.format((Locale)null, "%.6f", d); - if (abs < 1) - return String.format((Locale)null, "%.5f", d); - if (abs < 10) - return String.format((Locale)null, "%.4f", d); - if (abs < 100) - return String.format((Locale)null, "%.3f", d); - if (abs < 1000) - return String.format((Locale)null, "%.2f", d); - if (abs < 10000) - return String.format((Locale)null, "%.1f", d); - if (abs < 100000000.0) - return String.format((Locale)null, "%.0f", d); - - // Compact exponential notation - int exp = 0; - while (abs >= 10.0) { - abs /= 10; + + // Check whether decimal or exponential notation is shorter + + String exp = exponentialFormat(abs); + String dec = decimalFormat(abs); + + if (dec.length() <= exp.length()) + return sign + dec; + else + return sign + exp; + } + + + /* + * value must be positive and not zero! + */ + private static String exponentialFormat(double value) { + int exp; + + exp = 0; + while (value < 1.0) { + value *= 10; + exp--; + } + while (value >= 10.0) { + value /= 10; exp++; } - String sign = (d < 0) ? "-" : ""; - return sign + String.format((Locale)null, "%.4fe%d", abs, exp); + return shortDecimal(value, 4) + "e" + exp; + } + + + /* + * value must be positive and not zero! + */ + private static String decimalFormat(double value) { + if (value >= 10000) + return "" + (int)(value + 0.5); + + int decimals = 1; + double v = value; + while (v < 1000) { + v *= 10; + decimals++; + } + + return shortDecimal(value, decimals); + } + + + + + /* + * value must be positive! + */ + private static String shortDecimal(double value, int decimals) { + + int whole = (int)value; + value -= whole; + + // Calculate limit, return when remaining value less than this + double limit; + limit = 0.5; + for (int i=0; i