From 41a045038ed4ebf1e95eac5c24f7c087cbf7c6d9 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sun, 26 Mar 2023 21:33:26 +0200 Subject: [PATCH] [WIP] Implement RASAero exporting --- core/resources/l10n/messages.properties | 7 +- core/resources/l10n/messages_nl.properties | 5 + core/resources/l10n/messages_ru.properties | 5 + .../openrocket/file/GeneralRocketSaver.java | 13 +- .../net/sf/openrocket/file/RocketSaver.java | 2 + .../file/rasaero/CustomBooleanAdapter.java | 19 ++ .../file/rasaero/CustomDoubleAdapter.java | 22 ++ .../{importt => }/RASAeroCommonConstants.java | 198 ++++++++++-- .../file/rasaero/export/BasePartDTO.java | 133 ++++++++ .../file/rasaero/export/BodyTubeDTO.java | 156 ++++++++++ .../rasaero/export/BodyTubeDTOAdapter.java | 90 ++++++ .../file/rasaero/export/BoosterDTO.java | 285 ++++++++++++++++++ .../file/rasaero/export/FinDTO.java | 160 ++++++++++ .../file/rasaero/export/LaunchSiteDTO.java | 4 + .../file/rasaero/export/NoseConeDTO.java | 70 +++++ .../rasaero/export/RASAeroDocumentDTO.java | 87 ++++++ .../file/rasaero/export/RASAeroSaver.java | 93 ++++++ .../file/rasaero/export/RecoveryDTO.java | 4 + .../file/rasaero/export/RocketDesignDTO.java | 202 +++++++++++++ .../rasaero/export/SimulationListDTO.java | 4 + .../file/rasaero/export/TransitionDTO.java | 61 ++++ .../file/rasaero/importt/BaseHandler.java | 1 + .../file/rasaero/importt/BoattailHandler.java | 1 + .../file/rasaero/importt/BodyTubeHandler.java | 5 +- .../file/rasaero/importt/BoosterHandler.java | 13 +- .../file/rasaero/importt/FinCanHandler.java | 5 +- .../file/rasaero/importt/FinHandler.java | 15 +- .../rasaero/importt/LaunchLugHandler.java | 5 +- .../rasaero/importt/LaunchSiteHandler.java | 1 + .../file/rasaero/importt/NoseConeHandler.java | 9 +- .../file/rasaero/importt/RASAeroHandler.java | 1 + .../rasaero/importt/RailGuideHandler.java | 5 +- .../file/rasaero/importt/RecoveryHandler.java | 5 +- .../importt/SimulationListHandler.java | 1 + .../rasaero/importt/SurfaceFinishHandler.java | 3 +- .../rasaero/importt/TransitionHandler.java | 7 +- .../sf/openrocket/startup/Preferences.java | 9 + core/src/net/sf/openrocket/util/Color.java | 13 +- .../preferences/GeneralPreferencesPanel.java | 11 + .../sf/openrocket/gui/main/BasicFrame.java | 134 ++++++-- .../gui/main/DesignFileSaveAsFileChooser.java | 4 +- 41 files changed, 1777 insertions(+), 91 deletions(-) create mode 100644 core/src/net/sf/openrocket/file/rasaero/CustomBooleanAdapter.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/CustomDoubleAdapter.java rename core/src/net/sf/openrocket/file/rasaero/{importt => }/RASAeroCommonConstants.java (54%) create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/BasePartDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTOAdapter.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/BoosterDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/FinDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/LaunchSiteDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/NoseConeDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/RASAeroDocumentDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/RASAeroSaver.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/RecoveryDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/RocketDesignDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/SimulationListDTO.java create mode 100644 core/src/net/sf/openrocket/file/rasaero/export/TransitionDTO.java diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 88290ee6d..3d8cff127 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -289,6 +289,7 @@ pref.dlg.tab.Design = Design pref.dlg.tab.Simulation = Simulation pref.dlg.tab.Launch = Launch pref.dlg.tab.Miscellaneousoptions = Miscellaneous options +pref.dlg.lbl.RASAeroWarning = Show warning when saving in RASAero format pref.dlg.lbl.RockSimWarning = Show warning when saving in RockSim format pref.dlg.but.clearCachedPreferences = Reset all preferences pref.dlg.but.clearCachedPreferences.ttip = Reset all the preferences, including cached preferences (UI settings, recent files, etc.) @@ -1340,7 +1341,11 @@ TransitionCfg.tab.Generalproperties = General properties TransitionCfg.tab.Shoulder = Shoulder TransitionCfg.tab.Shoulderproperties = Shoulder properties -! Save RKT Warning Dialog +! Save RASAero Warning Dialog +SaveRASAeroWarningDialog.txt1 = Exporting to RASAero file format does not support all features of OpenRocket. +SaveRASAeroWarningDialog.donotshow = Do not show this dialog again + +! Save RockSim Warning Dialog SaveRktWarningDialog.txt1=Exporting to RockSim file format does not support all features of OpenRocket. SaveRktWarningDialog.donotshow=Do not show this dialog again diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index a2de9a92c..580477fbf 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -276,6 +276,7 @@ pref.dlg.tab.Design = Ontwerp pref.dlg.tab.Simulation = Simulatie pref.dlg.tab.Launch = Lanceer pref.dlg.tab.Miscellaneousoptions = Diverse opties +pref.dlg.lbl.RASAeroWarning = Toon waarschuwingen bij opslaan in RASAero-formaat pref.dlg.lbl.RockSimWarning = Toon waarschuwingen bij opslaan in RockSim-formaat pref.dlg.but.clearCachedPreferences = Alle voorkeuren opnieuw instellen pref.dlg.but.clearCachedPreferences.ttip = Alle voorkeuren opnieuw instellen, inclusief voorkeuren in de cache (UI-instellingen, recente bestanden, enz.) @@ -1219,6 +1220,10 @@ FinsetConfig.ttip.Finfillets1 = Voegt de voorspelde massa van de vin fille FinsetConfig.ttip.Finfillets2 = Veronderstelt dat de fillet concaaf is en raakt aan de buis en de vin.
FinsetConfig.ttip.Finfillets3 = Nul radius geeft geen fillet. +! Save RASAero Warning Dialog +SaveRASAeroWarningDialog.txt1 = Exporteren naar RASAero bestandsformaat ondersteunt niet alle functies van OpenRocket. +SaveRASAeroWarningDialog.donotshow = Laat dit dialoogvenster niet meer zien + ! Save RKT Warning Dialog SaveRktWarningDialog.txt1 = Exporteren naar RockSim bestandsformaat ondersteunt niet alle functies van OpenRocket. SaveRktWarningDialog.donotshow = Laat dit dialoogvenster niet meer zien diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 4cc9d0b6b..57de96180 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -276,6 +276,7 @@ pref.dlg.tab.Design = \u0414\u0438\u0437\u0430\u0439\u043D pref.dlg.tab.Simulation = \u0420\u0430\u0441\u0447\u0435\u0442 pref.dlg.tab.Launch = \u0417\u0430\u043F\u0443\u0441\u043A pref.dlg.tab.Miscellaneousoptions = \u041F\u0440\u043E\u0447\u0438\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 +pref.dlg.lbl.RASAeroWarning = \u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0440\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0438 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 RASAero pref.dlg.lbl.RockSimWarning = \u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0436\u0434\u0435\u043D\u0438\u0435 \u043F\u0440\u0438 \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0438 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 RockSim pref.dlg.tab.Graphics = \u0413\u0440\u0430\u0444\u0438\u043A\u0430 @@ -1267,6 +1268,10 @@ FinsetConfig.ttip.Finfillets1 = \u0414\u043E\u0431\u0430\u0432\u043B\u044F FinsetConfig.ttip.Finfillets2 = \u041F\u0440\u0435\u0434\u043F\u043E\u043B\u0430\u0433\u0430\u0435\u0442\u0441\u044F, \u0447\u0442\u043E \u0433\u0430\u043B\u0442\u0435\u043B\u044C \u043A\u0430\u0441\u0430\u0435\u0442\u0441\u044F \u0442\u0440\u0443\u0431\u044B \u043A\u043E\u0440\u043F\u0443\u0441\u0430 \u0438 \u0440\u0435\u0431\u0440\u0430.
FinsetConfig.ttip.Finfillets3 = \u041D\u0443\u043B\u0435\u0432\u043E\u0439 \u0440\u0430\u0434\u0438\u0443\u0441 \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043E \u0433\u0430\u043B\u0442\u0435\u043B\u0435\u0439 \u043D\u0435\u0442. +! Save RASAero Warning Dialog +SaveRASAeroWarningDialog.txt1 = \u042D\u043A\u0441\u043F\u043E\u0440\u0442 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442 \u0444\u0430\u0439\u043B\u0430 RASAero \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0444\u0443\u043D\u043A\u0446\u0438\u0438 OpenRocket. +SaveRASAeroWarningDialog.donotshow = \u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u044D\u0442\u043E\u0442 \u0434\u0438\u0430\u043B\u043E\u0433 + ! Save RKT Warning Dialog SaveRktWarningDialog.txt1 = \u042D\u043A\u0441\u043F\u043E\u0440\u0442 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442 \u0444\u0430\u0439\u043B\u0430 RockSim \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0444\u0443\u043D\u043A\u0446\u0438\u0438 OpenRocket. SaveRktWarningDialog.donotshow = \u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u044D\u0442\u043E\u0442 \u0434\u0438\u0430\u043B\u043E\u0433 diff --git a/core/src/net/sf/openrocket/file/GeneralRocketSaver.java b/core/src/net/sf/openrocket/file/GeneralRocketSaver.java index 511e670b2..a327e3be5 100644 --- a/core/src/net/sf/openrocket/file/GeneralRocketSaver.java +++ b/core/src/net/sf/openrocket/file/GeneralRocketSaver.java @@ -19,6 +19,7 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.StorageOptions; import net.sf.openrocket.document.StorageOptions.FileType; import net.sf.openrocket.file.openrocket.OpenRocketSaver; +import net.sf.openrocket.file.rasaero.export.RASAeroSaver; import net.sf.openrocket.file.rocksim.export.RockSimSaver; import net.sf.openrocket.rocketcomponent.InsideColorComponent; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -142,8 +143,10 @@ public class GeneralRocketSaver { * @return the estimated number of bytes the storage would take. */ public long estimateFileSize(OpenRocketDocument doc, StorageOptions options) { - if (options.getFileType() == StorageOptions.FileType.ROCKSIM) { + if (options.getFileType() == FileType.ROCKSIM) { return new RockSimSaver().estimateFileSize(doc, options); + } else if (options.getFileType() == FileType.RASAERO) { + return new RASAeroSaver().estimateFileSize(doc, options); } else { return new OpenRocketSaver().estimateFileSize(doc, options); } @@ -151,10 +154,10 @@ public class GeneralRocketSaver { private void save(String fileName, OutputStream output, OpenRocketDocument document, StorageOptions options) throws IOException, DecalNotFoundException { - // For now, we don't save decal information in ROCKSIM files, so don't do anything + // For now, we don't save decal information in ROCKSIM/RASAero files, so don't do anything // which follows. // TODO - add support for decals in ROCKSIM files? - if (options.getFileType() == FileType.ROCKSIM) { + if (options.getFileType() == FileType.ROCKSIM || options.getFileType() == FileType.RASAERO) { saveInternal(output, document, options); output.close(); return; @@ -232,8 +235,10 @@ public class GeneralRocketSaver { private void saveInternal(OutputStream output, OpenRocketDocument document, StorageOptions options) throws IOException { - if (options.getFileType() == StorageOptions.FileType.ROCKSIM) { + if (options.getFileType() == FileType.ROCKSIM) { new RockSimSaver().save(output, document, options); + } else if (options.getFileType() == FileType.RASAERO) { + new RASAeroSaver().save(output, document, options); } else { new OpenRocketSaver().save(output, document, options); } diff --git a/core/src/net/sf/openrocket/file/RocketSaver.java b/core/src/net/sf/openrocket/file/RocketSaver.java index b731c7f75..1ccb0e41b 100644 --- a/core/src/net/sf/openrocket/file/RocketSaver.java +++ b/core/src/net/sf/openrocket/file/RocketSaver.java @@ -3,11 +3,13 @@ package net.sf.openrocket.file; import java.io.IOException; import java.io.OutputStream; +import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.StorageOptions; public abstract class RocketSaver { + protected final WarningSet warnings = new WarningSet(); /** * Save the document to the specified output stream using the default storage options. diff --git a/core/src/net/sf/openrocket/file/rasaero/CustomBooleanAdapter.java b/core/src/net/sf/openrocket/file/rasaero/CustomBooleanAdapter.java new file mode 100644 index 000000000..336f326ed --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/CustomBooleanAdapter.java @@ -0,0 +1,19 @@ +package net.sf.openrocket.file.rasaero; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +public class CustomBooleanAdapter extends XmlAdapter { + + @Override + public Boolean unmarshal(String s) throws Exception { + return "true".equalsIgnoreCase(s); + } + + @Override + public String marshal(Boolean b) throws Exception { + if (b) { + return "True"; + } + return "False"; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/CustomDoubleAdapter.java b/core/src/net/sf/openrocket/file/rasaero/CustomDoubleAdapter.java new file mode 100644 index 000000000..77b600728 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/CustomDoubleAdapter.java @@ -0,0 +1,22 @@ +package net.sf.openrocket.file.rasaero; + +import javax.xml.bind.annotation.adapters.XmlAdapter; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + +public class CustomDoubleAdapter extends XmlAdapter { + @Override + public Double unmarshal(String s) throws Exception { + return Double.parseDouble(s); + } + + @Override + public String marshal(Double aDouble) throws Exception { + if (aDouble == null) { + return null; + } + DecimalFormat df = new DecimalFormat("#.####", new DecimalFormatSymbols(Locale.US)); // RASAero has 4 decimal precision + return df.format(aDouble); + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroCommonConstants.java b/core/src/net/sf/openrocket/file/rasaero/RASAeroCommonConstants.java similarity index 54% rename from core/src/net/sf/openrocket/file/rasaero/importt/RASAeroCommonConstants.java rename to core/src/net/sf/openrocket/file/rasaero/RASAeroCommonConstants.java index 8d69828e8..d42161a64 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroCommonConstants.java +++ b/core/src/net/sf/openrocket/file/rasaero/RASAeroCommonConstants.java @@ -1,25 +1,36 @@ -package net.sf.openrocket.file.rasaero.importt; +package net.sf.openrocket.file.rasaero; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration; import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.FinSet; import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.MathUtil; import java.util.HashMap; import java.util.Map; +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; + /** * List of constants used in RASAero files + helper functions to read parameters from it. * * @author Sibo Van Gool */ public class RASAeroCommonConstants { + // File settings + public static final String FILE_EXTENSION = "CDX1"; + // General settings public static final String RASAERO_DOCUMENT = "RASAeroDocument"; public static final String FILE_VERSION = "FileVersion"; public static final String ROCKET_DESIGN = "RocketDesign"; + // RASAeroDocument settings + public static final String MACH_ALT = "MachAlt"; + + // Base part settings public static final String PART_TYPE = "PartType"; public static final String LENGTH = "Length"; public static final String DIAMETER = "Diameter"; @@ -37,12 +48,24 @@ public class RASAeroCommonConstants { public static final String FIN_CAN = "FinCan"; public static final String BOATTAIL = "BoatTail"; + // Body tube settings + public static final String OVERHANG = "Overhang"; + // Nose cone settings public static final String SHAPE = "Shape"; public static final String POWER_LAW = "PowerLaw"; - //public static final String BLUNT_RADIUS = "BluntRadius"; + public static final String BLUNT_RADIUS = "BluntRadius"; private static final Map RASAeroNoseConeShapeMap = new HashMap<>(); + //// Nose cone shapes + private static final String SHAPE_CONICAL = "Conical"; + private static final String SHAPE_TANGENT_OGIVE = "Tangent Ogive"; + private static final String SHAPE_VON_KARMAN_OGIVE = "Von Karman Ogive"; + private static final String SHAPE_POWER_LAW = "Power Law"; + private static final String SHAPE_LVHAACK = "LV-Haack"; + private static final String SHAPE_PARABOLIC = "Parabolic"; + private static final String SHAPE_ELLIPTICAL = "Elliptical"; + // Transition settings public static final String REAR_DIAMETER = "RearDiameter"; @@ -53,6 +76,10 @@ public class RASAeroCommonConstants { public static final String FIN_SWEEP_DISTANCE = "SweepDistance"; public static final String FIN_TIP_CHORD = "TipChord"; public static final String FIN_THICKNESS = "Thickness"; + public static final String FIN_LE_RADIUS = "LERadius"; + public static final String FIN_AIRFOIL_SECTION = "AirfoilSection"; + public static final String FIN_FX1 = "FX1"; + public static final String FIN_FX3 = "FX3"; public static final String AIRFOIL_SECTION = "AirfoilSection"; //// LERadius, FX1 and FX3 not used public static final String CROSS_SECTION_SQUARE = "Square"; @@ -67,6 +94,9 @@ public class RASAeroCommonConstants { public static final String RAIL_GUIDE_DIAMETER = "RailGuideDiameter"; public static final String RAIL_GUIDE_HEIGHT = "RailGuideHeight"; + // Launch shoe settings + public static final String LAUNCH_SHOE_AREA = "LaunchShoeArea"; + // Fin can settings public static final String INSIDE_DIAMETER = "InsideDiameter"; public static final String SHOULDER_LENGTH = "ShoulderLength"; @@ -82,8 +112,20 @@ public class RASAeroCommonConstants { public static final String FINISH_CAST_IRON = "Cast Iron (Very Rough)"; // Booster settings - public static final String BOAT_TAIL_LENGTH = "BoattailLength"; - public static final String BOAT_TAIL_REAR_DIAMETER = "BoattailRearDiameter"; + public static final String BOATTAIL_LENGTH = "BoattailLength"; + public static final String BOATTAIL_REAR_DIAMETER = "BoattailRearDiameter"; + public static final String BOATTAIL_OFFSET = "BoattailOffset"; + public static final String NOZZLE_EXIT_DIAMETER = "NozzleExitDiameter"; + + // RocketDesign settings + public static final String CD = "CD"; + public static final String MODIFIED_BARROWMAN = "ModifiedBarrowman"; + public static final String TURBULENCE = "Turbulence"; + public static final String SUSTAINER_NOZZLE = "SustainerNozzle"; + public static final String BOOSTER1_NOZZLE = "Booster1Nozzle"; + public static final String BOOSTER2_NOZZLE = "Booster2Nozzle"; + public static final String USE_BOOSTER1 = "UseBooster1"; + public static final String USE_BOOSTER2 = "UseBooster2"; // Launch site settings public static final String LAUNCH_SITE = "LaunchSite"; @@ -126,7 +168,7 @@ public class RASAeroCommonConstants { /** * Length conversion from OpenRocket units to RASAero units. RASAero is in inches, OpenRocket in meters. */ - public static final double OPENROCKET_TO_RASAERO_TO_LENGTH = 39.37; + public static final double OPENROCKET_TO_RASAERO_LENGTH = 39.37; /** * Altitude conversion from OpenRocket units to RASAero units. RASAero is in feet, OpenRocket in meters. */ @@ -154,13 +196,13 @@ public class RASAeroCommonConstants { } static { - RASAeroNoseConeShapeMap.put("Conical", Transition.Shape.CONICAL); - RASAeroNoseConeShapeMap.put("Tangent Ogive", Transition.Shape.OGIVE); // = Ogive with shape parameter = 1 - RASAeroNoseConeShapeMap.put("Von Karman Ogive", Transition.Shape.HAACK); // = Haack series with shape parameter = 0 - RASAeroNoseConeShapeMap.put("Power Law", Transition.Shape.POWER); - RASAeroNoseConeShapeMap.put("LV-Haack", Transition.Shape.HAACK); // = Haack series with shape parameter = 1/3 - RASAeroNoseConeShapeMap.put("Parabolic", Transition.Shape.POWER); // = Power law with shape parameter = 1/2 - RASAeroNoseConeShapeMap.put("Elliptical", Transition.Shape.ELLIPSOID); + RASAeroNoseConeShapeMap.put(SHAPE_CONICAL, Transition.Shape.CONICAL); + RASAeroNoseConeShapeMap.put(SHAPE_TANGENT_OGIVE, Transition.Shape.OGIVE); // = Ogive with shape parameter = 1 + RASAeroNoseConeShapeMap.put(SHAPE_VON_KARMAN_OGIVE, Transition.Shape.HAACK); // = Haack series with shape parameter = 0 + RASAeroNoseConeShapeMap.put(SHAPE_POWER_LAW, Transition.Shape.POWER); + RASAeroNoseConeShapeMap.put(SHAPE_LVHAACK, Transition.Shape.HAACK); // = Haack series with shape parameter = 1/3 + RASAeroNoseConeShapeMap.put(SHAPE_PARABOLIC, Transition.Shape.POWER); // = Power law with shape parameter = 1/2 + RASAeroNoseConeShapeMap.put(SHAPE_ELLIPTICAL, Transition.Shape.ELLIPSOID); } /** @@ -168,10 +210,48 @@ public class RASAeroCommonConstants { * @param shape The RASAero shape string. * @return The OpenRocket nose cone shape. */ - public static Transition.Shape getNoseConeShapeFromRASAero(String shape) { + public static Transition.Shape RASAERO_TO_OPENROCKET_SHAPE(String shape) { return RASAeroNoseConeShapeMap.get(shape); } + /** + * Returns the OpenRocket nose cone shape from the RASAero shape string. + * @param shape The RASAero shape string. + * @return The OpenRocket nose cone shape object. + */ + public static NoseConeShapeSettings OPENROCKET_TO_RASAERO_SHAPE(Transition.Shape shape, double shapeParameter) + throws RASAeroExportException { + if (shape.equals(Transition.Shape.CONICAL)) { + return new NoseConeShapeSettings(SHAPE_CONICAL); + } else if (shape.equals(Transition.Shape.OGIVE) && MathUtil.equals(shapeParameter, 1)) { + return new NoseConeShapeSettings(SHAPE_TANGENT_OGIVE); + } else if (shape.equals(Transition.Shape.HAACK) && MathUtil.equals(shapeParameter, 0)) { + return new NoseConeShapeSettings(SHAPE_VON_KARMAN_OGIVE); + } else if (shape.equals(Transition.Shape.POWER) && MathUtil.equals(shapeParameter, 0.5, 0.01)) { + return new NoseConeShapeSettings(SHAPE_PARABOLIC); + } else if (shape.equals(Transition.Shape.POWER)) { + return new NoseConeShapeSettings(SHAPE_POWER_LAW, shapeParameter); + } else if (shape.equals(Transition.Shape.HAACK) && MathUtil.equals(shapeParameter, 0.33, 0.01)) { + return new NoseConeShapeSettings(SHAPE_LVHAACK); + } else if (shape.equals(Transition.Shape.ELLIPSOID)) { + return new NoseConeShapeSettings(SHAPE_ELLIPTICAL); + } + + // Special cases + else if (shape.equals(Transition.Shape.OGIVE)) { + throw new RASAeroExportException( + String.format("RASAero only supports Ogive nose cones with shape parameter 1, not %.2f", shapeParameter)); + } else if (shape.equals(Transition.Shape.HAACK)) { + throw new RASAeroExportException( + String.format("RASAero only supports Haack nose cones with shape parameter 0 or 0.33, not %.2f", shapeParameter)); + } else if (shape.equals(Transition.Shape.PARABOLIC)) { + throw new RASAeroExportException("RASAero does not support Parabolic nose cones"); + } + + throw new RASAeroExportException( + String.format("Invalid shape and shape parameter combination: %s, %.2f", shape.getName(), shapeParameter)); + } + /** * RASAero has a slightly different way of specifying shapes compared to OpenRocket. For instance * RASAero has an "LV-Haack" shape, which is the same as the OpenRocket "Haack" shape with a shape @@ -180,27 +260,27 @@ public class RASAeroCommonConstants { * @param shape The RASAero shape string. * @return The shape parameter for the OpenRocket nose cone. */ - public static double getNoseConeShapeParameterFromRASAeroShape(String shape) { - if ("Conical".equals(shape)) { + public static double RASAERO_TO_OPENROCKET_SHAPE_PARAMETER(String shape) { + if (SHAPE_CONICAL.equals(shape)) { return 0.0; // Not really needed, but just to be explicit - } else if ("Tangent Ogive".equals(shape)) { + } else if (SHAPE_TANGENT_OGIVE.equals(shape)) { return 1.0; - } else if ("Von Karman Ogive".equals(shape)) { + } else if (SHAPE_VON_KARMAN_OGIVE.equals(shape)) { return 0.0; - } else if ("Power Law".equals(shape)) { + } else if (SHAPE_POWER_LAW.equals(shape)) { return 0.0; // Not really needed, but just to be explicit (will be overwritten later) - } else if ("LV-Haack".equals(shape)) { + } else if (SHAPE_LVHAACK.equals(shape)) { return 0.33; - } else if ("Parabolic".equals(shape)) { + } else if (SHAPE_PARABOLIC.equals(shape)) { return 0.5; - } else if ("Elliptical".equals(shape)) { + } else if (SHAPE_ELLIPTICAL.equals(shape)) { return 0.0; // Not really needed, but just to be explicit } else { return 0.0; } } - public static FinSet.CrossSection getFinCrossSectionFromRASAero(String crossSection, WarningSet warnings) { + public static FinSet.CrossSection RASAERO_TO_OPENROCKET_FIN_CROSSSECTION(String crossSection, WarningSet warnings) { if (CROSS_SECTION_SQUARE.equals(crossSection)) { return FinSet.CrossSection.SQUARE; } else if (CROSS_SECTION_ROUNDED.equals(crossSection)) { @@ -213,7 +293,20 @@ public class RASAeroCommonConstants { } } - public static ExternalComponent.Finish getSurfaceFinishFromRASAero(String surfaceFinish, WarningSet warnings) { + public static String OPENROCKET_TO_RASAERO_FIN_CROSSSECTION(FinSet.CrossSection crossSection) { + if (FinSet.CrossSection.SQUARE.equals(crossSection)) { + return CROSS_SECTION_SQUARE; + } else if (FinSet.CrossSection.ROUNDED.equals(crossSection)) { + return CROSS_SECTION_ROUNDED; + } else if (FinSet.CrossSection.AIRFOIL.equals(crossSection)) { + return CROSS_SECTION_SUBSONIC_NACA; + } else { + //TODO: warnings.add("Unknown fin cross section: " + crossSection + "."); + return null; + } + } + + public static ExternalComponent.Finish RASAERO_TO_OPENROCKET_SURFACE(String surfaceFinish, WarningSet warnings) { // NOTE: the RASAero surface finishes are not really the same as the OpenRocket surface finishes. There are some // approximations here. if (FINISH_SMOOTH.equals(surfaceFinish)) { @@ -238,7 +331,29 @@ public class RASAeroCommonConstants { } } - public static DeploymentConfiguration.DeployEvent getDeployEventFromRASAero(String deployEvent, WarningSet warnings) { + public static String OPENROCKET_TO_RASAERO_SURFACE(ExternalComponent.Finish finish) { + if (finish.equals(ExternalComponent.Finish.MIRROR)) { + return FINISH_SMOOTH; + } else if (finish.equals(ExternalComponent.Finish.FINISHPOLISHED)) { + return FINISH_POLISHED; + } else if (finish.equals(ExternalComponent.Finish.OPTIMUM)) { + return FINISH_SHEET_METAL; + } else if (finish.equals(ExternalComponent.Finish.SMOOTH)) { + return FINISH_CAMOUFLAGE; + } else if (finish.equals(ExternalComponent.Finish.NORMAL)) { + return FINISH_ROUGH_CAMOUFLAGE; + } else if (finish.equals(ExternalComponent.Finish.UNFINISHED)) { + return FINISH_GALVANIZED; + } else if (finish.equals(ExternalComponent.Finish.ROUGHUNFINISHED)) { + return FINISH_CAST_IRON; + } else { + // TODO + //warnings.add("Unknown surface finish: " + finish + ", defaulting to Smooth."); + return FINISH_SMOOTH; + } + } + + public static DeploymentConfiguration.DeployEvent RASAERO_TO_OPENROCKET_DEPLOY_EVENT(String deployEvent, WarningSet warnings) { if (DEPLOYMENT_NONE.equals(deployEvent)) { return DeploymentConfiguration.DeployEvent.NEVER; } else if (DEPLOYMENT_APOGEE.equals(deployEvent)) { @@ -249,4 +364,39 @@ public class RASAeroCommonConstants { warnings.add("Unknown deployment event: " + deployEvent + ", defaulting to apogee."); return DeploymentConfiguration.DeployEvent.APOGEE; } + + public static String OPENROCKET_TO_RASAERO_COLOR(Color color) { + if (color != null) { + if (color.equals(Color.BLACK)) { + return "Black"; // Currently the only officially supported color by RASAero + } + } + return "Blue"; // But we can also apply our own color hehe + } + + /** + * Class containing the RASAero nose cone shape and shape parameter settings + */ + public static class NoseConeShapeSettings { + private final String shape; + private final Double shapeParameter; + + public NoseConeShapeSettings(String shape, double shapeParameter) { + this.shape = shape; + this.shapeParameter = shapeParameter; + } + + public NoseConeShapeSettings(String shape) { + this.shape = shape; + this.shapeParameter = null; + } + + public String getShape() { + return shape; + } + + public Double getShapeParameter() { + return shapeParameter; + } + } } diff --git a/core/src/net/sf/openrocket/file/rasaero/export/BasePartDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/BasePartDTO.java new file mode 100644 index 000000000..42dad3103 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/BasePartDTO.java @@ -0,0 +1,133 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlTransient; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; +import net.sf.openrocket.util.MathUtil; + +/** + * The base class for most RASAero components. + */ +@XmlRootElement +@XmlType(name="RASAeroBasePartDTO") +@XmlAccessorType(XmlAccessType.FIELD) +public class BasePartDTO { + @XmlElement(name = RASAeroCommonConstants.PART_TYPE) + private String partType; + @XmlElement(name = RASAeroCommonConstants.LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double length; + @XmlElement(name = RASAeroCommonConstants.DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double diameter; + @XmlElement(name = RASAeroCommonConstants.LOCATION) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double location; + @XmlElement(name = RASAeroCommonConstants.COLOR) + private String color; + + @XmlTransient + private final RocketComponent component; + + /** + * We need a default no-args constructor. + */ + public BasePartDTO() { + this.component = null; + } + + protected BasePartDTO(RocketComponent component) throws RASAeroExportException { + this.component = component; + + if (component instanceof BodyTube) { + setPartType(RASAeroCommonConstants.BODY_TUBE); + setDiameter(((BodyTube) component).getOuterRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else if (component instanceof NoseCone) { + setPartType(RASAeroCommonConstants.NOSE_CONE); + NoseCone noseCone = (NoseCone) component; + if (noseCone.isFlipped()) { + throw new RASAeroExportException("Nose cone may not be flipped"); + } + setDiameter(((NoseCone) component).getAftRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else if (component instanceof Transition) { + setPartType(RASAeroCommonConstants.TRANSITION); + // This is a bit strange: I would expect diameter to be the fore radius, since you also have a rearDiamter + // field for transitions, but okay + setDiameter(((Transition) component).getAftRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else if (component instanceof AxialStage) { + setPartType(RASAeroCommonConstants.BOOSTER); + AxialStage stage = (AxialStage) component; + if (stage.getChildCount() == 0 || !(stage.getChild(0) instanceof BodyTube)) { + throw new RASAeroExportException("First component of booster must be body tube"); + } + setDiameter(stage.getBoundingRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else { + throw new RASAeroExportException("Unsupported component: " + component.getComponentName()); + } + + setLength(component.getLength() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setLocation(component.getAxialOffset(AxialMethod.ABSOLUTE) * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setColor(RASAeroCommonConstants.OPENROCKET_TO_RASAERO_COLOR(component.getColor())); + } + + public String getPartType() { + return partType; + } + + public void setPartType(String partType) { + this.partType = partType; + } + + public Double getLength() { + return length; + } + + public void setLength(Double length) throws RASAeroExportException { + if (MathUtil.equals(length, 0)) { + throw new RASAeroExportException(String.format("Length of '%s' must be greater than 0", component.getName())); + } + this.length = length; + } + + public Double getDiameter() { + return diameter; + } + + public void setDiameter(Double diameter) throws RASAeroExportException { + if (MathUtil.equals(diameter, 0)) { + throw new RASAeroExportException(String.format("Diameter of '%s' must be greater than 0", component.getName())); + } + this.diameter = diameter; + } + + public Double getLocation() { + return location; + } + + public void setLocation(Double location) { + this.location = location; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTO.java new file mode 100644 index 000000000..a12f7f42b --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTO.java @@ -0,0 +1,156 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.BodyTube; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; +import net.sf.openrocket.util.MathUtil; + +@XmlRootElement(name = RASAeroCommonConstants.BODY_TUBE) +@XmlAccessorType(XmlAccessType.FIELD) +public class BodyTubeDTO extends BasePartDTO implements BodyTubeDTOAdapter { + @XmlElement(name = RASAeroCommonConstants.LAUNCH_LUG_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchLugDiameter = 0d; + @XmlElement(name = RASAeroCommonConstants.LAUNCH_LUG_LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchLugLength = 0d; + @XmlElement(name = RASAeroCommonConstants.RAIL_GUIDE_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double railGuideDiameter = 0d; + @XmlElement(name = RASAeroCommonConstants.RAIL_GUIDE_HEIGHT) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double railGuideHeight = 0d; + @XmlElement(name = RASAeroCommonConstants.LAUNCH_SHOE_AREA) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchShoeArea = 0d; // Currently not available in OR + + @XmlElement(name = RASAeroCommonConstants.BOATTAIL_LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double boattailLength = 0d; + @XmlElement(name = RASAeroCommonConstants.BOATTAIL_REAR_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double boattailRearDiameter = 0d; + @XmlElement(name = RASAeroCommonConstants.BOATTAIL_OFFSET) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double boattailOffset = 0d; + @XmlElement(name = RASAeroCommonConstants.OVERHANG) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double overhang = 0d; + + @XmlElementRef(name = RASAeroCommonConstants.FIN, type = FinDTO.class) + private FinDTO fin; + + /** + * We need a default no-args constructor. + */ + public BodyTubeDTO() { } + + public BodyTubeDTO(BodyTube bodyTube) throws RASAeroExportException { + super(bodyTube); + applyBodyTubeSettings(bodyTube); + } + + public Double getLaunchLugDiameter() { + return launchLugDiameter; + } + + public void setLaunchLugDiameter(Double launchLugDiameter) throws RASAeroExportException { + if (MathUtil.equals(launchLugDiameter, 0)) { + throw new RASAeroExportException("Launch lug diameter can not be 0"); + } + this.launchLugDiameter = launchLugDiameter; + } + + public Double getLaunchLugLength() { + return launchLugLength; + } + + public void setLaunchLugLength(Double launchLugLength) throws RASAeroExportException { + if (MathUtil.equals(launchLugLength, 0)) { + throw new RASAeroExportException("Launch lug length can not be 0"); + } + this.launchLugLength = launchLugLength; + } + + public Double getRailGuideDiameter() { + return railGuideDiameter; + } + + public void setRailGuideDiameter(Double railGuideDiameter) throws RASAeroExportException { + if (MathUtil.equals(railGuideDiameter, 0)) { + throw new RASAeroExportException("Rail button diameter can not be 0"); + } + this.railGuideDiameter = railGuideDiameter; + } + + public Double getRailGuideHeight() { + return railGuideHeight; + } + + public void setRailGuideHeight(Double railGuideHeight) throws RASAeroExportException { + if (MathUtil.equals(railGuideHeight, 0)) { + throw new RASAeroExportException("Rail button height can not be 0"); + } + this.railGuideHeight = railGuideHeight; + } + + public Double getLaunchShoeArea() { + return launchShoeArea; + } + + public void setLaunchShoeArea(Double launchShoeArea) throws RASAeroExportException { + if (MathUtil.equals(launchShoeArea, 0)) { + throw new RASAeroExportException("Launch shoe area can not be 0"); + } + this.launchShoeArea = launchShoeArea; + } + + public Double getBoattailLength() { + return boattailLength; + } + + public void setBoattailLength(Double boattailLength) { + this.boattailLength = boattailLength; + } + + public Double getBoattailRearDiameter() { + return boattailRearDiameter; + } + + public void setBoattailRearDiameter(Double boattailRearDiameter) { + this.boattailRearDiameter = boattailRearDiameter; + } + + public Double getBoattailOffset() { + return boattailOffset; + } + + public void setBoattailOffset(Double boattailOffset) { + this.boattailOffset = boattailOffset; + } + + public Double getOverhang() { + return overhang; + } + + public void setOverhang(Double overhang) { + this.overhang = overhang; + } + + public FinDTO getFin() { + return fin; + } + + public void setFin(FinDTO fin) { + this.fin = fin; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTOAdapter.java b/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTOAdapter.java new file mode 100644 index 000000000..3c7d84e41 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/BodyTubeDTOAdapter.java @@ -0,0 +1,90 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.LaunchLug; +import net.sf.openrocket.rocketcomponent.RailButton; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.util.MathUtil; +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; + +public interface BodyTubeDTOAdapter { + default void applyBodyTubeSettings(BodyTube bodyTube) throws RASAeroExportException { + for (RocketComponent child : bodyTube.getChildren()) { + if (child instanceof TrapezoidFinSet) { + setFin(new FinDTO((TrapezoidFinSet) child)); + } else if (child instanceof LaunchLug) { + if (!MathUtil.equals(getRailGuideDiameter(), 0) || !MathUtil.equals(getRailGuideHeight(), 0)) { // only one check on diameter or length should be sufficient, but just to be safe + //TODO: warning.add(String.format("Already added a rail button, ignoring launch lug '%s'", child.getName()); + continue; + } + if (!MathUtil.equals(getLaunchShoeArea(), 0)) { + //TODO: warning.add(String.format("Already added a launch shoe, ignoring launch lug '%s'", child.getName()); + continue; + } + + LaunchLug lug = (LaunchLug) child; + setLaunchLugDiameter(lug.getOuterRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + if (lug.getInstanceCount() == 2) { + setLaunchLugLength(lug.getLength() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else { + //TODO:warnings.add(String.format("Instance count of '%s' not set to 2, defaulting to 2 and adjusting + // launch lug length accordingly.", lug.getName()") + setLaunchLugLength(lug.getLength() * lug.getInstanceCount() / 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } + } else if (child instanceof RailButton) { + if (!MathUtil.equals(getLaunchLugDiameter(), 0) || !MathUtil.equals(getLaunchLugLength(), 0)) { // only one check on diameter or length should be sufficient, but just to be safe + // TODO: warning.add(String.format("Already added a launch lug, ignoring rail button '%s'", child.getName())); + continue; + } + if (!MathUtil.equals(getLaunchShoeArea(), 0)) { + // TODO: warning.add(String.format("Already added a launch shoe, ignoring rail button '%s'", child.getName())); + continue; + } + + RailButton button = (RailButton) child; + setRailGuideDiameter(button.getOuterDiameter() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setRailGuideHeight(button.getTotalHeight() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + + if (button.getInstanceCount() != 2) { + //TODO: warnings.add(String.format("Instance count of '%s' equals %d, defaulting to 2", button.getName(), button.getInstanceCount())); + } + } else { + // TODO: warnings.add(String.format("Unsupported component '%s', ignoring.", child.getComponentName())); + } + } + } + + Double getLaunchLugDiameter(); + + void setLaunchLugDiameter(Double launchLugDiameter) throws RASAeroExportException; + + Double getLaunchLugLength(); + + void setLaunchLugLength(Double launchLugLength) throws RASAeroExportException; + + Double getRailGuideDiameter(); + + void setRailGuideDiameter(Double railGuideDiameter) throws RASAeroExportException; + + Double getRailGuideHeight(); + + void setRailGuideHeight(Double railGuideHeight) throws RASAeroExportException; + + Double getLaunchShoeArea(); + + void setLaunchShoeArea(Double launchShoeArea) throws RASAeroExportException; + + Double getBoattailLength(); + + void setBoattailLength(Double boattailLength); + + Double getBoattailRearDiameter(); + + void setBoattailRearDiameter(Double boattailRearDiameter); + + FinDTO getFin(); + + void setFin(FinDTO fin); +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/BoosterDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/BoosterDTO.java new file mode 100644 index 000000000..b606c4e76 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/BoosterDTO.java @@ -0,0 +1,285 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; +import net.sf.openrocket.util.MathUtil; + +@XmlRootElement(name = RASAeroCommonConstants.BOOSTER) +@XmlAccessorType(XmlAccessType.FIELD) +public class BoosterDTO implements BodyTubeDTOAdapter { + + @XmlElement(name = RASAeroCommonConstants.PART_TYPE) + private String partType; + @XmlElement(name = RASAeroCommonConstants.LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double length; + @XmlElement(name = RASAeroCommonConstants.DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double diameter; + @XmlElement(name = RASAeroCommonConstants.INSIDE_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double insideDiameter; + @XmlElement(name = RASAeroCommonConstants.LAUNCH_LUG_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchLugDiameter = 0d; + @XmlElement(name = RASAeroCommonConstants.LAUNCH_LUG_LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchLugLength = 0d; + @XmlElement(name = RASAeroCommonConstants.RAIL_GUIDE_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double railGuideDiameter = 0d; + @XmlElement(name = RASAeroCommonConstants.RAIL_GUIDE_HEIGHT) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double railGuideHeight = 0d; + @XmlElement(name = RASAeroCommonConstants.LAUNCH_SHOE_AREA) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double launchShoeArea = 0d; // Currently not available in OR + @XmlElement(name = RASAeroCommonConstants.LOCATION) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double location; + @XmlElement(name = RASAeroCommonConstants.COLOR) + private String color; + @XmlElement(name = RASAeroCommonConstants.SHOULDER_LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double shoulderLength; + @XmlElement(name = RASAeroCommonConstants.NOZZLE_EXIT_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double nozzleExitDiameter; + @XmlElement(name = RASAeroCommonConstants.BOATTAIL_LENGTH) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double boattailLength; + @XmlElement(name = RASAeroCommonConstants.BOATTAIL_REAR_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double boattailRearDiameter; + + @XmlElementRef(name = RASAeroCommonConstants.FIN, type = FinDTO.class) + private FinDTO fin; + + + /** + * We need a default, no-args constructor. + */ + public BoosterDTO() { } + + protected BoosterDTO(Rocket rocket, AxialStage stage) throws RASAeroExportException { + int stageNr = rocket.getChildPosition(stage); // Use this instead of stage.getStageNumber() in case there are parallel stages in the design + if (stageNr != 1 && stageNr != 2) { + throw new RASAeroExportException(String.format("Invalid stage number '%d' for booster stage '%s'", stageNr, stage.getName())); + } + + if (stage.getChildCount() == 0) { + throw new RASAeroExportException(String.format("Stage '%s' can not be empty", stage.getName())); + } + + RocketComponent firstChild = stage.getChild(0); + if (!(firstChild instanceof BodyTube) && + !(firstChild instanceof Transition && !(firstChild instanceof NoseCone))) { + throw new RASAeroExportException(String.format("First component of stage '%s' must be a body tube or transition", stage.getName())); + } + final BodyTube firstTube; + if (firstChild instanceof Transition) { + if (stage.getChildCount() == 1 || !(stage.getChild(1) instanceof BodyTube)) { + throw new RASAeroExportException( + String.format("When the first component of stage '%s' is a transition, the second one must be a body tube", + stage.getName())); + } + + Transition transition = (Transition) firstChild; + SymmetricComponent previousComponent = transition.getPreviousSymmetricComponent(); + if (previousComponent == null) { + throw new RASAeroExportException(String.format("No previous component for '%s' in stage '%s'", + firstChild.getName(), stage.getName())); + } + + if (!MathUtil.equals(transition.getForeRadius(), previousComponent.getAftRadius())) { + throw new RASAeroExportException( + String.format("Transition '%s' in stage '%s' must have the same fore radius as the aft radius of its previous component '%s'", + transition.getName(), stage.getName(), previousComponent.getName())); + } + + firstTube = (BodyTube) stage.getChild(1); + if (!MathUtil.equals(firstTube.getOuterRadius(), transition.getAftRadius())) { + throw new RASAeroExportException( + String.format("Radius of '%s' in stage '%s' must be the same as the aft radius of '%s", + firstTube.getName(), stage.getName(), transition.getName())); + } + + setShoulderLength(firstChild.getLength() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setDiameter(firstTube.getOuterRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setInsideDiameter(transition.getForeRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } else { + firstTube = (BodyTube) stage.getChild(0); + } + + applyBodyTubeSettings(firstTube); + + TrapezoidFinSet finSet = getFinSetFromBodyTube(firstTube); + if (finSet == null) { + throw new RASAeroExportException( + String.format("Body tube '%s' in stage '%s' must have a TrapezoidFinSet", + firstTube.getName(), stage.getName())); + } + setFin(new FinDTO(finSet)); + + setPartType(RASAeroCommonConstants.BOOSTER); + setLength(firstTube.getLength() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setDiameter(firstTube.getOuterRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setLocation(firstChild.getAxialOffset(AxialMethod.ABSOLUTE) * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setColor(RASAeroCommonConstants.OPENROCKET_TO_RASAERO_COLOR(firstTube.getColor())); + } + + private TrapezoidFinSet getFinSetFromBodyTube(BodyTube bodyTube) { + for (RocketComponent child : bodyTube.getChildren()) { + if (child instanceof TrapezoidFinSet) { + return (TrapezoidFinSet) child; + } + } + return null; + } + + public String getPartType() { + return partType; + } + + public void setPartType(String partType) { + this.partType = partType; + } + + public Double getLength() { + return length; + } + + public void setLength(Double length) { + this.length = length; + } + + public Double getDiameter() { + return diameter; + } + + public void setDiameter(Double diameter) { + this.diameter = diameter; + } + + public Double getInsideDiameter() { + return insideDiameter; + } + + public void setInsideDiameter(Double insideDiameter) { + this.insideDiameter = insideDiameter; + } + + public Double getLaunchLugDiameter() { + return launchLugDiameter; + } + + public void setLaunchLugDiameter(Double launchLugDiameter) { + this.launchLugDiameter = launchLugDiameter; + } + + public Double getLaunchLugLength() { + return launchLugLength; + } + + public void setLaunchLugLength(Double launchLugLength) { + this.launchLugLength = launchLugLength; + } + + public Double getRailGuideDiameter() { + return railGuideDiameter; + } + + public void setRailGuideDiameter(Double railGuideDiameter) { + this.railGuideDiameter = railGuideDiameter; + } + + public Double getRailGuideHeight() { + return railGuideHeight; + } + + public void setRailGuideHeight(Double railGuideHeight) { + this.railGuideHeight = railGuideHeight; + } + + public Double getLaunchShoeArea() { + return launchShoeArea; + } + + public void setLaunchShoeArea(Double launchShoeArea) { + this.launchShoeArea = launchShoeArea; + } + + public Double getLocation() { + return location; + } + + public void setLocation(Double location) { + this.location = location; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public Double getShoulderLength() { + return shoulderLength; + } + + public void setShoulderLength(Double shoulderLength) { + this.shoulderLength = shoulderLength; + } + + public Double getNozzleExitDiameter() { + return nozzleExitDiameter; + } + + public void setNozzleExitDiameter(Double nozzleExitDiameter) { + this.nozzleExitDiameter = nozzleExitDiameter; + } + + public Double getBoattailLength() { + return boattailLength; + } + + public void setBoattailLength(Double boattailLength) { + this.boattailLength = boattailLength; + } + + public Double getBoattailRearDiameter() { + return boattailRearDiameter; + } + + public void setBoattailRearDiameter(Double boattailRearDiameter) { + this.boattailRearDiameter = boattailRearDiameter; + } + + public FinDTO getFin() { + return fin; + } + + public void setFin(FinDTO fin) { + this.fin = fin; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/FinDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/FinDTO.java new file mode 100644 index 000000000..947f4a1e2 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/FinDTO.java @@ -0,0 +1,160 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.TrapezoidFinSet; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +@XmlRootElement(name = RASAeroCommonConstants.FIN) +@XmlAccessorType(XmlAccessType.FIELD) +public class FinDTO { + @XmlElement(name = RASAeroCommonConstants.FIN_COUNT) + private int count; + @XmlElement(name = RASAeroCommonConstants.FIN_CHORD) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double chord; + @XmlElement(name = RASAeroCommonConstants.FIN_SPAN) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double span; + @XmlElement(name = RASAeroCommonConstants.FIN_SWEEP_DISTANCE) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double sweepDistance; + @XmlElement(name = RASAeroCommonConstants.FIN_TIP_CHORD) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double tipChord; + @XmlElement(name = RASAeroCommonConstants.FIN_THICKNESS) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double thickness; + @XmlElement(name = RASAeroCommonConstants.FIN_LE_RADIUS) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double LERadius = 0d; // Leading edge radius + @XmlElement(name = RASAeroCommonConstants.LOCATION) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double location; + @XmlElement(name = RASAeroCommonConstants.AIRFOIL_SECTION) + private String airfoilSection; + @XmlElement(name = RASAeroCommonConstants.FIN_FX1) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double FX1 = 0d; + @XmlElement(name = RASAeroCommonConstants.FIN_FX3) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double FX3 = 0d; + + /** + * We need a default no-args constructor. + */ + public FinDTO() { + } + + public FinDTO(TrapezoidFinSet fin) throws RASAeroExportException { + int finCount = fin.getFinCount(); + if (finCount < 3 || finCount > 8) { + throw new RASAeroExportException( + String.format("Fin set '%s' must have a fin count between 3 and 8", fin.getName())); + } + + setCount(fin.getFinCount()); + setChord(fin.getRootChord() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setTipChord(fin.getTipChord() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setSpan(fin.getSpan() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setSweepDistance(fin.getSweep() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setThickness(fin.getThickness() * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + setAirfoilSection(RASAeroCommonConstants.OPENROCKET_TO_RASAERO_FIN_CROSSSECTION(fin.getCrossSection())); + setLocation((-fin.getAxialOffset(AxialMethod.BOTTOM) + fin.getLength()) * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public Double getChord() { + return chord; + } + + public void setChord(Double chord) { + this.chord = chord; + } + + public Double getSpan() { + return span; + } + + public void setSpan(Double span) { + this.span = span; + } + + public Double getSweepDistance() { + return sweepDistance; + } + + public void setSweepDistance(Double sweepDistance) { + this.sweepDistance = sweepDistance; + } + + public Double getTipChord() { + return tipChord; + } + + public void setTipChord(Double tipChord) { + this.tipChord = tipChord; + } + + public Double getThickness() { + return thickness; + } + + public void setThickness(Double thickness) { + this.thickness = thickness; + } + + public Double getLERadius() { + return LERadius; + } + + public void setLERadius(Double LERadius) { + this.LERadius = LERadius; + } + + public Double getLocation() { + return location; + } + + public void setLocation(Double location) { + this.location = location; + } + + public String getAirfoilSection() { + return airfoilSection; + } + + public void setAirfoilSection(String airfoilSection) { + this.airfoilSection = airfoilSection; + } + + public Double getFX1() { + return FX1; + } + + public void setFX1(Double FX1) { + this.FX1 = FX1; + } + + public Double getFX3() { + return FX3; + } + + public void setFX3(Double FX3) { + this.FX3 = FX3; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/LaunchSiteDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/LaunchSiteDTO.java new file mode 100644 index 000000000..7c8fc83fe --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/LaunchSiteDTO.java @@ -0,0 +1,4 @@ +package net.sf.openrocket.file.rasaero.export; + +public class LaunchSiteDTO { +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/NoseConeDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/NoseConeDTO.java new file mode 100644 index 000000000..c1fae0c47 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/NoseConeDTO.java @@ -0,0 +1,70 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.NoseCone; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants.NoseConeShapeSettings; + +@XmlRootElement(name = RASAeroCommonConstants.NOSE_CONE) +@XmlAccessorType(XmlAccessType.FIELD) +public class NoseConeDTO extends BasePartDTO { + + @XmlElement(name = RASAeroCommonConstants.SHAPE) + private String shape; + @XmlElement(name = RASAeroCommonConstants.BLUNT_RADIUS) + private double bluntRadius = 0; + @XmlElement(name = RASAeroCommonConstants.POWER_LAW) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double powerLaw; + + /** + * We need a default no-args constructor. + */ + public NoseConeDTO() { + } + + public NoseConeDTO(NoseCone noseCone) throws RASAeroExportException { + super(noseCone); + + NoseConeShapeSettings shapeSettings = + RASAeroCommonConstants.OPENROCKET_TO_RASAERO_SHAPE(noseCone.getShapeType(), noseCone.getShapeParameter()); + + setShape(shapeSettings.getShape()); + Double shapeParameter = shapeSettings.getShapeParameter(); + if (shapeParameter != null) { + setPowerLaw(shapeParameter); + } + } + + public String getShape() { + return shape; + } + + public void setShape(String shape) { + this.shape = shape; + } + + public Double getPowerLaw() { + return powerLaw; + } + + public void setPowerLaw(Double powerLaw) { + this.powerLaw = powerLaw; + } + + public double getBluntRadius() { + return bluntRadius; + } + + public void setBluntRadius(double bluntRadius) { + this.bluntRadius = bluntRadius; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/RASAeroDocumentDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/RASAeroDocumentDTO.java new file mode 100644 index 000000000..fc5ac0051 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/RASAeroDocumentDTO.java @@ -0,0 +1,87 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * The top level RASAero document. + */ +@XmlRootElement(name = RASAeroCommonConstants.RASAERO_DOCUMENT) +@XmlAccessorType(XmlAccessType.FIELD) +public class RASAeroDocumentDTO { + @XmlElement(name = RASAeroCommonConstants.FILE_VERSION) + private final String version = "2"; + + @XmlElement(name = RASAeroCommonConstants.ROCKET_DESIGN) + private RocketDesignDTO design; + + @XmlElement(name = RASAeroCommonConstants.LAUNCH_SITE) + private LaunchSiteDTO launchSite; + + @XmlElement(name = RASAeroCommonConstants.RECOVERY) + private RecoveryDTO recovery; + + + @XmlElement(name = RASAeroCommonConstants.MACH_ALT) + private String machAlt = ""; // Currently not implemented + + /* + @XmlElementWrapper(name=RASAeroCommonConstants.SIMULATION_LIST) + @XmlElement(name=RASAeroCommonConstants.SIMULATION) + */ + @XmlElement(name = RASAeroCommonConstants.SIMULATION_LIST) + private SimulationListDTO simulationList = null; + + /** + * Get the subordinate design DTO. + * + * @return the RocketDesignDTO + */ + public RocketDesignDTO getDesign() { + return design; + } + + public void setDesign(RocketDesignDTO theDesign) { + this.design = theDesign; + } + + public LaunchSiteDTO getLaunchSite() { + return launchSite; + } + + public void setLaunchSite(LaunchSiteDTO launchSite) { + this.launchSite = launchSite; + } + + public RecoveryDTO getRecovery() { + return recovery; + } + + public void setRecovery(RecoveryDTO recovery) { + this.recovery = recovery; + } + + public SimulationListDTO getSimulationList() { + return simulationList; + } + + public void setSimulationList(SimulationListDTO simulationList) { + this.simulationList = simulationList; + } + + public String getMachAlt() { + return this.machAlt; + } + + public void setMachAlt(String machAlt) { + this.machAlt = machAlt; + } + + public String getVersion() { + return version; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/RASAeroSaver.java b/core/src/net/sf/openrocket/file/rasaero/export/RASAeroSaver.java new file mode 100644 index 000000000..45a717611 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/RASAeroSaver.java @@ -0,0 +1,93 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.document.StorageOptions; +import net.sf.openrocket.file.RocketSaver; +import net.sf.openrocket.rocketcomponent.Rocket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +public class RASAeroSaver extends RocketSaver { + /** + * The logger. + */ + private static final Logger log = LoggerFactory.getLogger(RASAeroSaver.class); + + public static class RASAeroExportException extends Exception { + public RASAeroExportException(String errorMessage) { + super(errorMessage); + } + } + + /** + * This method marshals an OpenRocketDocument (OR design) to RASAero-compliant XML. + * + * @param doc the OR design + * @return RASAero-compliant XML + */ + public String marshalToRASAero(OpenRocketDocument doc) { + try { + JAXBContext binder = JAXBContext.newInstance(RASAeroDocumentDTO.class); + Marshaller marshaller = binder.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + StringWriter sw = new StringWriter(); + + marshaller.marshal(toRASAeroDocumentDTO(doc), sw); + return sw.toString(); + } catch (RASAeroExportException e) { + throw new RuntimeException(e); + } catch (Exception e) { + log.error("Could not marshall a design to RASAero format. " + e.getMessage()); + } + + return null; + } + + @Override + public void save(OutputStream dest, OpenRocketDocument doc, StorageOptions options) throws IOException { + log.info("Saving .CDX1 file"); + + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(dest, StandardCharsets.UTF_8)); + writer.write(marshalToRASAero(doc)); + writer.flush(); + } + + @Override + public long estimateFileSize(OpenRocketDocument doc, StorageOptions options) { + return marshalToRASAero(doc).length(); + } + + /** + * Root conversion method. It iterates over all subcomponents. + * + * @param doc the OR design + * @return a corresponding RASAero representation + */ + private RASAeroDocumentDTO toRASAeroDocumentDTO(OpenRocketDocument doc) throws RASAeroExportException { + RASAeroDocumentDTO rad = new RASAeroDocumentDTO(); + rad.setDesign(toRocketDesignDTO(doc.getRocket())); + + return rad; + } + + /** + * Create the RASAero rocket design (containing all the actual rocket components). + * @param rocket the OR rocket to export the components from + * @return the RASAero rocket design + */ + private RocketDesignDTO toRocketDesignDTO(Rocket rocket) throws RASAeroExportException { + RocketDesignDTO result = new RocketDesignDTO(rocket); + return result; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/RecoveryDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/RecoveryDTO.java new file mode 100644 index 000000000..8cbb8233f --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/RecoveryDTO.java @@ -0,0 +1,4 @@ +package net.sf.openrocket.file.rasaero.export; + +public class RecoveryDTO { +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/RocketDesignDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/RocketDesignDTO.java new file mode 100644 index 000000000..4453018e9 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/RocketDesignDTO.java @@ -0,0 +1,202 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomBooleanAdapter; +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.util.ArrayList; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.List; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; + +@XmlAccessorType(XmlAccessType.FIELD) +public class RocketDesignDTO { + @XmlElementRefs({ + @XmlElementRef(name = RASAeroCommonConstants.BODY_TUBE, type = BodyTubeDTO.class), + @XmlElementRef(name = RASAeroCommonConstants.NOSE_CONE, type = NoseConeDTO.class), + @XmlElementRef(name = RASAeroCommonConstants.TRANSITION, type = TransitionDTO.class), + @XmlElementRef(name = RASAeroCommonConstants.BOOSTER, type = BoosterDTO.class) + }) + private final List externalPart = new ArrayList<>(); + + @XmlElementRefs({ + @XmlElementRef(name = RASAeroCommonConstants.BOOSTER, type = BoosterDTO.class), + }) + private final List boosters = new ArrayList<>(); + + @XmlElement(name = RASAeroCommonConstants.SURFACE_FINISH) + private String surface = RASAeroCommonConstants.FINISH_SMOOTH; + @XmlElement(name = RASAeroCommonConstants.CD) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double CD = 0d; + @XmlElement(name = RASAeroCommonConstants.MODIFIED_BARROWMAN) + @XmlJavaTypeAdapter(CustomBooleanAdapter.class) + private Boolean modifiedBarrowman = false; + @XmlElement(name = RASAeroCommonConstants.TURBULENCE) + @XmlJavaTypeAdapter(CustomBooleanAdapter.class) + private Boolean turbulence = false; + @XmlElement(name = RASAeroCommonConstants.SUSTAINER_NOZZLE) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double sustainerNozzle = 0d; + @XmlElement(name = RASAeroCommonConstants.BOOSTER1_NOZZLE) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double booster1Nozzle = 0d; + @XmlElement(name = RASAeroCommonConstants.BOOSTER2_NOZZLE) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double booster2Nozzle = 0d; + @XmlElement(name = RASAeroCommonConstants.USE_BOOSTER1) + @XmlJavaTypeAdapter(CustomBooleanAdapter.class) + private Boolean useBooster1 = false; + @XmlElement(name = RASAeroCommonConstants.USE_BOOSTER2) + @XmlJavaTypeAdapter(CustomBooleanAdapter.class) + private Boolean useBooster2 = false; + @XmlElement(name = RASAeroCommonConstants.COMMENTS) + private String comments = ""; + + public RocketDesignDTO(Rocket rocket) throws RASAeroExportException { + setComments(rocket.getComment()); + if (rocket.getChildCount() > 3) { + throw new RASAeroExportException("Rocket should have no more then 3 stages (excl. boosters) in total"); + } + setUseBooster1(rocket.getStageCount() >= 2); + setUseBooster2(rocket.getStageCount() == 3); + + AxialStage sustainer = rocket.getStage(0); + + // Export components from sustainer + for (int i = 0; i < sustainer.getChildCount(); i++) { + RocketComponent component = sustainer.getChild(i); + if (i == 0 && !(component instanceof NoseCone)) { + throw new RASAeroExportException("First component of the sustainer must be a nose cone"); + } else if (i == 1 && !(component instanceof BodyTube)) { + throw new RASAeroExportException("Second component of the sustainer must be a body tube"); + } + if (component instanceof BodyTube) { + addExternalPart(new BodyTubeDTO((BodyTube) component)); + } else if (component instanceof NoseCone) { + if (i != 0) { + throw new RASAeroExportException("A nose cone can only be the first component of the rocket"); + } + addExternalPart(new NoseConeDTO((NoseCone) component)); + // Set the global surface finish to that of the first nose cone + setSurface(RASAeroCommonConstants.OPENROCKET_TO_RASAERO_SURFACE(((NoseCone) component).getFinish())); + } + else if (component instanceof Transition) { + addExternalPart(new TransitionDTO((Transition) component)); + } + } + + // Export components from other stages + for (int i = 1; i < rocket.getChildCount(); i++) { + addBooster(new BoosterDTO(rocket, (AxialStage) rocket.getChild(i))); + } + } + + public String getSurface() { + return surface; + } + + public void setSurface(String surface) { + this.surface = surface; + } + + public double getCD() { + return CD; + } + + public void setCD(double CD) { + this.CD = CD; + } + + public boolean isModifiedBarrowman() { + return modifiedBarrowman; + } + + public void setModifiedBarrowman(boolean modifiedBarrowman) { + this.modifiedBarrowman = modifiedBarrowman; + } + + public Boolean isTurbulence() { + return turbulence; + } + + public void setTurbulence(Boolean turbulence) { + this.turbulence = turbulence; + } + + public Double getSustainerNozzle() { + return sustainerNozzle; + } + + public void setSustainerNozzle(Double sustainerNozzle) { + this.sustainerNozzle = sustainerNozzle; + } + + public Double getBooster1Nozzle() { + return booster1Nozzle; + } + + public void setBooster1Nozzle(Double booster1Nozzle) { + this.booster1Nozzle = booster1Nozzle; + } + + public Double getBooster2Nozzle() { + return booster2Nozzle; + } + + public void setBooster2Nozzle(Double booster2Nozzle) { + this.booster2Nozzle = booster2Nozzle; + } + + public Boolean isUseBooster1() { + return useBooster1; + } + + public void setUseBooster1(Boolean useBooster1) { + this.useBooster1 = useBooster1; + } + + public Boolean isUseBooster2() { + return useBooster2; + } + + public void setUseBooster2(Boolean useBooster2) { + this.useBooster2 = useBooster2; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + + public List getExternalPart() { + return externalPart; + } + + public void addExternalPart(BasePartDTO theExternalPartDTO) { + externalPart.add(theExternalPartDTO); + } + + public List getBoosters() { + return boosters; + } + + public void addBooster(BoosterDTO boosterDTO) { + boosters.add(boosterDTO); + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/SimulationListDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/SimulationListDTO.java new file mode 100644 index 000000000..a3af9c971 --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/SimulationListDTO.java @@ -0,0 +1,4 @@ +package net.sf.openrocket.file.rasaero.export; + +public class SimulationListDTO { +} diff --git a/core/src/net/sf/openrocket/file/rasaero/export/TransitionDTO.java b/core/src/net/sf/openrocket/file/rasaero/export/TransitionDTO.java new file mode 100644 index 000000000..6259876cb --- /dev/null +++ b/core/src/net/sf/openrocket/file/rasaero/export/TransitionDTO.java @@ -0,0 +1,61 @@ +package net.sf.openrocket.file.rasaero.export; + +import net.sf.openrocket.file.rasaero.CustomDoubleAdapter; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; +import net.sf.openrocket.rocketcomponent.RocketComponent; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import net.sf.openrocket.file.rasaero.export.RASAeroSaver.RASAeroExportException; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.util.MathUtil; + +import java.util.Objects; + +@XmlRootElement(name = RASAeroCommonConstants.TRANSITION) +@XmlAccessorType(XmlAccessType.FIELD) +public class TransitionDTO extends BasePartDTO { + + @XmlElement(name = RASAeroCommonConstants.REAR_DIAMETER) + @XmlJavaTypeAdapter(CustomDoubleAdapter.class) + private Double rearDiameter; + + /** + * We need a default no-args constructor. + */ + public TransitionDTO() { + } + + public TransitionDTO(Transition transition) throws RASAeroExportException { + super(transition); + + if (!transition.getShapeType().equals(Transition.Shape.CONICAL)) { + throw new RASAeroExportException("RASAero only supports conical transitions"); + } + + SymmetricComponent previousComp = transition.getPreviousSymmetricComponent(); + if (previousComp == null) { + throw new RASAeroExportException(String.format("Transition '%s' has no previous component", transition.getName())); + } + if (!MathUtil.equals(transition.getForeRadius(), previousComp.getAftRadius())) { + throw new RASAeroExportException( + String.format("Transition '%s' should have the same fore radius as the aft radius (%f) of its previous component, not (%f)", + transition.getName(), previousComp.getAftRadius(), transition.getForeRadius())); + } + + setRearDiameter(transition.getAftRadius() * 2 * RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + } + + public Double getRearDiameter() { + return rearDiameter; + } + + public void setRearDiameter(Double rearDiameter) { + this.rearDiameter = rearDiameter; + } +} diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/BaseHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/BaseHandler.java index cde34acdb..c09f69003 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/BaseHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/BaseHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; import org.xml.sax.SAXException; diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/BoattailHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/BoattailHandler.java index 72f1ed3a9..60c688dde 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/BoattailHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/BoattailHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.PodSet; diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/BodyTubeHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/BodyTubeHandler.java index 44ea54ac0..b5f080418 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/BodyTubeHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/BodyTubeHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.BodyTube; @@ -75,8 +76,8 @@ public class BodyTubeHandler extends BaseHandler { @Override public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { super.endHandler(element, attributes, content, warnings); - this.bodyTube.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); - this.bodyTube.setOuterRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); // Not really useful, but included for completeness + this.bodyTube.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + this.bodyTube.setOuterRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); // Not really useful, but included for completeness this.bodyTube.setOuterRadiusAutomatic(true); this.bodyTube.setThickness(0.002); // Arbitrary value; RASAero doesn't specify this diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/BoosterHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/BoosterHandler.java index 9b27a7c55..b8cb625d2 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/BoosterHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/BoosterHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.AxialStage; @@ -40,7 +41,7 @@ public class BoosterHandler extends BodyTubeHandler { @Override public ElementHandler openElement(String element, HashMap attributes, WarningSet warnings) throws SAXException { - if (RASAeroCommonConstants.BOAT_TAIL_LENGTH.equals(element) || RASAeroCommonConstants.BOAT_TAIL_REAR_DIAMETER.equals(element) + if (RASAeroCommonConstants.BOATTAIL_LENGTH.equals(element) || RASAeroCommonConstants.BOATTAIL_REAR_DIAMETER.equals(element) || RASAeroCommonConstants.SHOULDER_LENGTH.equals(element)) { return PlainTextHandler.INSTANCE; } @@ -50,12 +51,12 @@ public class BoosterHandler extends BodyTubeHandler { @Override public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { super.closeElement(element, attributes, content, warnings); - if (RASAeroCommonConstants.BOAT_TAIL_LENGTH.equals(element)) { - this.boatTailLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; - } else if (RASAeroCommonConstants.BOAT_TAIL_REAR_DIAMETER.equals(element)) { - this.boatTailRearDiameter = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + if (RASAeroCommonConstants.BOATTAIL_LENGTH.equals(element)) { + this.boatTailLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; + } else if (RASAeroCommonConstants.BOATTAIL_REAR_DIAMETER.equals(element)) { + this.boatTailRearDiameter = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; } else if (RASAeroCommonConstants.SHOULDER_LENGTH.equals(element)) { - this.shoulderLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + this.shoulderLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; } } diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/FinCanHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/FinCanHandler.java index 353f1a3b6..f1d9699d9 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/FinCanHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/FinCanHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -59,9 +60,9 @@ public class FinCanHandler extends BodyTubeHandler { super.closeElement(element, attributes, content, warnings); try { if (RASAeroCommonConstants.INSIDE_DIAMETER.equals(element)) { - insideDiameter = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + insideDiameter = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; } else if (RASAeroCommonConstants.SHOULDER_LENGTH.equals(element)) { - shoulderLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + shoulderLength = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; } } catch (NumberFormatException nfe) { warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/FinHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/FinHandler.java index a0aca4fcb..b650846aa 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/FinHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/FinHandler.java @@ -1,6 +1,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; @@ -46,21 +47,21 @@ public class FinHandler extends AbstractElementHandler { if (RASAeroCommonConstants.FIN_COUNT.equals(element)) { finSet.setFinCount(Integer.parseInt(content)); } else if (RASAeroCommonConstants.FIN_CHORD.equals(element)) { - finSet.setRootChord(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + finSet.setRootChord(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } else if (RASAeroCommonConstants.FIN_SPAN.equals(element)) { - finSet.setHeight(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + finSet.setHeight(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } else if (RASAeroCommonConstants.FIN_SWEEP_DISTANCE.equals(element)) { - finSet.setSweep(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + finSet.setSweep(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } else if (RASAeroCommonConstants.FIN_TIP_CHORD.equals(element)) { - finSet.setTipChord(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + finSet.setTipChord(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } else if (RASAeroCommonConstants.FIN_THICKNESS.equals(element)) { - finSet.setThickness(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + finSet.setThickness(Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } else if (RASAeroCommonConstants.AIRFOIL_SECTION.equals(element)) { - finSet.setCrossSection(RASAeroCommonConstants.getFinCrossSectionFromRASAero(content, warnings)); + finSet.setCrossSection(RASAeroCommonConstants.RASAERO_TO_OPENROCKET_FIN_CROSSSECTION(content, warnings)); } else if (RASAeroCommonConstants.LOCATION.equals(element)) { // Location is the location of the front of the fin relative to the bottom of the body tube finSet.setAxialMethod(AxialMethod.BOTTOM); - double location = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + double location = Double.parseDouble(content) / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; location = -location + finSet.getLength(); finSet.setAxialOffset(location); } diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/LaunchLugHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/LaunchLugHandler.java index 66713f85d..a6a001c50 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/LaunchLugHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/LaunchLugHandler.java @@ -1,5 +1,6 @@ package net.sf.openrocket.file.rasaero.importt; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.LaunchLug; import net.sf.openrocket.rocketcomponent.position.AxialMethod; @@ -14,8 +15,8 @@ import net.sf.openrocket.rocketcomponent.position.AxialMethod; */ public abstract class LaunchLugHandler { public static void addLaunchLug(BodyTube parent, double diameter, double length) { - diameter = diameter / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; - length = length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + diameter = diameter / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; + length = length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; LaunchLug lug = generateLaunchLugFromRASAeroRailGuide(diameter, length, parent.getLength()); parent.addChild(lug); diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/LaunchSiteHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/LaunchSiteHandler.java index d8145e260..81c856c3b 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/LaunchSiteHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/LaunchSiteHandler.java @@ -1,6 +1,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/NoseConeHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/NoseConeHandler.java index 84114e199..f8ffb19c4 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/NoseConeHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/NoseConeHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.NoseCone; @@ -47,8 +48,8 @@ public class NoseConeHandler extends BaseHandler { public void closeElement(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { super.closeElement(element, attributes, content, warnings); if (RASAeroCommonConstants.SHAPE.equals(element)) { - this.noseCone.setShapeType(RASAeroCommonConstants.getNoseConeShapeFromRASAero(content)); - this.noseCone.setShapeParameter(RASAeroCommonConstants.getNoseConeShapeParameterFromRASAeroShape(content)); + this.noseCone.setShapeType(RASAeroCommonConstants.RASAERO_TO_OPENROCKET_SHAPE(content)); + this.noseCone.setShapeParameter(RASAeroCommonConstants.RASAERO_TO_OPENROCKET_SHAPE_PARAMETER(content)); } try { if (RASAeroCommonConstants.POWER_LAW.equals(element)) { @@ -63,8 +64,8 @@ public class NoseConeHandler extends BaseHandler { @Override public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { super.endHandler(element, attributes, content, warnings); - this.noseCone.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); - this.noseCone.setBaseRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + this.noseCone.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + this.noseCone.setBaseRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); this.noseCone.setThickness(0.002); // Arbitrary value; RASAero doesn't specify this } diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroHandler.java index 3b85c9d5b..bc4a9a8cc 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/RASAeroHandler.java @@ -3,6 +3,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/RailGuideHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/RailGuideHandler.java index 6b7217174..2ba8ba2d3 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/RailGuideHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/RailGuideHandler.java @@ -1,5 +1,6 @@ package net.sf.openrocket.file.rasaero.importt; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.RailButton; import net.sf.openrocket.rocketcomponent.position.AxialMethod; @@ -22,8 +23,8 @@ public abstract class RailGuideHandler { * @param height total height of the rail guide, plus the screw height */ public static void addRailGuide(BodyTube parent, double diameter, double height) { - diameter = diameter / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; - height = height / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH; + diameter = diameter / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; + height = height / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH; RailButton button = generateRailButtonFromRASAeroRailGuide(diameter, height, parent.getLength()); parent.addChild(button); diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/RecoveryHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/RecoveryHandler.java index 39f6a9768..ed346a3bd 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/RecoveryHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/RecoveryHandler.java @@ -1,6 +1,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; @@ -163,7 +164,7 @@ public class RecoveryHandler extends AbstractElementHandler { recoveryDevice.setName("Recovery Event " + (recoveryDeviceNr+1)); DeploymentConfiguration config = recoveryDevice.getDeploymentConfigurations().getDefault(); - recoveryDevice.setDiameter(size / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + recoveryDevice.setDiameter(size / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); recoveryDevice.setLineLength(recoveryDevice.getDiameter()); recoveryDevice.setCD(CD); config.setDeployAltitude(altitude / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_ALTITUDE); @@ -173,7 +174,7 @@ public class RecoveryHandler extends AbstractElementHandler { eventType = "Altitude"; warnings.add("Recovery device 2 is set to apogee, but recovery device 1 is also set to apogee. Setting recovery device 2 to altitude."); } - config.setDeployEvent(RASAeroCommonConstants.getDeployEventFromRASAero(eventType, warnings)); + config.setDeployEvent(RASAeroCommonConstants.RASAERO_TO_OPENROCKET_DEPLOY_EVENT(eventType, warnings)); // Shroud line count = diameter / 6 inches. 6 inches = 0.1524 meters. Minimum is 6 lines. recoveryDevice.setLineCount(Math.max(6, (int) Math.round(recoveryDevice.getDiameter() / 0.1524))); diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/SimulationListHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/SimulationListHandler.java index 8b8a26ec7..1bd623d80 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/SimulationListHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/SimulationListHandler.java @@ -3,6 +3,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.AbstractElementHandler; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/SurfaceFinishHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/SurfaceFinishHandler.java index 3205af2c3..dfd1d328b 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/SurfaceFinishHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/SurfaceFinishHandler.java @@ -1,6 +1,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -12,7 +13,7 @@ import net.sf.openrocket.rocketcomponent.RocketComponent; */ public abstract class SurfaceFinishHandler { public static void setSurfaceFinishes(Rocket rocket, String finish, WarningSet warnings) { - ExternalComponent.Finish surfaceFinish = RASAeroCommonConstants.getSurfaceFinishFromRASAero(finish, warnings); + ExternalComponent.Finish surfaceFinish = RASAeroCommonConstants.RASAERO_TO_OPENROCKET_SURFACE(finish, warnings); for (RocketComponent component : rocket) { if (component instanceof ExternalComponent) { ((ExternalComponent) component).setFinish(surfaceFinish); diff --git a/core/src/net/sf/openrocket/file/rasaero/importt/TransitionHandler.java b/core/src/net/sf/openrocket/file/rasaero/importt/TransitionHandler.java index 6f9893cc3..7afeebee8 100644 --- a/core/src/net/sf/openrocket/file/rasaero/importt/TransitionHandler.java +++ b/core/src/net/sf/openrocket/file/rasaero/importt/TransitionHandler.java @@ -2,6 +2,7 @@ package net.sf.openrocket.file.rasaero.importt; import net.sf.openrocket.aerodynamics.WarningSet; import net.sf.openrocket.file.DocumentLoadingContext; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.file.simplesax.ElementHandler; import net.sf.openrocket.file.simplesax.PlainTextHandler; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -54,7 +55,7 @@ public class TransitionHandler extends BaseHandler { super.closeElement(element, attributes, content, warnings); try { if (RASAeroCommonConstants.REAR_DIAMETER.equals(element)) { - this.transition.setAftRadius(Double.parseDouble(content) / 2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); + this.transition.setAftRadius(Double.parseDouble(content) / 2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); } } catch (NumberFormatException nfe) { warnings.add("Could not convert " + element + " value of " + content + ". It is expected to be a number."); @@ -64,8 +65,8 @@ public class TransitionHandler extends BaseHandler { @Override public void endHandler(String element, HashMap attributes, String content, WarningSet warnings) throws SAXException { super.endHandler(element, attributes, content, warnings); - this.transition.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); - this.transition.setForeRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_TO_LENGTH); // Not really useful, but adding it for completeness + this.transition.setLength(length / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); + this.transition.setForeRadius(diameter/2 / RASAeroCommonConstants.OPENROCKET_TO_RASAERO_LENGTH); // Not really useful, but adding it for completeness this.transition.setForeRadiusAutomatic(true); this.transition.setThickness(0.002); // Arbitrary value; RASAero doesn't specify this } diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index eac3b3642..3bc6b357f 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -79,6 +79,7 @@ public abstract class Preferences implements ChangeSource { private static final String SHOW_DISCARD_CONFIRMATION = "IgnoreDiscardEditingWarning"; public static final String MARKER_STYLE_ICON = "MARKER_STYLE_ICON"; private static final String SHOW_MARKERS = "SHOW_MARKERS"; + private static final String SHOW_RASAERO_FORMAT_WARNING = "SHOW_RASAERO_FORMAT_WARNING"; private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; //Preferences related to 3D graphics @@ -223,6 +224,14 @@ public abstract class Preferences implements ChangeSource { public final void setLaunchIntoWind(boolean check) { this.putBoolean(LAUNCH_INTO_WIND, check); } + + public final boolean getShowRASAeroFormatWarning() { + return this.getBoolean(SHOW_RASAERO_FORMAT_WARNING, true); + } + + public final void setShowRASAeroFormatWarning(boolean check) { + this.putBoolean(SHOW_RASAERO_FORMAT_WARNING, check); + } public final boolean getShowRockSimFormatWarning() { return this.getBoolean(SHOW_ROCKSIM_FORMAT_WARNING, true); diff --git a/core/src/net/sf/openrocket/util/Color.java b/core/src/net/sf/openrocket/util/Color.java index 8d598cc77..7c3186d34 100644 --- a/core/src/net/sf/openrocket/util/Color.java +++ b/core/src/net/sf/openrocket/util/Color.java @@ -65,5 +65,16 @@ public class Color { public java.awt.Color toAWTColor() { return new java.awt.Color(red, green, blue, alpha); } - + + @Override + public boolean equals(Object obj) { + if (super.equals(obj)) { + return true; + } + if (!(obj instanceof Color)) { + return false; + } + Color c = (Color) obj; + return c.getRed() == getRed() && c.getGreen() == getGreen() && c.getBlue() == getBlue() && c.getAlpha() == getAlpha(); + } } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java index de73270f9..f4893800a 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/GeneralPreferencesPanel.java @@ -228,6 +228,17 @@ public class GeneralPreferencesPanel extends PreferencesPanel { } }); this.add(openRecentOnStartupBox,"spanx, wrap"); + + //// Save RASAero Format warning dialog + final JCheckBox rasaeroWarningDialogBox = new JCheckBox(trans.get("pref.dlg.lbl.RASAeroWarning")); + rasaeroWarningDialogBox.setSelected(preferences.getShowRASAeroFormatWarning()); + rasaeroWarningDialogBox.addActionListener( new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setShowRASAeroFormatWarning(rasaeroWarningDialogBox.isSelected()); + } + }); + this.add(rasaeroWarningDialogBox,"spanx, wrap"); //// Save RockSim Format warning dialog final JCheckBox rocksimWarningDialogBox = new JCheckBox(trans.get("pref.dlg.lbl.RockSimWarning")); diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index dd3417dbf..2cec966ea 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -60,6 +60,7 @@ import net.sf.openrocket.document.events.DocumentChangeEvent; import net.sf.openrocket.document.events.DocumentChangeListener; import net.sf.openrocket.file.GeneralRocketSaver; import net.sf.openrocket.file.RocketLoadException; +import net.sf.openrocket.file.rasaero.RASAeroCommonConstants; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; import net.sf.openrocket.gui.customexpression.CustomExpressionDialog; @@ -87,7 +88,6 @@ import net.sf.openrocket.gui.util.OpenFileWorker; import net.sf.openrocket.gui.util.SaveFileWorker; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.URLUtil; -import net.sf.openrocket.gui.widgets.SaveFileChooser; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -407,18 +407,16 @@ public class BasicFrame extends JFrame { exportSubMenu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.exportAs.desc")); exportSubMenu.setIcon(Icons.FILE_EXPORT); - /* Pending Future Development ////// Export RASAero - JMenuItem exportRASAero = new JMenuItem(trans.get("main.fileMenu.file.exportAs.RASAero")); - exportRASAero.setIcon(Icons.RASAERO - exportRASAero.getAccessibleContext().setAccessibleDescription(trans.get("main.fileMenu.file.exportAs.RASAero.desc")); + JMenuItem exportRASAero = new JMenuItem(trans.get("main.menu.file.exportAs.RASAero")); + exportRASAero.setIcon(Icons.RASAERO); + exportRASAero.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.file.exportAs.RASAero.desc")); exportRASAero.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { exportRASAeroAction();} }); exportSubMenu.add(exportRASAero); - */ ////// Export RockSim JMenuItem exportRockSim = new JMenuItem(trans.get("main.menu.file.exportAs.RockSim")); @@ -1374,56 +1372,134 @@ public class BasicFrame extends JFrame { return saveAsOpenRocket(file); } + /** + * Opens a file chooser dialog for saving a new file, and returns the selected file. + * @param fileType file type to use (e.g. RASAero) + * @return the file selected from the dialog, or null if no file was selected. + */ + private File openFileSaveAsDialog(FileType fileType) { + final DesignFileSaveAsFileChooser chooser = DesignFileSaveAsFileChooser.build(document, fileType); - //// BEGIN RASAERO Export Action *** UNDER CONSTRUCTION -- CURRENTLY FOR TESTING ONLY *** + int option = chooser.showSaveDialog(BasicFrame.this); + + if (option != JFileChooser.APPROVE_OPTION) { + log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); + return null; + } + + File file = chooser.getSelectedFile(); + if (file == null) { + log.info(Markers.USER_MARKER, "User did not select a file"); + return null; + } + + ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); + + return file; + } + + + //// BEGIN RASAero Save/Export Action /** * MODEL "Export as" RASAero file format * * @return true if the file was saved, false otherwise */ - /* + public boolean exportRASAeroAction() { - Object exportRASAeroAction = ExportFileTranslator_RASAero.exportRASAeroAction; + File file = openFileSaveAsDialog(FileType.RASAERO); + if (file == null) { + return false; + } + + file = FileHelper.forceExtension(file, RASAeroCommonConstants.FILE_EXTENSION); + if (FileHelper.confirmWrite(file, this) ) { + return saveAsRASAero(file); + } return false; } - */ - //// END RASAERO Export Action + + /** + * Perform the writing of the design to the given file in RASAero format. + * @param file the chosen file + * @return true if the file was written + */ + private boolean saveAsRASAero(File file) { + if (prefs.getShowRASAeroFormatWarning()) { + // Show RASAero format warning + JPanel panel = new JPanel(new MigLayout()); + panel.add(new StyledLabel(trans.get("SaveRASAeroWarningDialog.txt1")), "wrap"); + final JCheckBox check = new JCheckBox(trans.get("SaveRASAeroWarningDialog.donotshow")); + check.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + prefs.setShowRASAeroFormatWarning(!check.isSelected()); + } + }); + panel.add(check); + int sel = JOptionPane.showOptionDialog(null, + panel, + "", // title + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE, + null, // icon + null, // options + null // default option + ); + if (sel == 1) { + return false; + } + } + + StorageOptions options = new StorageOptions(); + options.setFileType(FileType.RASAERO); + return saveRASAeroFile(file, options); + } + + /** + * Perform the actual saving of the RASAero file + * @param file file to be stored + * @param options storage options to use + * @return true if the file was written + */ + private boolean saveRASAeroFile(File file, StorageOptions options) { + try { + ROCKET_SAVER.save(file, document, options); + // Do not update the save state of the document. + return true; + } catch (IOException e) { + return false; + } catch (DecalNotFoundException decex) { + DecalImage decal = decex.getDecal(); + // Check if the user replaced the source file, if not, just ignore the faulty decal on the next save + if (!DecalNotFoundDialog.showDialog(null, decex) && decal != null) { + decal.setIgnored(true); + } + return saveRASAeroFile(file, options); // Re-save + } + } + //// END RASAero Save/Export Action - //// BEGIN ROCKSIM Export Action + //// BEGIN ROCKSIM Save/Export Action /** * MODEL "Export as" RASAero file format * * @return true if the file was saved, false otherwise */ public boolean exportRockSimAction() { - File file; - - final DesignFileSaveAsFileChooser chooser = DesignFileSaveAsFileChooser.build(document, FileType.ROCKSIM); - - int option = chooser.showSaveDialog(BasicFrame.this); - - if (option != JFileChooser.APPROVE_OPTION) { - log.info(Markers.USER_MARKER, "User decided not to save, option=" + option); - return false; - } - - file = chooser.getSelectedFile(); + File file = openFileSaveAsDialog(FileType.ROCKSIM); if (file == null) { - log.info(Markers.USER_MARKER, "User did not select a file"); return false; } - ((SwingPreferences) Application.getPreferences()).setDefaultDirectory(chooser.getCurrentDirectory()); - file = FileHelper.forceExtension(file, "rkt"); if (FileHelper.confirmWrite(file, this) ) { return saveAsRockSim(file); } return false; } - // END ROCKSIM Export Action /** * Perform the writing of the design to the given file in RockSim format. @@ -1488,6 +1564,8 @@ public class BasicFrame extends JFrame { } } + //// END ROCKSIM Save/Export Action + /** * "Save As" action. diff --git a/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java b/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java index 7202e02f9..52a8d3c29 100644 --- a/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java +++ b/swing/src/net/sf/openrocket/gui/main/DesignFileSaveAsFileChooser.java @@ -53,13 +53,13 @@ public class DesignFileSaveAsFileChooser extends SaveFileChooser { this.addChoosableFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); this.setFileFilter(FileHelper.ROCKSIM_DESIGN_FILTER); break; - /*case RASAERO: + case RASAERO: defaultFilename = FileHelper.forceExtension(defaultFilename,"CDX1"); this.setDialogTitle(trans.get("saveAs.rasaero.title")); storageChooser = null; this.addChoosableFileFilter(FileHelper.RASAERO_DESIGN_FILTER); this.setFileFilter(FileHelper.RASAERO_DESIGN_FILTER); - break;*/ + break; } final RememberFilenamePropertyListener listener = new RememberFilenamePropertyListener();