diff --git a/core/.classpath b/core/.classpath index 453ab552f..06a9ed43d 100644 --- a/core/.classpath +++ b/core/.classpath @@ -17,6 +17,7 @@ + diff --git a/core/OpenRocket Core.iml b/core/OpenRocket Core.iml index 8a5fd8008..dce24f46b 100644 --- a/core/OpenRocket Core.iml +++ b/core/OpenRocket Core.iml @@ -270,5 +270,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/lib/graal-sdk-22.1.0.1.jar b/core/lib/graal-sdk-22.1.0.1.jar new file mode 100644 index 000000000..a08644e94 Binary files /dev/null and b/core/lib/graal-sdk-22.1.0.1.jar differ diff --git a/core/lib/icu4j-71.1.jar b/core/lib/icu4j-71.1.jar new file mode 100644 index 000000000..7c5cb5064 Binary files /dev/null and b/core/lib/icu4j-71.1.jar differ diff --git a/core/lib/js-22.1.0.1.jar b/core/lib/js-22.1.0.1.jar new file mode 100644 index 000000000..3985e48e9 Binary files /dev/null and b/core/lib/js-22.1.0.1.jar differ diff --git a/core/lib/js-scriptengine-22.1.0.1.jar b/core/lib/js-scriptengine-22.1.0.1.jar new file mode 100644 index 000000000..c9f7332eb Binary files /dev/null and b/core/lib/js-scriptengine-22.1.0.1.jar differ diff --git a/core/lib/truffle-api-22.1.0.1.jar b/core/lib/truffle-api-22.1.0.1.jar new file mode 100644 index 000000000..4f35bd7c7 Binary files /dev/null and b/core/lib/truffle-api-22.1.0.1.jar differ diff --git a/core/lib/yasson-1.0.2.jar b/core/lib/yasson-1.0.2.jar new file mode 100644 index 000000000..ed935f3aa Binary files /dev/null and b/core/lib/yasson-1.0.2.jar differ diff --git a/core/resources-src/pix/componenticons/bulkhead-small.png b/core/resources-src/pix/componenticons/bulkhead-small.png new file mode 100644 index 000000000..c41db8b18 Binary files /dev/null and b/core/resources-src/pix/componenticons/bulkhead-small.png differ diff --git a/core/resources-src/pix/componenticons/centeringring-small.png b/core/resources-src/pix/componenticons/centeringring-small.png new file mode 100644 index 000000000..6b4a80aed Binary files /dev/null and b/core/resources-src/pix/componenticons/centeringring-small.png differ diff --git a/core/resources-src/pix/componenticons/ellipticalfin-small.png b/core/resources-src/pix/componenticons/ellipticalfin-small.png new file mode 100644 index 000000000..35180a0e3 Binary files /dev/null and b/core/resources-src/pix/componenticons/ellipticalfin-small.png differ diff --git a/core/resources-src/pix/componenticons/engineblock-small.png b/core/resources-src/pix/componenticons/engineblock-small.png new file mode 100644 index 000000000..58cf6316e Binary files /dev/null and b/core/resources-src/pix/componenticons/engineblock-small.png differ diff --git a/core/resources-src/pix/componenticons/freeformfin-small.png b/core/resources-src/pix/componenticons/freeformfin-small.png new file mode 100644 index 000000000..e8ce8ea02 Binary files /dev/null and b/core/resources-src/pix/componenticons/freeformfin-small.png differ diff --git a/core/resources-src/pix/componenticons/innertube-small.png b/core/resources-src/pix/componenticons/innertube-small.png new file mode 100644 index 000000000..5f65f3116 Binary files /dev/null and b/core/resources-src/pix/componenticons/innertube-small.png differ diff --git a/core/resources-src/pix/componenticons/mass-small.png b/core/resources-src/pix/componenticons/mass-small.png new file mode 100644 index 000000000..6104b8562 Binary files /dev/null and b/core/resources-src/pix/componenticons/mass-small.png differ diff --git a/core/resources-src/pix/componenticons/nosecone-small.png b/core/resources-src/pix/componenticons/nosecone-small.png new file mode 100644 index 000000000..6cc18ff27 Binary files /dev/null and b/core/resources-src/pix/componenticons/nosecone-small.png differ diff --git a/core/resources-src/pix/componenticons/parachute-small.png b/core/resources-src/pix/componenticons/parachute-small.png new file mode 100644 index 000000000..a3db68b2e Binary files /dev/null and b/core/resources-src/pix/componenticons/parachute-small.png differ diff --git a/core/resources-src/pix/componenticons/pods-large.xcf.gz b/core/resources-src/pix/componenticons/pods-large.xcf.gz index 0c28362eb..6d81c3d0e 100644 Binary files a/core/resources-src/pix/componenticons/pods-large.xcf.gz and b/core/resources-src/pix/componenticons/pods-large.xcf.gz differ diff --git a/core/resources-src/pix/componenticons/pods-small.xcf.gz b/core/resources-src/pix/componenticons/pods-small.xcf.gz index 3af13b5f1..17e95a14e 100644 Binary files a/core/resources-src/pix/componenticons/pods-small.xcf.gz and b/core/resources-src/pix/componenticons/pods-small.xcf.gz differ diff --git a/core/resources-src/pix/componenticons/railbutton-small.png b/core/resources-src/pix/componenticons/railbutton-small.png new file mode 100644 index 000000000..3cc5d4b69 Binary files /dev/null and b/core/resources-src/pix/componenticons/railbutton-small.png differ diff --git a/core/resources-src/pix/componenticons/shockcord-small.png b/core/resources-src/pix/componenticons/shockcord-small.png new file mode 100644 index 000000000..653107120 Binary files /dev/null and b/core/resources-src/pix/componenticons/shockcord-small.png differ diff --git a/core/resources-src/pix/componenticons/streamer-small.png b/core/resources-src/pix/componenticons/streamer-small.png new file mode 100644 index 000000000..32fb68627 Binary files /dev/null and b/core/resources-src/pix/componenticons/streamer-small.png differ diff --git a/core/resources-src/pix/componenticons/transition-small.png b/core/resources-src/pix/componenticons/transition-small.png new file mode 100644 index 000000000..04604fe33 Binary files /dev/null and b/core/resources-src/pix/componenticons/transition-small.png differ diff --git a/core/resources-src/pix/componenticons/trapezoidfin-small.png b/core/resources-src/pix/componenticons/trapezoidfin-small.png new file mode 100644 index 000000000..c676e928f Binary files /dev/null and b/core/resources-src/pix/componenticons/trapezoidfin-small.png differ diff --git a/core/resources-src/pix/componenticons/tubecoupler-small.png b/core/resources-src/pix/componenticons/tubecoupler-small.png new file mode 100644 index 000000000..ef0e08653 Binary files /dev/null and b/core/resources-src/pix/componenticons/tubecoupler-small.png differ diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 7d81f82eb..fcf0b1298 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -294,6 +294,8 @@ pref.dlg.lbl.PositiontoinsertStages = Position to insert new stages: pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations. pref.dlg.checkbox.Runsimulations = Run out-dated simulations when you open the simulation tab. pref.dlg.checkbox.Updateestimates = Update estimated flight parameters in design window +pref.dlg.checkbox.Markers = Only show pod set/booster markers when the pod set/booster is selected +pref.dlg.checkbox.Markers.ttip = If checked, pod set/booster markers will only be shown when the pod set/booster is selected.
If unchecked, pod set/booster markers will always be shown. pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a component edit dialog pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = If checked, a component edit dialog will always pop up with the first tab selected.
If unchecked, the previous selected tab will be used. pref.dlg.lbl.User-definedthrust = User-defined thrust curves: @@ -1050,7 +1052,10 @@ LaunchLugCfg.tab.Generalprop = General properties ! RailButtonConfig RailBtnCfg.lbl.OuterDiam = Outer Diameter: -RailBtnCfg.lbl.TotalHeight = Total Height +RailBtnCfg.lbl.InnerDiam = Inner Diameter: +RailBtnCfg.lbl.TotalHeight = Total Height: +RailBtnCfg.lbl.BaseHeight = Base Height: +RailBtnCfg.lbl.FlangeHeight = Flange Height: RailBtnCfg.lbl.Angle = Rotation: RailBtnCfg.lbl.PosRelativeTo = Position relative to: RailBtnCfg.lbl.Plus = plus @@ -1373,14 +1378,13 @@ main.menu.edit.editpreset= Edit Component Preset File main.menu.edit.preferences = Preferences main.menu.edit.preferences.desc = Setup the application preferences -main.menu.analyze = Tools -main.menu.analyze.desc = Rocket analysis -main.menu.analyze.componentAnalysis = Component analysis -main.menu.analyze.componentAnalysis.desc = Analyze the rocket components separately -main.menu.analyze.optimization = Rocket optimization -main.menu.analyze.optimization.desc = General rocket design optimization -main.menu.analyze.customExpressions = Custom expressions -main.menu.analyze.customExpressions.desc = Define new flight data types by writing custom mathematical expressions +main.menu.tools = Tools +main.menu.tools.componentAnalysis = Component analysis +main.menu.tools.componentAnalysis.desc = Analyze the rocket components separately +main.menu.tools.optimization = Rocket optimization +main.menu.tools.optimization.desc = General rocket design optimization +main.menu.tools.customExpressions = Custom expressions +main.menu.tools.customExpressions.desc = Define new flight data types by writing custom mathematical expressions main.menu.help = Help main.menu.help.desc = Information about OpenRocket @@ -2072,8 +2076,10 @@ CustomFinImport.error.badimage = Could not deduce fin shape from image. CustomFinImport.description = The image will be converted internally to black and white image (black for the fin), so make sure you use a solid dark color for the fin, and white or a light color for the background. The fin must be touching the bottom of the image, which is the base of the fin. -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database\u2026 +PresetModel.combo.ttip = Select a preset model from a list of favorites (selected in the component preset dialog),
or select 'Custom' when no preset is required. +PresetModel.lbl.custompreset = Custom +PresetModel.lbl.partsLib = Parts Library +PresetModel.lbl.partsLib.ttip = Select a preset model for this rocket component from a library of parts. DecalModel.lbl.select = DecalModel.lbl.choose = From file\u2026 @@ -2095,7 +2101,7 @@ ComponentPresetChooserDialog.menu.sortDesc = Sort Descending ComponentPresetChooserDialog.menu.units = Units ComponentPresetChooserDialog.checkbox.showAllCompatible = Show all compatible ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = Show Legacy Database -ComponentPresetChooserDialog.lbl.favorites = Select to add preset to drop-down menu +ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog
Directly apply a preset by double-clicking it or by selecting it and closing this window. table.column.Favorite = Favorite table.column.Legacy = Legacy table.column.Manufacturer = Manufacturer @@ -2112,19 +2118,23 @@ table.column.AftShoulderDiameter = Aft Shoulder Diameter table.column.ForeShoulderLength = Fore Shoulder Length table.column.ForeShoulderDiameter = Fore Shoulder Diameter table.column.ForeOuterDiameter = Fore Outer Diameter -table.column.StandoffHeight = Standoff Height +table.column.BaseHeight = Base Height table.column.FlangeHeight = Flange Height +table.column.ScrewHeight = Screw Height table.column.Shape = Shape table.column.Material = Material table.column.Finish = Finish table.column.Thickness = Thickness table.column.Filled = Filled table.column.Mass = Mass +table.column.ScrewMass = Screw Mass +table.column.NutMass = Nut Mass table.column.Diameter = Diameter table.column.Sides = Sides table.column.LineCount = Line Count table.column.LineLength = Line Length table.column.LineMaterial = Line Material +table.column.CD = Drag Coefficient ! Edit Decal Dialog EditDecalDialog.title = Edit decal diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index 627fe287f..ae8ba7d23 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -1004,12 +1004,11 @@ main.menu.edit.delete = Smazat main.menu.edit.preferences = Nastavení main.menu.edit.preferences.desc = Nastavení aplikace -main.menu.analyze = Anal\u017Eýza -main.menu.analyze.desc = Analýza rakety -main.menu.analyze.componentAnalysis = Analýza komponent -main.menu.analyze.componentAnalysis.desc = Analyzuj cásti rakety samostatne -main.menu.analyze.optimization = Optimalizace rakety -main.menu.analyze.optimization.desc = Obecný návrh optimalizace rakety +main.menu.tools = Anal\u017Eýza +main.menu.tools.componentAnalysis = Analýza komponent +main.menu.tools.componentAnalysis.desc = Analyzuj cásti rakety samostatne +main.menu.tools.optimization = Optimalizace rakety +main.menu.tools.optimization.desc = Obecný návrh optimalizace rakety main.menu.help = Pomoc main.menu.help.desc = Informace o programu OpenRocket @@ -1587,8 +1586,8 @@ CustomFinImport.error.badimage = Nemohu vyvodit tvar stabiliz CustomFinImport.description = Obrázek bude zmenen na cernobílý \n(cerná pro stabilizátor), ujistete se prosím, \u017Ee jste pou\u017Eily cernou barvu na stabilizátor \na bílou nebo svetlou barvu na pozadí. Stabilizátor \nse musí dotýkat steny obrázku, která predstavuje uchycení pro stabilizátor. -PresetModel.lbl.select = Výber predvolby: -PresetModel.lbl.database = Z databáze... +PresetModel.lbl.custompreset = Vlastní +PresetModel.lbl.partsLib = Knihovna díl? ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 30b6df86c..a308fd80b 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -1061,12 +1061,11 @@ main.menu.edit.delete = L main.menu.edit.preferences = Einstellungen main.menu.edit.preferences.desc = Einstellungen der Anwenung ändern -main.menu.analyze = Analysieren -main.menu.analyze.desc = Rakete analysieren -main.menu.analyze.componentAnalysis = Komponente analysieren -main.menu.analyze.componentAnalysis.desc = Komponenten der Rakete einzeln analysieren -main.menu.analyze.optimization = Rocket optimization -main.menu.analyze.optimization.desc = General rocket design optimization +main.menu.tools = Analysieren +main.menu.tools.componentAnalysis = Komponente analysieren +main.menu.tools.componentAnalysis.desc = Komponenten der Rakete einzeln analysieren +main.menu.tools.optimization = Rocket optimization +main.menu.tools.optimization.desc = General rocket design optimization main.menu.help = Hilfe main.menu.help.desc = Informationen über OpenRocket @@ -1645,8 +1644,8 @@ CustomFinImport.error.badimage = Konnte keine Leitwerksform aus dem Bild erzeuge CustomFinImport.description = Das Bild wird intern in ein Schwarz-Weiß-Bild konvertiert (Leitwerk: schwarz). Bitte sicherstellen, dass das Leitwerk in einer dichten, dunklen Farbe ist, während der Hintergrund weiß oder sehr hell sein sollte. Das Leitwerk muss das untere Bildende berühren, da dies die Verbindungsstelle zur Rakete wird. -PresetModel.lbl.select = Voreinstellung auswählen: -PresetModel.lbl.database = Aus Datenbank... +PresetModel.lbl.custompreset = Benutzerdefiniert +PresetModel.lbl.partsLib = Teile-Bibliothek ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 6386c101d..854ec0214 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -737,8 +737,8 @@ PreferencesDialog.languages.default = Idioma por defecto PreferencesDialog.lbl.language = Idioma de la interfaz: PreferencesDialog.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket. -PresetModel.lbl.database = Desde la Base de Datos... -PresetModel.lbl.select = Prefabricado +PresetModel.lbl.custompreset = Personalizado +PresetModel.lbl.partsLib = Biblioteca de piezas PrintDialog.but.previewAndPrint = Vista previa e Imprimir PrintDialog.checkbox.showByStage = Mostrar por etapas @@ -1429,14 +1429,13 @@ FileHelper.IMAGES = Archivos de imagen ! General file type names FileHelper.PDF_FILTER = Archivos PDF -main.menu.analyze = Analizar -main.menu.analyze.componentAnalysis = An\u00e1lisis de los componentes -main.menu.analyze.componentAnalysis.desc = Analiza los componentes del cohete por separado -main.menu.analyze.customExpressions = Expresiones personalizadas -main.menu.analyze.customExpressions.desc = Defina nuevos tipos de datos escribiendo expresiones matem\u00e1ticas personalizadas -main.menu.analyze.desc = An\u00e1lisis del cohete -main.menu.analyze.optimization = Optimizaci\u00f3n del dise\u00f1o -main.menu.analyze.optimization.desc = Optimizaci\u00f3n global del dise\u00f1o del cohete +main.menu.tools = Analizar +main.menu.tools.componentAnalysis = An\u00e1lisis de los componentes +main.menu.tools.componentAnalysis.desc = Analiza los componentes del cohete por separado +main.menu.tools.customExpressions = Expresiones personalizadas +main.menu.tools.customExpressions.desc = Defina nuevos tipos de datos escribiendo expresiones matem\u00e1ticas personalizadas +main.menu.tools.optimization = Optimizaci\u00f3n del dise\u00f1o +main.menu.tools.optimization.desc = Optimizaci\u00f3n global del dise\u00f1o del cohete main.menu.debug = Recuperaci\u00f3n main.menu.debug.createtestrocket = Crear una prueba de modelo main.menu.debug.whatisthismenu = \u00bfQue es este men\u00fa? diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index 2ea355901..d15c994e5 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -729,8 +729,8 @@ PreferencesDialog.languages.default = Valeur syst\u00E8me par d\u00E9faut PreferencesDialog.lbl.language = Langue du programme: PreferencesDialog.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket. -PresetModel.lbl.database = A partir d'une base de donn\u00E9es... -PresetModel.lbl.select = Choisir une pi\u00E8ce pr\u00E9d\u00E9finie: +PresetModel.lbl.custompreset = Personnalisé +PresetModel.lbl.partsLib = Biblioth\u00E8que de pi\u00E8ces PrintDialog.but.previewAndPrint = Pr\u00E9-visualiser et imprimer PrintDialog.checkbox.showByStage = Montrer par \u00E9tage @@ -1423,14 +1423,13 @@ FileHelper.IMAGES = Fichiers Image ! General file type names FileHelper.PDF_FILTER = fichier PDF -main.menu.analyze = Analyse -main.menu.analyze.componentAnalysis = Analyse des Pi\u00E8ces -main.menu.analyze.componentAnalysis.desc = Analyse s\u00E9par\u00E9e des pi\u00E8ces de la fus\u00E9e -main.menu.analyze.customExpressions = Expressions personnalis\u00E9es -main.menu.analyze.customExpressions.desc = D\u00E9fini de nouveaux type de donn\u00E9es de vol en \u00E9crivant des expressions math\u00E9matique personnalis\u00E9es -main.menu.analyze.desc = Analyses de la fus\u00E9e -main.menu.analyze.optimization = Optimisation de la fus\u00E9e -main.menu.analyze.optimization.desc = Optimisation g\u00E9n\u00E9rale de la fus\u00E9e +main.menu.tools = Analyse +main.menu.tools.componentAnalysis = Analyse des Pi\u00E8ces +main.menu.tools.componentAnalysis.desc = Analyse s\u00E9par\u00E9e des pi\u00E8ces de la fus\u00E9e +main.menu.tools.customExpressions = Expressions personnalis\u00E9es +main.menu.tools.customExpressions.desc = D\u00E9fini de nouveaux type de donn\u00E9es de vol en \u00E9crivant des expressions math\u00E9matique personnalis\u00E9es +main.menu.tools.optimization = Optimisation de la fus\u00E9e +main.menu.tools.optimization.desc = Optimisation g\u00E9n\u00E9rale de la fus\u00E9e main.menu.debug = Debug main.menu.debug.createtestrocket = Cr\u00E9er une fus\u00E9e test main.menu.debug.whatisthismenu = Quel est ce menu? diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index 6d44cadf1..447e4eff8 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -1065,12 +1065,11 @@ main.menu.edit.delete = Cancella main.menu.edit.preferences = Preferenze main.menu.edit.preferences.desc = Imposta le preferenze dell'applicazione -main.menu.analyze = Analizza -main.menu.analyze.desc = Analisi del razzo -main.menu.analyze.componentAnalysis = Analizza componente -main.menu.analyze.componentAnalysis.desc = Analizza ogni componente del razzo separatamente -main.menu.analyze.optimization = Ottimizzazione del razzo -main.menu.analyze.optimization.desc = Ottimizzazioni generali sul disegno del razzo +main.menu.tools = Analizza +main.menu.tools.componentAnalysis = Analizza componente +main.menu.tools.componentAnalysis.desc = Analizza ogni componente del razzo separatamente +main.menu.tools.optimization = Ottimizzazione del razzo +main.menu.tools.optimization.desc = Ottimizzazioni generali sul disegno del razzo main.menu.help = Aiuto main.menu.help.desc = Informazioni su OpenRocket @@ -1651,8 +1650,8 @@ CustomFinImport.error.badimage = Non riesco a capire la forma della pinna dall CustomFinImport.description = L'immagine sar\u00e0 convertita in bianco e nero internamente (nero per le pinne), cos\u00ec assicurati di usare un nero pieno per le pinne e bianco, o colore chiaro, per lo sfondo. La pinna ndeve toccare il fondo dell'immagine, che \u00e8 la base della pinna. -PresetModel.lbl.select = Seleziona precaricati: -PresetModel.lbl.database = Da database... +PresetModel.lbl.custompreset = Personalizzato +PresetModel.lbl.partsLib = Libreria di parti ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index 44e7e9004..bfef9700f 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -1097,14 +1097,13 @@ main.menu.edit.editpreset = Component Preset File\u306E\u7DE8\u96C6 main.menu.edit.preferences = \u8A2D\u5B9A main.menu.edit.preferences.desc = \u30A2\u30D7\u30EA\u306E\u8A2D\u5B9A\u3092\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7 -main.menu.analyze = \u89E3\u6790 -main.menu.analyze.desc = \u30ED\u30B1\u30C3\u30C8\u89E3\u6790 -main.menu.analyze.componentAnalysis = \u90E8\u54C1\u89E3\u6790 -main.menu.analyze.componentAnalysis.desc = \u90E8\u54C1\u3092\u5206\u3051\u3066\u89E3\u6790 -main.menu.analyze.optimization = \u30ED\u30B1\u30C3\u30C8\u6700\u9069\u5316 -main.menu.analyze.optimization.desc = \u5168\u4F53\u3092\u6700\u9069\u5316 -main.menu.analyze.customExpressions = \u30AB\u30B9\u30BF\u30E0\u5F0F -main.menu.analyze.customExpressions.desc = \u65B0\u3057\u3044\u30D5\u30E9\u30A4\u30C8\u30C7\u30FC\u30BF\u30BF\u30A4\u30D7\u3092\u30AB\u30B9\u30BF\u30E0\u3057\u305F\u6570\u5F0F\u3067\u5B9A\u7FA9 +main.menu.tools = \u89E3\u6790 +main.menu.tools.componentAnalysis = \u90E8\u54C1\u89E3\u6790 +main.menu.tools.componentAnalysis.desc = \u90E8\u54C1\u3092\u5206\u3051\u3066\u89E3\u6790 +main.menu.tools.optimization = \u30ED\u30B1\u30C3\u30C8\u6700\u9069\u5316 +main.menu.tools.optimization.desc = \u5168\u4F53\u3092\u6700\u9069\u5316 +main.menu.tools.customExpressions = \u30AB\u30B9\u30BF\u30E0\u5F0F +main.menu.tools.customExpressions.desc = \u65B0\u3057\u3044\u30D5\u30E9\u30A4\u30C8\u30C7\u30FC\u30BF\u30BF\u30A4\u30D7\u3092\u30AB\u30B9\u30BF\u30E0\u3057\u305F\u6570\u5F0F\u3067\u5B9A\u7FA9 main.menu.help = \u30D8\u30EB\u30D7 main.menu.help.desc = OpenRocket\u306B\u3064\u3044\u3066\u306E\u60C5\u5831 @@ -1713,8 +1712,8 @@ CustomFinImport.error.badimage = \u753B\u50CF\u304B\u3089\u30D5\u30A3\u30F3\u5F CustomFinImport.description = \u753B\u50CF\u306F\u5185\u90E8\u3067\u767D\u80CC\u666F\u3068\u9ED2\u7DDA\u306B\u5909\u63DB\u3055\u308C\u307E\u3059\u3002\u306A\u306E\u3067\u30D5\u30A3\u30F3\u306B\u306F\u6697\u3044\u8272\u306E\u5B9F\u7DDA\u3001\u80CC\u666F\u306B\u306F\u767D\u304B\u660E\u308B\u3044\u8272\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D5\u30A3\u30F3\u306F\u753B\u50CF\u306E\u5E95\u9762\u306B\u63A5\u3057\u3066\u3044\u306A\u304F\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3001\u3053\u308C\u306F\u30D5\u30A3\u30F3\u306E\u5E95\u9762\u306B\u306A\u308A\u307E\u3059\u3002 -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database... +PresetModel.lbl.custompreset = \u30ab\u30b9\u30bf\u30e0 +PresetModel.lbl.partsLib = \u30d1\u30fc\u30c4\u30e9\u30a4\u30d6\u30e9\u30ea\u30fc ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 78365b1c9..4557ca938 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -1274,14 +1274,13 @@ main.menu.edit.editpreset= Bewerk Component Preset bestand main.menu.edit.preferences = Voorkeuren main.menu.edit.preferences.desc = Stel de programma voorkeuren in -main.menu.analyze = Tools -main.menu.analyze.desc = Raket-analyses -main.menu.analyze.componentAnalysis = Onderdeel-analyses -main.menu.analyze.componentAnalysis.desc = Analyseer de raketonderdelen afzonderlijk -main.menu.analyze.optimization = Raket-optimalisatie -main.menu.analyze.optimization.desc = Algemene optimalisatie van raketontwerp -main.menu.analyze.customExpressions = Aangepaste uitdrukkingen -main.menu.analyze.customExpressions.desc = Definieer nieuwe vluchtdatatypes door aangepaste wiskundige uitdrukkingen te schrijven +main.menu.tools = Tools +main.menu.tools.componentAnalysis = Onderdeel-analyses +main.menu.tools.componentAnalysis.desc = Analyseer de raketonderdelen afzonderlijk +main.menu.tools.optimization = Raket-optimalisatie +main.menu.tools.optimization.desc = Algemene optimalisatie van raketontwerp +main.menu.tools.customExpressions = Aangepaste uitdrukkingen +main.menu.tools.customExpressions.desc = Definieer nieuwe vluchtdatatypes door aangepaste wiskundige uitdrukkingen te schrijven main.menu.help = Help main.menu.help.desc = Informatie over OpenRocket @@ -1967,8 +1966,8 @@ CustomFinImport.error.badimage = Kon de vorm van de vin niet afleiden uit het be CustomFinImport.description = De afbeelding wordt intern geconverteerd naar een zwart-wit afbeelding (zwart voor de vin), dus zorg ervoor dat je een effen donkere kleur gebruikt voor de vin, en wit of een lichte kleur voor de achtergrond. De vin moet de onderkant van het beeld raken, dat is de basis van de vin. -PresetModel.lbl.select = Selecteer preset -PresetModel.lbl.database = Uit databank... +PresetModel.lbl.custompreset = Aangepast +PresetModel.lbl.partsLib = Onderdelenbibliotheek DecalModel.lbl.select = DecalModel.lbl.choose = Van bestand... @@ -2003,7 +2002,7 @@ table.column.AftShoulderDiameter = Achteste Schouderdiameter table.column.ForeShoulderLength = Voorste Schouderlengte table.column.ForeShoulderDiameter = Voorste Schouderdiameter table.column.ForeOuterDiameter = Voorste Buitendiameter -table.column.StandoffHeight = Standoff Hoogte +table.column.BaseHeight = Standoff Hoogte table.column.FlangeHeight = Flens Hoogte table.column.Shape = Vorm table.column.Material = Materiaal diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index 6ed2c71de..1bc052188 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -1006,12 +1006,11 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. main.menu.edit.preferences = Ustawienia main.menu.edit.preferences.desc = Edytuj ustawienia programu - main.menu.analyze = Analiza - main.menu.analyze.desc = Analiza rakiety - main.menu.analyze.componentAnalysis = Analiza cz\u0119\u015Bci - main.menu.analyze.componentAnalysis.desc = Analizuj oddzielnie poszczególne cz\u0119\u015Bci sk\u0142adóe rakiety - main.menu.analyze.optimization = Optymalizacja rakiety - main.menu.analyze.optimization.desc = Ogólna optymalizacja projektu rakiety + main.menu.tools = Analiza + main.menu.tools.componentAnalysis = Analiza cz\u0119\u015Bci + main.menu.tools.componentAnalysis.desc = Analizuj oddzielnie poszczególne cz\u0119\u015Bci sk\u0142adóe rakiety + main.menu.tools.optimization = Optymalizacja rakiety + main.menu.tools.optimization.desc = Ogólna optymalizacja projektu rakiety main.menu.help = Pomoc main.menu.help.desc = Informacje o programie OpenRocket @@ -1592,8 +1591,8 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. CustomFinImport.description = Obraz zostanie automatycznie zamieniony na czarno-bia\u0142y (statecznik w kolorze czarnym), wi\u0119c upewnij si\u0119, \u017Ce kszta\u0142t statecznika jest wype\u0142niony ciemnym kolorem, a t\u0142o jest bia\u0142e lub jasne. Podstawa statecznika musi przylega\u0107 do dolnej kraw\u0119dzi obrazu. - PresetModel.lbl.select = Wybierz ustawienia: - PresetModel.lbl.database = Z bazy danych... + PresetModel.lbl.custompreset = Niestandardowe + PresetModel.lbl.partsLib = Biblioteka cz??ci ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties index 04abc97fe..4bdb89578 100644 --- a/core/resources/l10n/messages_pt.properties +++ b/core/resources/l10n/messages_pt.properties @@ -715,8 +715,8 @@ PreferencesDialog.languages.default = Padr\u00e3o do sistema PreferencesDialog.lbl.language = Idioma da interface: PreferencesDialog.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket. -PresetModel.lbl.database = \u00c0 partir do banco de dados... -PresetModel.lbl.select = Selecione ajustes pr\u00e9-definidos +PresetModel.lbl.custompreset = Personalizado +PresetModel.lbl.partsLib = Biblioteca de pe\u00e7as PrintDialog.but.previewAndPrint = Visualiza\u00e7\u00e3o e impress\u00e3o PrintDialog.checkbox.showByStage = Mostrar por est\u00e1gio @@ -1386,14 +1386,13 @@ FileHelper.IMAGES = Arquivos de imagem # General file type names FileHelper.PDF_FILTER = Arquivos PDF (*.pdf) -main.menu.analyze = Analisar -main.menu.analyze.componentAnalysis = An\u00e1lise dos componentes -main.menu.analyze.componentAnalysis.desc = Analisar os componentes dos foguetes separadamente -main.menu.analyze.customExpressions = Express\u00f5es personalizadas -main.menu.analyze.customExpressions.desc = Definir novos tipos de dados de voo por escrito personalizados express\u00f5es matem\u00e1ticas -main.menu.analyze.desc = An\u00e1lise do foguete -main.menu.analyze.optimization = Otimiza\u00e7\u00e3o do foguete -main.menu.analyze.optimization.desc = Otimiza\u00e7\u00e3o do projeto geral do foguete +main.menu.tools = Analisar +main.menu.tools.componentAnalysis = An\u00e1lise dos componentes +main.menu.tools.componentAnalysis.desc = Analisar os componentes dos foguetes separadamente +main.menu.tools.customExpressions = Express\u00f5es personalizadas +main.menu.tools.customExpressions.desc = Definir novos tipos de dados de voo por escrito personalizados express\u00f5es matem\u00e1ticas +main.menu.tools.optimization = Otimiza\u00e7\u00e3o do foguete +main.menu.tools.optimization.desc = Otimiza\u00e7\u00e3o do projeto geral do foguete main.menu.debug = Depura\u00e7\u00e3o main.menu.debug.createtestrocket = Criar foguete de teste main.menu.debug.whatisthismenu = O que \u00e9 esse menu? diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 1688fc775..e2be1a52e 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -293,8 +293,10 @@ pref.dlg.lbl.launchWarning.ttip = \u0412\u044B \u043D\u0435 \u043F\u0435\u0440\u pref.dlg.lbl.Positiontoinsert = \u041C\u0435\u0441\u0442\u043E \u0434\u043B\u044F \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432: pref.dlg.lbl.PositiontoinsertStages = \u041C\u0435\u0441\u0442\u043E \u0434\u043B\u044F \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439: pref.dlg.lbl.Confirmdeletion = \u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0440\u0430\u0441\u0447\u0435\u0442\u0430: -pref.dlg.checkbox.Runsimulations = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432. +pref.dlg.checkbox.Runsimulations = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432 pref.dlg.checkbox.Updateestimates = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u0430\u0433\u0430\u0435\u043C\u044B\u0435 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u043F\u043E\u043B\u0435\u0442\u0430 \u0432 \u043E\u043A\u043D\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F +pref.dlg.checkbox.Markers = \u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u043E\u043F\u043E\u0440/\u0440\u0430\u0437\u0433\u043E\u043D\u043D\u044B\u0445 \u0431\u043B\u043E\u043A\u043E\u0432 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0432\u044B\u0431\u0440\u0430\u043D\u044B +pref.dlg.checkbox.Markers.ttip = \u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u043E\u043F\u043E\u0440/\u0440\u0430\u0437\u0433\u043E\u043D\u043D\u044B\u0445 \u0431\u043B\u043E\u043A\u043E\u0432 \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C\u0441\u044F, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
\u0412 \u043F\u0440\u043E\u0442\u0438\u0432\u043D\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435 \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u0432\u0441\u0435\u0433\u0434\u0430. pref.dlg.checkbox.AlwaysOpenLeftmost = \u0412\u0441\u0435\u0433\u0434\u0430 \u0432\u044B\u0431\u0438\u0440\u0430\u0442\u044C \u043F\u0435\u0440\u0432\u0443\u044E \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u043E\u043A\u043D\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = \u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u0435 \u043E\u043A\u043D\u043E \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u0441 \u043F\u0435\u0440\u0432\u043E\u0439 \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0439 \u0432\u043A\u043B\u0430\u0434\u043A\u043E\u0439.
\u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0430\u044F \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u0430\u044F \u0432\u043A\u043B\u0430\u0434\u043A\u0430. pref.dlg.lbl.User-definedthrust = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u043F\u0440\u043E\u0444\u0438\u043B\u0438 \u0442\u044F\u0433\u0438: @@ -716,7 +718,7 @@ compaddbuttons.Launchlug = \u041D\u0430\u043F\u0440\u0430\u0432\u043B\u044F\u044 compaddbuttons.RailButton = \u0417\u0430\u0446\u0435\u043F\n\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0449\u0435\u0439 compaddbuttons.InnerComponent = \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0435 \u0434\u0435\u0442\u0430\u043B\u0438 compaddbuttons.Innertube = \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F\n\u0442\u0440\u0443\u0431\u0430 -compaddbuttons.Coupler = \u041C\u0443\u0444\u0442\u0430 +compaddbuttons.Coupler = \u0422\u0440\u0443\u0431\u0447\u0430\u0442\u0430\u044F\n\u043C\u0443\u0444\u0442\u0430 compaddbuttons.Centeringring = \u0426\u0435\u043D\u0442\u0440\u0438\u0440\u0443\u044E\u0449\u0435\u0435\n\u043A\u043E\u043B\u044C\u0446\u043E compaddbuttons.Bulkhead = \u041F\u0435\u0440\u0435\u0431\u043E\u0440\u043A\u0430 compaddbuttons.Engineblock = \u0423\u043F\u043E\u0440\n\u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u044F @@ -898,6 +900,7 @@ RocketCompCfg.tab.Figstyleopt = \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440 RocketCompCfg.tab.Comment = \u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 RocketCompCfg.tab.Comment.ttip = \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 \u043A \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0443 RocketCompCfg.tab.Appearance = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0432\u0438\u0434 +RocketCompCfg.tab.Appearance.ttip = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0432\u0438\u0434 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 RocketCompCfg.lbl.Mass = \u041C\u0430\u0441\u0441\u0430: RocketCompCfg.lbl.Componentmass = \u041C\u0430\u0441\u0441\u0430 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430: RocketCompCfg.lbl.overriddento = (\u043F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043E \u043D\u0430 @@ -1050,7 +1053,10 @@ LaunchLugCfg.tab.Generalprop = \u041E\u0441\u043D\u043E\u0432\u043D\u044B\u0435 ! RailButtonConfig RailBtnCfg.lbl.OuterDiam = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0434\u0438\u0430\u043C\u0435\u0442\u0440: -RailBtnCfg.lbl.TotalHeight = \u041E\u0431\u0449\u0430\u044F \u0432\u044B\u0441\u043E\u0442\u0430 +RailBtnCfg.lbl.InnerDiam = \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0439 \u0434\u0438\u0430\u043C\u0435\u0442\u0440: +RailBtnCfg.lbl.TotalHeight = \u041E\u0431\u0449\u0430\u044F \u0432\u044B\u0441\u043E\u0442\u0430: +RailBtnCfg.lbl.BaseHeight = \u0412\u044B\u0441\u043E\u0442\u0430 \u043E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u044F: +RailBtnCfg.lbl.FlangeHeight = \u0412\u044B\u0441\u043E\u0442\u0430 \u0448\u043B\u044F\u043F\u043A\u0438:: RailBtnCfg.lbl.Angle = \u0423\u0433\u043E\u043B: RailBtnCfg.lbl.PosRelativeTo = \u041F\u043E\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u043E\u0442\u043D\u043E\u0441\u0438\u0442\u0435\u043B\u044C\u043D\u043E: RailBtnCfg.lbl.Plus = \u043F\u043B\u044E\u0441 @@ -1373,14 +1379,13 @@ main.menu.edit.editpreset = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0 main.menu.edit.preferences = \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 main.menu.edit.preferences.desc = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F -main.menu.analyze = \u0410\u043D\u0430\u043B\u0438\u0437 -main.menu.analyze.desc = \u0410\u043D\u0430\u043B\u0438\u0437 \u0440\u0430\u043A\u0435\u0442\u044B -main.menu.analyze.componentAnalysis = \u0410\u043D\u0430\u043B\u0438\u0437 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432 -main.menu.analyze.componentAnalysis.desc = \u0410\u043D\u0430\u043B\u0438\u0437 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432 \u0440\u0430\u043A\u0435\u0442\u044B \u043F\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438 -main.menu.analyze.optimization = \u041E\u043F\u0442\u0438\u043C\u0438\u0437\u0430\u0446\u0438\u044F \u0440\u0430\u043A\u0435\u0442\u044B -main.menu.analyze.optimization.desc = \u041E\u0431\u0449\u0430\u044F \u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0430\u0446\u0438\u044F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0440\u0430\u043A\u0435\u0442\u044B -main.menu.analyze.customExpressions = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u044F -main.menu.analyze.customExpressions.desc = \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u044B\u0445 \u0442\u0438\u043F\u043E\u0432 \u0434\u0430\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u0435\u0442\u0430 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u0438\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0445 \u043C\u0430\u0442\u0435\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0439 +main.menu.tools = \u0410\u043D\u0430\u043B\u0438\u0437 +main.menu.tools.componentAnalysis = \u0410\u043D\u0430\u043B\u0438\u0437 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432 +main.menu.tools.componentAnalysis.desc = \u0410\u043D\u0430\u043B\u0438\u0437 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432 \u0440\u0430\u043A\u0435\u0442\u044B \u043F\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438 +main.menu.tools.optimization = \u041E\u043F\u0442\u0438\u043C\u0438\u0437\u0430\u0446\u0438\u044F \u0440\u0430\u043A\u0435\u0442\u044B +main.menu.tools.optimization.desc = \u041E\u0431\u0449\u0430\u044F \u043E\u043F\u0442\u0438\u043C\u0438\u0437\u0430\u0446\u0438\u044F \u043F\u0440\u043E\u0435\u043A\u0442\u0430 \u0440\u0430\u043A\u0435\u0442\u044B +main.menu.tools.customExpressions = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u044F +main.menu.tools.customExpressions.desc = \u041E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u044B\u0445 \u0442\u0438\u043F\u043E\u0432 \u0434\u0430\u043D\u043D\u044B\u0445 \u043F\u043E\u043B\u0435\u0442\u0430 \u043D\u0430\u043F\u0438\u0441\u0430\u043D\u0438\u0435\u043C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0445 \u043C\u0430\u0442\u0435\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0439 main.menu.help = \u0421\u043F\u0440\u0430\u0432\u043A\u0430 main.menu.help.desc = \u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F \u043E\u0431 OpenRocket @@ -2075,8 +2080,10 @@ CustomFinImport.error.badimage = \u041D\u0435\u0432\u043E\u0437\u043C\u043E\u043 CustomFinImport.description = \u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044F \u0432 \u0447\u0435\u0440\u043D\u043E-\u0431\u0435\u043B\u043E\u0435 (\u0433\u0434\u0435 \u0447\u0435\u0440\u043D\u044B\u0439 - \u0446\u0432\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430), \u0442\u0430\u043A \u0447\u0442\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u043D\u044B\u0439 \u0446\u0432\u0435\u0442 \u0434\u043B\u044F \u0440\u0438\u0441\u0443\u043D\u043A\u0430 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430 \u0438 \u0431\u0435\u043B\u044B\u0439 \u0438\u043B\u0438 \u0441\u0432\u0435\u0442\u043B\u044B\u0439 \u0446\u0432\u0435\u0442 \u0434\u043B\u044F \u0444\u043E\u043D\u0430. \u041E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430 \u0434\u043E\u043B\u0436\u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C\u0441\u044F \u0441\u043D\u0438\u0437\u0443 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F. -PresetModel.lbl.select = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 -PresetModel.lbl.database = \u0418\u0437 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445... +PresetModel.combo.ttip = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u0445 (\u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0445 \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C \u043E\u043A\u043D\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043E\u043A \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432)
\u0438\u043B\u0438 \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435 "\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0439", \u0435\u0441\u043B\u0438 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F. +PresetModel.lbl.custompreset = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u0438\u0439 +PresetModel.lbl.partsLib = \u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u0434\u0435\u0442\u0430\u043B\u0435\u0439 +PresetModel.lbl.partsLib.ttip = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 \u0440\u0430\u043A\u0435\u0442\u044B \u0438\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u0435\u0442\u0430\u043B\u0435\u0439. DecalModel.lbl.select = <\u043D\u0435\u0442> DecalModel.lbl.choose = \u0418\u0437 \u0444\u0430\u0439\u043B\u0430... @@ -2098,7 +2105,7 @@ ComponentPresetChooserDialog.menu.sortDesc = \u041F\u043E \u0443\u0431\u044B\u04 ComponentPresetChooserDialog.menu.units = \u0415\u0434\u0438\u043D\u0438\u0446\u044B \u0438\u0437\u043C\u0435\u0440\u0435\u043D\u0438\u044F ComponentPresetChooserDialog.checkbox.showAllCompatible = \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432\u0441\u0435 \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u044B\u0435 ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0445 -ComponentPresetChooserDialog.lbl.favorites = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u043B\u044F \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0432 \u0432\u044B\u043F\u0430\u0434\u0430\u044E\u0449\u0435\u0435 \u043C\u0435\u043D\u044E +ComponentPresetChooserDialog.lbl.favorites = \u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A, \u0447\u0442\u043E\u0431\u044B \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0432 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0435\u0435\u0441\u044F \u043C\u0435\u043D\u044E \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043E\u043A \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C \u043E\u043A\u043D\u0435 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430. table.column.Favorite = \u0418\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0435 table.column.Legacy = \u0423\u0441\u0442\u0430\u0440\u0435\u043B\u043E table.column.Manufacturer = \u041F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C @@ -2115,7 +2122,7 @@ table.column.AftShoulderDiameter = \u0414\u0438\u0430\u043C\u0435\u0442\u0440 \u table.column.ForeShoulderLength = \u0414\u043B\u0438\u043D\u0430 \u043F\u0435\u0440\u0435\u0434\u043D\u0435\u0439 \u043C\u0443\u0444\u0442\u044B table.column.ForeShoulderDiameter = \u0414\u0438\u0430\u043C\u0435\u0442\u0440 \u043F\u0435\u0440\u0435\u0434\u043D\u0435\u0439 \u043C\u0443\u0444\u0442\u044B table.column.ForeOuterDiameter = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0434\u0438\u0430\u043C\u0435\u0442\u0440 \u043F\u0435\u0440\u0435\u0434\u043D\u0435\u0439 \u043C\u0443\u0444\u0442\u044B -table.column.StandoffHeight = \u0412\u044B\u0441\u043E\u0442\u0430 \u0437\u0430\u0437\u043E\u0440\u0430 +table.column.BaseHeight = \u0412\u044B\u0441\u043E\u0442\u0430 \u0437\u0430\u0437\u043E\u0440\u0430 table.column.FlangeHeight = \u0412\u044B\u0441\u043E\u0442\u0430 \u0444\u043B\u0430\u043D\u0446\u0430 table.column.Shape = \u0424\u043E\u0440\u043C\u0430 table.column.Material = \u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index c5da6e887..5c4eb0c59 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -1175,14 +1175,13 @@ main.menu.edit.editpreset= Edit Component Preset File main.menu.edit.preferences = Preferences main.menu.edit.preferences.desc = Setup the application preferences -main.menu.analyze = Tools -main.menu.analyze.desc = Rocket analysis -main.menu.analyze.componentAnalysis = Component analysis -main.menu.analyze.componentAnalysis.desc = Analyze the rocket components separately -main.menu.analyze.optimization = Rocket optimization -main.menu.analyze.optimization.desc = General rocket design optimization -main.menu.analyze.customExpressions = Custom expressions -main.menu.analyze.customExpressions.desc = Define new flight data types by writing custom mathematical expressions +main.menu.tools = Tools +main.menu.tools.componentAnalysis = Component analysis +main.menu.tools.componentAnalysis.desc = Analyze the rocket components separately +main.menu.tools.optimization = Rocket optimization +main.menu.tools.optimization.desc = General rocket design optimization +main.menu.tools.customExpressions = Custom expressions +main.menu.tools.customExpressions.desc = Define new flight data types by writing custom mathematical expressions main.menu.help = Help main.menu.help.desc = Information about OpenRocket @@ -1812,8 +1811,8 @@ CustomFinImport.error.badimage = Could not deduce fin shape from image. CustomFinImport.description = The image will be converted internally to black and white image (black for the fin), so make sure you use a solid dark color for the fin, and white or a light color for the background. The fin must be touching the bottom of the image, which is the base of the fin. -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database... +PresetModel.lbl.custompreset = Custom +PresetModel.lbl.partsLib = Parts Library DecalModel.lbl.select = DecalModel.lbl.choose = From file... diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties index b06908d10..6d98ddb49 100644 --- a/core/resources/l10n/messages_zh_CN.properties +++ b/core/resources/l10n/messages_zh_CN.properties @@ -803,8 +803,8 @@ PreferencesDialog.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4 PreferencesDialog.lbl.language = \u754C\u9762\u8BED\u8A00: PreferencesDialog.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548 -PresetModel.lbl.database = \u4ECE\u6570\u636E\u5E93... -PresetModel.lbl.select = \u9009\u62E9\u9884\u8BBE +PresetModel.lbl.custompreset = \u5b9a\u5236 +PresetModel.lbl.partsLib = \u96f6\u4ef6\u5e93 PrintDialog.but.previewAndPrint = \u9884\u89C8 & \u6253\u5370 PrintDialog.checkbox.showByStage = \u6309\u7EA7\u663E\u793A @@ -1509,14 +1509,13 @@ generalprefs.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4 generalprefs.lbl.language = \u754C\u9762\u8BED\u8A00 generalprefs.lbl.languageEffect = \u65B0\u7684\u8BED\u8A00\u5C06\u5728\u4E0B\u6B21\u542F\u52A8OpenRocket\u65F6\u751F\u6548. -main.menu.analyze = \u5206\u6790 -main.menu.analyze.componentAnalysis = \u7EC4\u4EF6\u5206\u6790 -main.menu.analyze.componentAnalysis.desc = \u4EC5\u5206\u6790\u706B\u7BAD\u90E8\u4EF6 -main.menu.analyze.customExpressions = \u81EA\u5B9A\u4E49\u8868\u8FBE\u5F0F -main.menu.analyze.customExpressions.desc = \u901A\u8FC7\u81EA\u5B9A\u4E49\u7684\u6570\u5B66\u8868\u8FBE\u5F0F\u6765\u5B9A\u4E49\u65B0\u7684\u98DE\u884C\u6570\u636E\u7C7B\u578B -main.menu.analyze.desc = \u706B\u7BAD\u5206\u6790 -main.menu.analyze.optimization = \u706B\u7BAD\u4F18\u5316 -main.menu.analyze.optimization.desc = \u5E38\u89C4\u706B\u7BAD\u8BBE\u8BA1\u4F18\u5316 +main.menu.tools = \u5206\u6790 +main.menu.tools.componentAnalysis = \u7EC4\u4EF6\u5206\u6790 +main.menu.tools.componentAnalysis.desc = \u4EC5\u5206\u6790\u706B\u7BAD\u90E8\u4EF6 +main.menu.tools.customExpressions = \u81EA\u5B9A\u4E49\u8868\u8FBE\u5F0F +main.menu.tools.customExpressions.desc = \u901A\u8FC7\u81EA\u5B9A\u4E49\u7684\u6570\u5B66\u8868\u8FBE\u5F0F\u6765\u5B9A\u4E49\u65B0\u7684\u98DE\u884C\u6570\u636E\u7C7B\u578B +main.menu.tools.optimization = \u706B\u7BAD\u4F18\u5316 +main.menu.tools.optimization.desc = \u5E38\u89C4\u706B\u7BAD\u8BBE\u8BA1\u4F18\u5316 main.menu.debug = \u8C03\u8BD5 main.menu.debug.createtestrocket = \u5EFA\u7ACB\u6D4B\u8BD5\u706B\u7BAD main.menu.debug.whatisthismenu = \u8FD9\u662F\u4EC0\u4E48\u83DC\u5355? diff --git a/core/resources/pix/componenticons/bulkhead-small.png b/core/resources/pix/componenticons/bulkhead-small.png index 78bbdb2b4..1d26860e9 100644 Binary files a/core/resources/pix/componenticons/bulkhead-small.png and b/core/resources/pix/componenticons/bulkhead-small.png differ diff --git a/core/resources/pix/componenticons/centeringring-small.png b/core/resources/pix/componenticons/centeringring-small.png index 5bd9b79b6..6b4a80aed 100644 Binary files a/core/resources/pix/componenticons/centeringring-small.png and b/core/resources/pix/componenticons/centeringring-small.png differ diff --git a/core/resources/pix/componenticons/ellipticalfin-small.png b/core/resources/pix/componenticons/ellipticalfin-small.png index 1abd29e4c..35180a0e3 100644 Binary files a/core/resources/pix/componenticons/ellipticalfin-small.png and b/core/resources/pix/componenticons/ellipticalfin-small.png differ diff --git a/core/resources/pix/componenticons/engineblock-large.png b/core/resources/pix/componenticons/engineblock-large.png index e58795c5e..1407bd5e4 100644 Binary files a/core/resources/pix/componenticons/engineblock-large.png and b/core/resources/pix/componenticons/engineblock-large.png differ diff --git a/core/resources/pix/componenticons/engineblock-small.png b/core/resources/pix/componenticons/engineblock-small.png index ec689da8d..c416b040e 100644 Binary files a/core/resources/pix/componenticons/engineblock-small.png and b/core/resources/pix/componenticons/engineblock-small.png differ diff --git a/core/resources/pix/componenticons/freeformfin-small.png b/core/resources/pix/componenticons/freeformfin-small.png index e9e9bc8dc..e8ce8ea02 100644 Binary files a/core/resources/pix/componenticons/freeformfin-small.png and b/core/resources/pix/componenticons/freeformfin-small.png differ diff --git a/core/resources/pix/componenticons/innertube-small.png b/core/resources/pix/componenticons/innertube-small.png index c6c448a90..5f65f3116 100644 Binary files a/core/resources/pix/componenticons/innertube-small.png and b/core/resources/pix/componenticons/innertube-small.png differ diff --git a/core/resources/pix/componenticons/mass-small.png b/core/resources/pix/componenticons/mass-small.png index fa5ba8eec..6104b8562 100644 Binary files a/core/resources/pix/componenticons/mass-small.png and b/core/resources/pix/componenticons/mass-small.png differ diff --git a/core/resources/pix/componenticons/nosecone-small.png b/core/resources/pix/componenticons/nosecone-small.png index f920c73d9..6cc18ff27 100644 Binary files a/core/resources/pix/componenticons/nosecone-small.png and b/core/resources/pix/componenticons/nosecone-small.png differ diff --git a/core/resources/pix/componenticons/parachute-small.png b/core/resources/pix/componenticons/parachute-small.png index eb4dea3ff..a3db68b2e 100644 Binary files a/core/resources/pix/componenticons/parachute-small.png and b/core/resources/pix/componenticons/parachute-small.png differ diff --git a/core/resources/pix/componenticons/pods-large.png b/core/resources/pix/componenticons/pods-large.png index a462c9193..f40055a4b 100644 Binary files a/core/resources/pix/componenticons/pods-large.png and b/core/resources/pix/componenticons/pods-large.png differ diff --git a/core/resources/pix/componenticons/pods-small.png b/core/resources/pix/componenticons/pods-small.png index 4deee6b90..17371e846 100644 Binary files a/core/resources/pix/componenticons/pods-small.png and b/core/resources/pix/componenticons/pods-small.png differ diff --git a/core/resources/pix/componenticons/railbutton-small.png b/core/resources/pix/componenticons/railbutton-small.png index 5adae70a7..3cc5d4b69 100644 Binary files a/core/resources/pix/componenticons/railbutton-small.png and b/core/resources/pix/componenticons/railbutton-small.png differ diff --git a/core/resources/pix/componenticons/shockcord-small.png b/core/resources/pix/componenticons/shockcord-small.png index 13ff97e37..653107120 100644 Binary files a/core/resources/pix/componenticons/shockcord-small.png and b/core/resources/pix/componenticons/shockcord-small.png differ diff --git a/core/resources/pix/componenticons/streamer-small.png b/core/resources/pix/componenticons/streamer-small.png index 317e46996..32fb68627 100644 Binary files a/core/resources/pix/componenticons/streamer-small.png and b/core/resources/pix/componenticons/streamer-small.png differ diff --git a/core/resources/pix/componenticons/transition-small.png b/core/resources/pix/componenticons/transition-small.png index da25735dc..6e1a8f7da 100644 Binary files a/core/resources/pix/componenticons/transition-small.png and b/core/resources/pix/componenticons/transition-small.png differ diff --git a/core/resources/pix/componenticons/trapezoidfin-small.png b/core/resources/pix/componenticons/trapezoidfin-small.png index db3c28872..c676e928f 100644 Binary files a/core/resources/pix/componenticons/trapezoidfin-small.png and b/core/resources/pix/componenticons/trapezoidfin-small.png differ diff --git a/core/resources/pix/componenticons/tubecoupler-small.png b/core/resources/pix/componenticons/tubecoupler-small.png index 7e08d674a..ef0e08653 100644 Binary files a/core/resources/pix/componenticons/tubecoupler-small.png and b/core/resources/pix/componenticons/tubecoupler-small.png differ diff --git a/core/resources/pix/componenticons/tubefin-large.png b/core/resources/pix/componenticons/tubefin-large.png index 2cc686d3f..0af76fd0e 100644 Binary files a/core/resources/pix/componenticons/tubefin-large.png and b/core/resources/pix/componenticons/tubefin-large.png differ diff --git a/core/resources/pix/componenticons/tubefin-small.png b/core/resources/pix/componenticons/tubefin-small.png index 01683c3ff..93baa9c96 100644 Binary files a/core/resources/pix/componenticons/tubefin-small.png and b/core/resources/pix/componenticons/tubefin-small.png differ diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index add6b3b7b..2df5909c9 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import net.sf.openrocket.aerodynamics.barrowman.FinSetCalc; import net.sf.openrocket.aerodynamics.barrowman.RocketComponentCalc; +import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; @@ -370,9 +371,10 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { for(Map.Entry> entry: imap.entrySet() ) { final RocketComponent c = entry.getKey(); - if (!c.isAerodynamic()) + if (!c.isAerodynamic()) { continue; - + } + // Handle Overriden CD for Whole Rocket if(c.isCDOverridden()) { continue; @@ -406,32 +408,30 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { componentCf = Math.max(Cf, roughnessLimited[finish.ordinal()]); } - - // iterate across component instances - final ArrayList contextList = entry.getValue(); - for(InstanceContext context: contextList ) { - double componentFrictionCD = calcMap.get(c).calculateFrictionCD(conditions, componentCf, warningSet); - if (c instanceof SymmetricComponent) { - SymmetricComponent s = (SymmetricComponent) c; - bodyFrictionCD += componentFrictionCD; + double componentFrictionCD = calcMap.get(c).calculateFrictionCD(conditions, componentCf, warningSet); - final double componentMinX = context.getLocation().x; - minX = Math.min(minX, componentMinX); + int instanceCount = entry.getValue().size(); + if (c instanceof SymmetricComponent) { + SymmetricComponent s = (SymmetricComponent) c; - final double componentMaxX = componentMinX + c.getLength(); - maxX = Math.max(maxX, componentMaxX); + bodyFrictionCD += instanceCount * componentFrictionCD; + + final double componentMinX = c.getAxialOffset(AxialMethod.ABSOLUTE); + minX = Math.min(minX, componentMinX); - final double componentMaxR = Math.max(s.getForeRadius(), s.getAftRadius()); - maxR = Math.max(maxR, componentMaxR); - - } else { - otherFrictionCD += componentFrictionCD; - } + final double componentMaxX = componentMinX + c.getLength(); + maxX = Math.max(maxX, componentMaxX); - if (map != null) { - map.get(c).setFrictionCD(componentFrictionCD); - } + final double componentMaxR = Math.max(s.getForeRadius(), s.getAftRadius()); + maxR = Math.max(maxR, componentMaxR); + + } else { + otherFrictionCD += instanceCount * componentFrictionCD; + } + + if (map != null) { + map.get(c).setFrictionCD(componentFrictionCD); } } @@ -447,7 +447,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { } } } - + return otherFrictionCD + correction * bodyFrictionCD; } @@ -590,57 +590,58 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { */ private double calculatePressureCD(FlightConfiguration configuration, FlightConditions conditions, Map forceMap, WarningSet warningSet) { - - double stagnation, base, total; - + + double total, stagnation, base; if (calcMap == null) buildCalcMap(configuration); stagnation = calculateStagnationCD(conditions.getMach()); base = calculateBaseCD(conditions.getMach()); - + total = 0; final InstanceMap imap = configuration.getActiveInstances(); for(Map.Entry> entry: imap.entrySet() ) { final RocketComponent c = entry.getKey(); - if (!c.isAerodynamic()) + if (!c.isAerodynamic()) { continue; + } + + if(c.isCDOverridden()) { + continue; + } + + int instanceCount = entry.getValue().size(); - // iterate across component instances - final ArrayList contextList = entry.getValue(); - for(InstanceContext context: contextList ) { - - // Pressure drag - double cd = calcMap.get(c).calculatePressureCD(conditions, stagnation, base, + // Pressure drag of this component + double cd = calcMap.get(c).calculatePressureCD(conditions, stagnation, base, warningSet); - total += cd; + + if (forceMap != null) { + forceMap.get(c).setPressureCD(cd); + } - if (forceMap != null) { - forceMap.get(c).setPressureCD(cd); - } + total += cd * instanceCount; + + // Stagnation drag caused by difference in radius between this component + // and previous component (increasing radii. Decreasing radii handled in + // base drag calculation + if (c instanceof SymmetricComponent) { + SymmetricComponent s = (SymmetricComponent) c; - if(c.isCDOverridden()) - continue; - - // Stagnation drag - if (c instanceof SymmetricComponent) { - SymmetricComponent s = (SymmetricComponent) c; - - double radius = 0; - final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); - if (prevComponent != null && configuration.isComponentActive(prevComponent)) - radius = prevComponent.getAftRadius(); + double radius = 0; + final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); + if (prevComponent != null && configuration.isComponentActive(prevComponent)) + radius = prevComponent.getAftRadius(); - if (radius < s.getForeRadius()) { - double area = Math.PI * (pow2(s.getForeRadius()) - pow2(radius)); - cd = stagnation * area / conditions.getRefArea(); - total += cd; + if (radius < s.getForeRadius()) { + double area = Math.PI * (pow2(s.getForeRadius()) - pow2(radius)); + cd = stagnation * area / conditions.getRefArea(); + total += instanceCount * cd; if (forceMap != null) { forceMap.get(c).setPressureCD(forceMap.get(c).getPressureCD() + cd); } - } } } } @@ -673,47 +674,46 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { for(Map.Entry> entry: imap.entrySet() ) { final RocketComponent c = entry.getKey(); - if (!(c instanceof SymmetricComponent)) + if (!(c instanceof SymmetricComponent)) { continue; + } SymmetricComponent s = (SymmetricComponent) c; + + int instanceCount = entry.getValue().size(); - // iterate across component instances - final ArrayList contextList = entry.getValue(); - for(InstanceContext context: contextList ) { - if(c.isCDOverridden()) { - total += c.getOverrideCD(); - continue; - } + if(c.isCDOverridden()) { + total += instanceCount * c.getOverrideCD(); + continue; + } - // if aft radius of previous component is greater than my forward radius, set - // its aft CD - double radius = 0; - final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); - if (prevComponent != null && configuration.isComponentActive(prevComponent)) { - radius = prevComponent.getAftRadius(); + // if aft radius of previous component is greater than my forward radius, set + // its aft CD + double radius = 0; + final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); + if (prevComponent != null && configuration.isComponentActive(prevComponent)) { + radius = prevComponent.getAftRadius(); + } + + if (radius > s.getForeRadius()) { + double area = Math.PI * (pow2(radius) - pow2(s.getForeRadius())); + double cd = base * area / conditions.getRefArea(); + total += instanceCount * cd; + if ((map != null) && (prevComponent != null)) { + map.get(prevComponent).setBaseCD(cd); } + } - if (radius > s.getForeRadius()) { - double area = Math.PI * (pow2(radius) - pow2(s.getForeRadius())); - double cd = base * area / conditions.getRefArea(); - total += cd; - if ((map != null) && (prevComponent != null)) { - map.get(prevComponent).setBaseCD(cd); - } - } - - // if I'm the last component, set my base CD - // note: the iterator *should* serve up the next component.... buuuut .... - // this code has is tested, and there's no compelling reason to change. - final SymmetricComponent n = s.getNextSymmetricComponent(); - if ((n == null) || !configuration.isStageActive(n.getStageNumber())) { - double area = Math.PI * pow2(s.getAftRadius()); - double cd = base * area / conditions.getRefArea(); - total += cd; - if (map != null) { - map.get(s).setBaseCD(cd); - } + // if I'm the last component, set my base CD + // note: the iterator *should* serve up the next component.... buuuut .... + // this code has is tested, and there's no compelling reason to change. + final SymmetricComponent n = s.getNextSymmetricComponent(); + if ((n == null) || !configuration.isStageActive(n.getStageNumber())) { + double area = Math.PI * pow2(s.getAftRadius()); + double cd = base * area / conditions.getRefArea(); + total += instanceCount * cd; + if (map != null) { + map.get(s).setBaseCD(cd); } } } @@ -885,8 +885,9 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { calcMap = new HashMap<>(); for (RocketComponent comp: configuration.getAllComponents()) { - if (!comp.isAerodynamic()) + if (!comp.isAerodynamic()) { continue; + } RocketComponentCalc calcObj = (RocketComponentCalc) Reflection.construct(BARROWMAN_PACKAGE, comp, BARROWMAN_SUFFIX, comp); diff --git a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java index 16f733ec9..da4978cd4 100644 --- a/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java +++ b/core/src/net/sf/openrocket/file/openrocket/importt/DocumentConfig.java @@ -190,8 +190,15 @@ class DocumentConfig { setters.put("RailButton:angleoffset", new AnglePositionSetter() ); setters.put("RailButton:height", new DoubleSetter( Reflection.findMethod( RailButton.class, "setTotalHeight", double.class))); + setters.put("RailButton:baseheight", new DoubleSetter( + Reflection.findMethod( RailButton.class, "setBaseHeight", double.class))); + setters.put("RailButton:flangeheight", new DoubleSetter( + Reflection.findMethod( RailButton.class, "setFlangeHeight", double.class))); setters.put("RailButton:outerdiameter", new DoubleSetter( Reflection.findMethod( RailButton.class, "setOuterDiameter", double.class))); + setters.put("RailButton:innerdiameter", new DoubleSetter( + Reflection.findMethod( RailButton.class, "setInnerDiameter", double.class))); + // Transition setters.put("Transition:shape", new EnumSetter( diff --git a/core/src/net/sf/openrocket/file/openrocket/savers/RailButtonSaver.java b/core/src/net/sf/openrocket/file/openrocket/savers/RailButtonSaver.java index 74a9c0638..73db68866 100644 --- a/core/src/net/sf/openrocket/file/openrocket/savers/RailButtonSaver.java +++ b/core/src/net/sf/openrocket/file/openrocket/savers/RailButtonSaver.java @@ -26,8 +26,10 @@ public class RailButtonSaver extends ExternalComponentSaver { RailButton rb = (RailButton) c; emitDouble( elements, "outerdiameter", rb.getOuterDiameter()); + emitDouble( elements, "innerdiameter", rb.getInnerDiameter()); emitDouble( elements, "height", rb.getTotalHeight()); - + emitDouble( elements, "baseheight", rb.getBaseHeight()); + emitDouble( elements, "flangeheight", rb.getFlangeHeight()); } diff --git a/core/src/net/sf/openrocket/preset/ComponentPreset.java b/core/src/net/sf/openrocket/preset/ComponentPreset.java index ecc28441a..b92c4fcaa 100644 --- a/core/src/net/sf/openrocket/preset/ComponentPreset.java +++ b/core/src/net/sf/openrocket/preset/ComponentPreset.java @@ -129,12 +129,16 @@ public class ComponentPreset implements Comparable, Serializabl ComponentPreset.MANUFACTURER, ComponentPreset.PARTNO, ComponentPreset.DESCRIPTION, - // these are optional / secondary parameters. Probably not necessary to include. - //ComponentPreset.BASE_HEIGHT, - //ComponentPreset.FLANGE_HEIGHT, - //ComponentPreset.INNER_DIAMETER, + ComponentPreset.BASE_HEIGHT, + ComponentPreset.FLANGE_HEIGHT, + //ComponentPreset.SCREW_HEIGHT, // Add this later when we implement screws in the rail button + ComponentPreset.HEIGHT, + ComponentPreset.INNER_DIAMETER, ComponentPreset.OUTER_DIAMETER, - ComponentPreset.HEIGHT }), + ComponentPreset.MASS, + ComponentPreset.SCREW_MASS, + ComponentPreset.NUT_MASS, + ComponentPreset.CD }), STREAMER(new TypedKey[] { ComponentPreset.LEGACY, @@ -160,7 +164,7 @@ public class ComponentPreset implements Comparable, Serializabl ComponentPreset.LINE_COUNT, ComponentPreset.LINE_LENGTH, ComponentPreset.LINE_MATERIAL, - ComponentPreset.PARACHUTE_CD, + ComponentPreset.CD, ComponentPreset.PACKED_DIAMETER, ComponentPreset.PACKED_LENGTH }); @@ -206,6 +210,7 @@ public class ComponentPreset implements Comparable, Serializabl public final static TypedKey AFT_SHOULDER_LENGTH = new TypedKey("AftShoulderLength", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey AFT_SHOULDER_DIAMETER = new TypedKey("AftShoulderDiameter", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey AFT_OUTER_DIAMETER = new TypedKey("AftOuterDiameter", Double.class, UnitGroup.UNITS_LENGTH); + public static final TypedKey CD = new TypedKey("DragCoefficient", Double.class, UnitGroup.UNITS_COEFFICIENT); public final static TypedKey SHAPE = new TypedKey("Shape", Shape.class); public final static TypedKey MATERIAL = new TypedKey("Material", Material.class); public final static TypedKey FINISH = new TypedKey("Finish", Finish.class); @@ -214,8 +219,13 @@ public class ComponentPreset implements Comparable, Serializabl public final static TypedKey MASS = new TypedKey("Mass", Double.class, UnitGroup.UNITS_MASS); public final static TypedKey DIAMETER = new TypedKey("Diameter", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey IMAGE = new TypedKey("Image", byte[].class); - public final static TypedKey STANDOFF_HEIGHT = new TypedKey("StandoffHeight", Double.class, UnitGroup.UNITS_LENGTH); + + // RAIL BUTTON SPECIFIC + public final static TypedKey BASE_HEIGHT = new TypedKey("BaseHeight", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey FLANGE_HEIGHT = new TypedKey("FlangeHeight", Double.class, UnitGroup.UNITS_LENGTH); + public final static TypedKey SCREW_HEIGHT = new TypedKey("ScrewHeight", Double.class, UnitGroup.UNITS_LENGTH); + public final static TypedKey SCREW_MASS = new TypedKey("ScrewMass", Double.class, UnitGroup.UNITS_MASS); + public final static TypedKey NUT_MASS = new TypedKey("NutMass", Double.class, UnitGroup.UNITS_MASS); // PARACHUTE SPECIFIC // Parachute Manufacturer declaration see: MANUFACTURER @@ -225,7 +235,7 @@ public class ComponentPreset implements Comparable, Serializabl // Parachute diameter declaration see: DIAMETER public final static TypedKey SPILL_DIA = new TypedKey("SpillDia", Double.class, UnitGroup.UNITS_LENGTH); public final static TypedKey SURFACE_AREA = new TypedKey("SurfaceArea", Double.class, UnitGroup.UNITS_LENGTH); - public static final TypedKey PARACHUTE_CD = new TypedKey("DragCoefficient", Double.class, UnitGroup.UNITS_COEFFICIENT); + // Parachute canopy material declaration see: MATERIAL public final static TypedKey SIDES = new TypedKey("Sides", Integer.class); public final static TypedKey LINE_COUNT = new TypedKey("LineCount", Integer.class); @@ -251,8 +261,9 @@ public class ComponentPreset implements Comparable, Serializabl AFT_SHOULDER_LENGTH, FORE_SHOULDER_DIAMETER, FORE_SHOULDER_LENGTH, - STANDOFF_HEIGHT, + BASE_HEIGHT, FLANGE_HEIGHT, + SCREW_HEIGHT, SHAPE, THICKNESS, FILLED, @@ -262,6 +273,8 @@ public class ComponentPreset implements Comparable, Serializabl LINE_LENGTH, LINE_MATERIAL, MASS, + SCREW_MASS, + NUT_MASS, FINISH, MATERIAL )); @@ -411,6 +424,9 @@ public class ComponentPreset implements Comparable, Serializabl os.writeDouble(d); } else if (key.getType() == String.class) { String s = (String) value; + if (s == null) { + s = ""; + } os.writeBytes(s); } else if (key.getType() == Manufacturer.class) { String s = ((Manufacturer) value).getSimpleName(); diff --git a/core/src/net/sf/openrocket/preset/ComponentPresetFactory.java b/core/src/net/sf/openrocket/preset/ComponentPresetFactory.java index 77ecc3691..26cb79696 100644 --- a/core/src/net/sf/openrocket/preset/ComponentPresetFactory.java +++ b/core/src/net/sf/openrocket/preset/ComponentPresetFactory.java @@ -113,13 +113,13 @@ public abstract class ComponentPresetFactory { } - private static void makeRailButton(InvalidComponentPresetException exceptions, ComponentPreset preset) throws InvalidComponentPresetException { + private static void makeRailButton(InvalidComponentPresetException exceptions, ComponentPreset preset) { checkRequiredFields(exceptions, preset, HEIGHT); checkRequiredFields(exceptions, preset, OUTER_DIAMETER); checkRequiredFields(exceptions, preset, INNER_DIAMETER); checkRequiredFields(exceptions, preset, FLANGE_HEIGHT); - checkRequiredFields(exceptions, preset, STANDOFF_HEIGHT); + checkRequiredFields(exceptions, preset, BASE_HEIGHT); if (preset.has(MASS)) { double mass = preset.get(MASS); @@ -271,7 +271,6 @@ public abstract class ComponentPresetFactory { if (hasOd) { outerRadius = preset.get(OUTER_DIAMETER) / 2.0; - thickness = 0; if (hasId) { innerRadius = preset.get(INNER_DIAMETER) / 2.0; thickness = outerRadius - innerRadius; diff --git a/core/src/net/sf/openrocket/preset/loader/RailButtonLoader.java b/core/src/net/sf/openrocket/preset/loader/RailButtonLoader.java index 5980ed0c5..7c92b6c75 100644 --- a/core/src/net/sf/openrocket/preset/loader/RailButtonLoader.java +++ b/core/src/net/sf/openrocket/preset/loader/RailButtonLoader.java @@ -13,7 +13,7 @@ public class RailButtonLoader extends BaseComponentLoader { fileColumns.add(new DoubleUnitColumnParser("OD","Units",ComponentPreset.OUTER_DIAMETER)); fileColumns.add(new DoubleUnitColumnParser("Height","Units",ComponentPreset.HEIGHT)); fileColumns.add(new DoubleUnitColumnParser("Flange Height", "Units", ComponentPreset.FLANGE_HEIGHT)); - fileColumns.add(new DoubleUnitColumnParser("Standoff Height", "Units", ComponentPreset.STANDOFF_HEIGHT)); + fileColumns.add(new DoubleUnitColumnParser("Standoff Height", "Units", ComponentPreset.BASE_HEIGHT)); } diff --git a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentLoader.java b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentLoader.java index 9cbeced10..32bfc25d0 100644 --- a/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentLoader.java +++ b/core/src/net/sf/openrocket/preset/xml/OpenRocketComponentLoader.java @@ -34,9 +34,7 @@ public class OpenRocketComponentLoader implements Loader { presets = (new OpenRocketComponentSaver().unmarshalFromOpenRocketComponent( new InputStreamReader (stream))).asComponentPresets(); log.debug("ComponentPreset file " + filename + " contained " + presets.size() + " presets"); return presets; - } catch (JAXBException e) { - throw new BugException("Unable to parse file: "+ filename, e); - } catch (InvalidComponentPresetException e) { + } catch (JAXBException | InvalidComponentPresetException e) { throw new BugException("Unable to parse file: "+ filename, e); } diff --git a/core/src/net/sf/openrocket/preset/xml/ParachuteDTO.java b/core/src/net/sf/openrocket/preset/xml/ParachuteDTO.java index 6ebd20e86..844c8372e 100644 --- a/core/src/net/sf/openrocket/preset/xml/ParachuteDTO.java +++ b/core/src/net/sf/openrocket/preset/xml/ParachuteDTO.java @@ -146,8 +146,8 @@ public class ParachuteDTO extends BaseComponentDTO { if ( preset.has(ComponentPreset.PACKED_LENGTH)) { setPackedLength(preset.get(ComponentPreset.PACKED_LENGTH)); } - if ( preset.has(ComponentPreset.PARACHUTE_CD)) { - setDragCoefficient(preset.get(ComponentPreset.PARACHUTE_CD)); + if ( preset.has(ComponentPreset.CD)) { + setDragCoefficient(preset.get(ComponentPreset.CD)); } if ( preset.has(ComponentPreset.LINE_MATERIAL)) { setLineMaterial(new AnnotatedMaterialDTO(preset.get(ComponentPreset.LINE_MATERIAL))); @@ -174,7 +174,7 @@ public class ParachuteDTO extends BaseComponentDTO { props.put(ComponentPreset.PACKED_LENGTH, this.getPackedLength()); } if ( this.dragCoefficient != null ) { - props.put(ComponentPreset.PARACHUTE_CD, this.getDragCoefficient()); + props.put(ComponentPreset.CD, this.getDragCoefficient()); } props.put(ComponentPreset.LINE_COUNT, this.getLineCount()); if ( this.lineLength != null ) { diff --git a/core/src/net/sf/openrocket/preset/xml/RailButtonDTO.java b/core/src/net/sf/openrocket/preset/xml/RailButtonDTO.java index aa4f37bdc..fdf025aff 100644 --- a/core/src/net/sf/openrocket/preset/xml/RailButtonDTO.java +++ b/core/src/net/sf/openrocket/preset/xml/RailButtonDTO.java @@ -19,16 +19,22 @@ import java.util.List; @XmlAccessorType(XmlAccessType.FIELD) public class RailButtonDTO extends BaseComponentDTO { - @XmlElement(name = "InsideDiameter") - private AnnotatedLengthDTO insideDiameter; - @XmlElement(name = "OutsideDiameter") - private AnnotatedLengthDTO outsideDiameter; + @XmlElement(name = "InnerDiameter") + private AnnotatedLengthDTO innerDiameter; + @XmlElement(name = "OuterDiameter") + private AnnotatedLengthDTO outerDiameter; @XmlElement(name = "Height") private AnnotatedLengthDTO height; - @XmlElement(name = "StandoffHeight") - private AnnotatedLengthDTO standoffHeight; + @XmlElement(name = "BaseHeight") + private AnnotatedLengthDTO baseHeight; @XmlElement(name = "FlangeHeight") private AnnotatedLengthDTO flangeHeight; + @XmlElement(name = "ScrewHeight") + private AnnotatedLengthDTO screwHeight; + @XmlElement(name = "ScrewMass") + private AnnotatedMassDTO screwMass; + @XmlElement(name = "NutMass") + private AnnotatedMassDTO nutMass; /** * Default constructor. @@ -48,32 +54,35 @@ public class RailButtonDTO extends BaseComponentDTO { setInsideDiameter(preset.get(ComponentPreset.INNER_DIAMETER)); setOutsideDiameter(preset.get(ComponentPreset.OUTER_DIAMETER)); setHeight(preset.get(ComponentPreset.HEIGHT)); - setStandoffHeight(preset.get(ComponentPreset.STANDOFF_HEIGHT)); + setBaseHeight(preset.get(ComponentPreset.BASE_HEIGHT)); setFlangeHeight(preset.get(ComponentPreset.FLANGE_HEIGHT)); + setScrewHeight(preset.get(ComponentPreset.SCREW_HEIGHT)); + setScrewMass(preset.get(ComponentPreset.SCREW_MASS)); + setNutMass(preset.get(ComponentPreset.NUT_MASS)); } - public double getInsideDiameter() { - return insideDiameter.getValue(); + public double getInnerDiameter() { + return innerDiameter.getValue(); } - public void setInsideDiameter( final AnnotatedLengthDTO theLength ) { - insideDiameter = theLength; + public void setInnerDiameter(final AnnotatedLengthDTO theLength ) { + innerDiameter = theLength; } public void setInsideDiameter(final double theId) { - insideDiameter = new AnnotatedLengthDTO(theId); + innerDiameter = new AnnotatedLengthDTO(theId); } - public double getOutsideDiameter() { - return outsideDiameter.getValue(); + public double getOuterDiameter() { + return outerDiameter.getValue(); } - public void setOutsideDiameter(final AnnotatedLengthDTO theOd) { - outsideDiameter = theOd; + public void setOuterDiameter(final AnnotatedLengthDTO theOd) { + outerDiameter = theOd; } public void setOutsideDiameter(final double theOd) { - outsideDiameter = new AnnotatedLengthDTO(theOd); + outerDiameter = new AnnotatedLengthDTO(theOd); } public double getHeight() { @@ -88,16 +97,12 @@ public class RailButtonDTO extends BaseComponentDTO { height = new AnnotatedLengthDTO(theHeight); } - public double getStandoffHeight() { - return standoffHeight.getValue(); + public double getBaseHeight() { + return baseHeight.getValue(); } - public void setStandoffHeight(final AnnotatedLengthDTO theStandoffHeight) { - standoffHeight = theStandoffHeight; - } - - public void setStandoffHeight(final double theStandoffHeight) { - standoffHeight = new AnnotatedLengthDTO(theStandoffHeight); + public void setBaseHeight(final double theBaseHeight) { + baseHeight = new AnnotatedLengthDTO(theBaseHeight); } public double getFlangeHeight() { @@ -112,6 +117,30 @@ public class RailButtonDTO extends BaseComponentDTO { flangeHeight = new AnnotatedLengthDTO(theFlangeHeight); } + public double getScrewHeight() { + return screwHeight.getValue(); + } + + public void setScrewHeight(final double screwHeight) { + this.screwHeight = new AnnotatedLengthDTO(screwHeight); + } + + public double getScrewMass() { + return screwMass.getValue(); + } + + public void setScrewMass(double screwMass) { + this.screwMass = new AnnotatedMassDTO(screwMass); + } + + public double getNutMass() { + return nutMass.getValue(); + } + + public void setNutMass(double nutMass) { + this.nutMass = new AnnotatedMassDTO(nutMass); + } + @Override public ComponentPreset asComponentPreset(Boolean legacy, java.util.List materials) throws InvalidComponentPresetException { return asComponentPreset(legacy, ComponentPreset.Type.RAIL_BUTTON, materials); @@ -121,11 +150,14 @@ public class RailButtonDTO extends BaseComponentDTO { TypedPropertyMap props = new TypedPropertyMap(); props.put(ComponentPreset.LEGACY, legacy); addProps(props, materials); - props.put(ComponentPreset.INNER_DIAMETER, this.getInsideDiameter()); - props.put(ComponentPreset.OUTER_DIAMETER, this.getOutsideDiameter()); + props.put(ComponentPreset.INNER_DIAMETER, this.getInnerDiameter()); + props.put(ComponentPreset.OUTER_DIAMETER, this.getOuterDiameter()); props.put(ComponentPreset.HEIGHT, this.getHeight()); - props.put(ComponentPreset.STANDOFF_HEIGHT, this.getStandoffHeight()); + props.put(ComponentPreset.BASE_HEIGHT, this.getBaseHeight()); props.put(ComponentPreset.FLANGE_HEIGHT, this.getFlangeHeight()); + props.put(ComponentPreset.SCREW_HEIGHT, this.getScrewHeight()); + props.put(ComponentPreset.SCREW_MASS, this.getScrewMass()); + props.put(ComponentPreset.NUT_MASS, this.getNutMass()); props.put(ComponentPreset.TYPE, type); return ComponentPresetFactory.create(props); diff --git a/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java b/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java index c97a04ce7..ac2aeaf96 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ExternalComponent.java @@ -144,7 +144,9 @@ public abstract class ExternalComponent extends RocketComponent { protected void loadFromPreset(ComponentPreset preset) { super.loadFromPreset(preset); - // Surface finish is left unchanged + if (preset.has(ComponentPreset.FINISH)) { + setFinish(preset.get(ComponentPreset.FINISH)); + } if (preset.has(ComponentPreset.MATERIAL)) { Material mat = preset.get(ComponentPreset.MATERIAL); diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 93ed781b3..79dc0c4a3 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -227,8 +227,8 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona if (MathUtil.equals(clampedCant, this.cantRadians)) return; this.cantRadians = clampedCant; - - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + + fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE); } public Transformation getCantRotation() { diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 7bb3cfae3..d8f21cbc9 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,7 +63,7 @@ public class FlightConfiguration implements FlightConfigurableParameter stages = new HashMap(); // Map of stage number to StageFlags of the corresponding stage final protected Map motors = new HashMap(); - final private Collection activeMotors = new ArrayList(); + final private Collection activeMotors = new ConcurrentLinkedQueue(); final private InstanceMap activeInstances = new InstanceMap(); private int boundsModID = -1; @@ -225,8 +226,7 @@ public class FlightConfiguration implements FlightConfigurableParameter * */ -public class InstanceMap extends HashMap> { +public class InstanceMap extends ConcurrentHashMap> { // =========== Public Functions ======================== diff --git a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java index 12d152c8e..48804f2b8 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java +++ b/core/src/net/sf/openrocket/rocketcomponent/LaunchLug.java @@ -2,10 +2,7 @@ package net.sf.openrocket.rocketcomponent; import java.util.ArrayList; import java.util.Collection; -import java.util.EventObject; -import net.sf.openrocket.appearance.Appearance; -import net.sf.openrocket.appearance.Decal; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset.Type; @@ -14,7 +11,6 @@ import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.MathUtil; -import net.sf.openrocket.util.StateChangeListener; public class LaunchLug extends Tube implements AnglePositionable, BoxBounded, LineInstanceable, InsideColorComponent { diff --git a/core/src/net/sf/openrocket/rocketcomponent/Parachute.java b/core/src/net/sf/openrocket/rocketcomponent/Parachute.java index 28174f894..8a2fbf43b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Parachute.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Parachute.java @@ -178,9 +178,9 @@ public class Parachute extends RecoveryDevice { this.diameter = DEFAULT_DIAMETER; } // // Set preset parachute drag coefficient - if ((preset.has(ComponentPreset.PARACHUTE_CD)) && preset.get(ComponentPreset.PARACHUTE_CD) > 0){ + if ((preset.has(ComponentPreset.CD)) && preset.get(ComponentPreset.CD) > 0){ cdAutomatic = false; - cd = preset.get(ComponentPreset.PARACHUTE_CD); + cd = preset.get(ComponentPreset.CD); } else { cdAutomatic = true; cd = Parachute.DEFAULT_CD; diff --git a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java index a5b0e08fa..794e639f1 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RailButton.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RailButton.java @@ -40,19 +40,19 @@ public class RailButton extends ExternalComponent implements AnglePositionable, * ^ [[[[[[]]]]]] flangeHeight * total >||||||<= inner dia ^ * height |||||| v - * v [[[[[[]]]]]] standoff == baseHeight + * v [[[[[[]]]]]] baseHeight / standoff * ================== ^ * (body) * */ // Note: the reference point for Rail Button Components is in the center bottom of the button. protected double outerDiameter_m; - protected double totalHeight_m; protected double innerDiameter_m; + protected double totalHeight_m; protected double flangeHeight_m; - protected double standoff_m; - - protected final static double MINIMUM_STANDOFF= 0.001; + protected double baseHeight_m; + protected double screwHeight_m; // This has no effect at the moment; is for future use. + private double radialDistance_m=0; protected static final AngleMethod angleMethod = AngleMethod.RELATIVE; @@ -66,7 +66,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, this.totalHeight_m = 0.0097; this.innerDiameter_m = 0.008; this.flangeHeight_m = 0.002; - this.setStandoff( 0.002); + this.setBaseHeight(0.002); this.setInstanceSeparation( this.outerDiameter_m * 6); this.setMaterial(Databases.findMaterial(Material.Type.BULK, "Delrin")); super.displayOrder_side = 14; // Order for displaying the component in the 2D side view @@ -75,19 +75,19 @@ public class RailButton extends ExternalComponent implements AnglePositionable, public RailButton( final double od, final double ht ) { this(); - this.setOuterDiameter( od); - this.setTotalHeight( ht); + this.setOuterDiameter(od); + this.setTotalHeight(ht); super.displayOrder_side = 14; // Order for displaying the component in the 2D side view super.displayOrder_back = 11; // Order for displaying the component in the 2D back view } - public RailButton( final double od, final double id, final double ht, final double flangeThickness, final double _standoff ) { + public RailButton( final double od, final double id, final double ht, final double _flangeHeight, final double _baseHeight ) { super(AxialMethod.MIDDLE); this.outerDiameter_m = od; this.totalHeight_m = ht; this.innerDiameter_m = id; - this.flangeHeight_m = flangeThickness; - this.setStandoff( _standoff); + this.flangeHeight_m = _flangeHeight; + this.setBaseHeight(_baseHeight); this.setInstanceSeparation( od*2); this.setMaterial(Databases.findMaterial(Material.Type.BULK, "Delrin")); super.displayOrder_side = 14; // Order for displaying the component in the 2D side view @@ -121,13 +121,9 @@ public class RailButton extends ExternalComponent implements AnglePositionable, return rb1010; } - - public double getStandoff(){ - return this.standoff_m; - } public double getBaseHeight(){ - return this.getStandoff(); + return this.baseHeight_m; } public double getOuterDiameter() { @@ -139,7 +135,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, } public double getInnerHeight() { - return (this.totalHeight_m - this.flangeHeight_m - this.standoff_m); + return (this.totalHeight_m - this.flangeHeight_m - this.baseHeight_m); } public double getTotalHeight() { @@ -149,16 +145,48 @@ public class RailButton extends ExternalComponent implements AnglePositionable, public double getFlangeHeight() { return this.flangeHeight_m; } + + public double getScrewHeight() { + return this.screwHeight_m; + } + - - public void setStandoff(double newStandoff){ + public void setBaseHeight(double newBaseHeight){ for (RocketComponent listener : configListeners) { if (listener instanceof RailButton) { - ((RailButton) listener).setStandoff(newStandoff); + ((RailButton) listener).setBaseHeight(newBaseHeight); } } - this.standoff_m = Math.max( newStandoff, RailButton.MINIMUM_STANDOFF ); + this.baseHeight_m = Math.max(newBaseHeight, 0); + this.baseHeight_m = Math.min(this.baseHeight_m, this.totalHeight_m - this.flangeHeight_m); + clearPreset(); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + } + + public void setFlangeHeight(double newFlangeHeight){ + for (RocketComponent listener : configListeners) { + if (listener instanceof RailButton) { + ((RailButton) listener).setFlangeHeight(newFlangeHeight); + } + } + + this.flangeHeight_m = Math.max(newFlangeHeight, 0); + this.flangeHeight_m = Math.min(this.flangeHeight_m, this.totalHeight_m - this.baseHeight_m); + clearPreset(); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + } + + public void setScrewHeight(double height) { + for (RocketComponent listener : configListeners) { + if (listener instanceof RailButton) { + ((RailButton) listener).setScrewHeight(height); + } + } + + this.screwHeight_m = Math.max(height, 0); + clearPreset(); + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } public void setInnerDiameter(double newID ){ @@ -168,7 +196,8 @@ public class RailButton extends ExternalComponent implements AnglePositionable, } } - this.innerDiameter_m = newID; + this.innerDiameter_m = Math.min(newID, this.outerDiameter_m); + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -181,7 +210,9 @@ public class RailButton extends ExternalComponent implements AnglePositionable, } this.outerDiameter_m = newOD; + setInnerDiameter(this.innerDiameter_m); + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } @@ -192,22 +223,12 @@ public class RailButton extends ExternalComponent implements AnglePositionable, } } - this.totalHeight_m = newHeight; + this.totalHeight_m = Math.max(newHeight, this.flangeHeight_m + this.baseHeight_m); + clearPreset(); fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); } - - public void setThickness(double newThickness ) { - for (RocketComponent listener : configListeners) { - if (listener instanceof RailButton) { - ((RailButton) listener).setThickness(newThickness); - } - } - this.flangeHeight_m = newThickness; - fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); - } - @Override public boolean isAerodynamic(){ // TODO: implement aerodynamics @@ -252,15 +273,17 @@ public class RailButton extends ExternalComponent implements AnglePositionable, super.setAxialMethod(position); fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } - + + @Override public BoundingBox getInstanceBoundingBox(){ BoundingBox instanceBounds = new BoundingBox(); - instanceBounds.update(new Coordinate(0, this.getTotalHeight(), 0)); + instanceBounds.update(new Coordinate(0, this.totalHeight_m, 0)); + instanceBounds.update(new Coordinate(0, -this.totalHeight_m, 0)); - final double r = this.getOuterDiameter(); - instanceBounds.update(new Coordinate(r,r,0)); - instanceBounds.update(new Coordinate(-r,-r,0)); + final double r = this.getOuterDiameter() / 2; + instanceBounds.update(new Coordinate(r, 0, r)); + instanceBounds.update(new Coordinate(-r, 0, -r)); return instanceBounds; } @@ -306,7 +329,7 @@ public class RailButton extends ExternalComponent implements AnglePositionable, public double getComponentVolume() { final double volOuter = Math.PI*Math.pow( outerDiameter_m/2, 2)*flangeHeight_m; final double volInner = Math.PI*Math.pow( innerDiameter_m/2, 2)*getInnerHeight(); - final double volStandoff = Math.PI*Math.pow( outerDiameter_m/2, 2)*standoff_m; + final double volStandoff = Math.PI*Math.pow( outerDiameter_m/2, 2)* baseHeight_m; return (volOuter+ volInner+ volStandoff); @@ -370,12 +393,12 @@ public class RailButton extends ExternalComponent implements AnglePositionable, @Override public Coordinate getComponentCG() { - // Math.PI and density are assumed constant through calculation, and thus may be factored out. - final double volumeFlange = Math.pow( outerDiameter_m/2, 2)*flangeHeight_m; - final double volumeInner = Math.pow( innerDiameter_m/2, 2)*(getInnerHeight()); - final double volumeStandoff = Math.pow( outerDiameter_m/2, 2)*this.standoff_m; - final double totalVolume = volumeFlange + volumeInner + volumeStandoff; - final double heightCM = (volumeFlange*( this.totalHeight_m-getFlangeHeight()/2) + volumeInner*( this.standoff_m + this.getInnerHeight()/2) + volumeStandoff*(this.standoff_m/2))/totalVolume; + // Math.PI and density are assumed constant through calculation, and thus may be factored out. + final double volumeBase = Math.pow(outerDiameter_m / 2, 2) * this.baseHeight_m; + final double volumeFlange = Math.pow(outerDiameter_m / 2, 2)* this.flangeHeight_m; + final double volumeInner = Math.pow(innerDiameter_m / 2, 2)* getInnerHeight(); + final double totalVolume = volumeFlange + volumeInner + volumeBase; + final double heightCM = (volumeFlange*( this.totalHeight_m-getFlangeHeight()/2) + volumeInner*( this.baseHeight_m + this.getInnerHeight()/2) + volumeBase*(this.baseHeight_m /2))/totalVolume; if( heightCM > this.totalHeight_m ){ throw new BugException(" bug found while computing the CG of a RailButton: "+this.getName()+"\n height of CG: "+heightCM); @@ -417,4 +440,48 @@ public class RailButton extends ExternalComponent implements AnglePositionable, return false; } + @Override + protected void loadFromPreset(ComponentPreset preset) { + super.loadFromPreset(preset); + if (preset.has(ComponentPreset.OUTER_DIAMETER)) { + this.outerDiameter_m = preset.get(ComponentPreset.OUTER_DIAMETER); + } + if (preset.has(ComponentPreset.INNER_DIAMETER)) { + this.innerDiameter_m = preset.get(ComponentPreset.INNER_DIAMETER); + } + if (preset.has(ComponentPreset.HEIGHT)) { + this.totalHeight_m = preset.get(ComponentPreset.HEIGHT); + } + if (preset.has(ComponentPreset.FLANGE_HEIGHT)) { + this.flangeHeight_m = preset.get(ComponentPreset.FLANGE_HEIGHT); + } + if (preset.has(ComponentPreset.BASE_HEIGHT)) { + this.baseHeight_m = preset.get(ComponentPreset.BASE_HEIGHT); + } + if (preset.has(ComponentPreset.CD) && preset.get(ComponentPreset.CD) > 0) { + setCDOverridden(true); + setOverrideCD(preset.get(ComponentPreset.CD)); + } + + double totalMass = 0; + boolean massOverridden = false; + if (preset.has(ComponentPreset.MASS)) { + massOverridden = true; + totalMass += preset.get(ComponentPreset.MASS); + } + if (preset.has(ComponentPreset.SCREW_MASS)) { + massOverridden = true; + totalMass += preset.get(ComponentPreset.SCREW_MASS); + } + if (preset.has(ComponentPreset.NUT_MASS)) { + massOverridden = true; + totalMass += preset.get(ComponentPreset.NUT_MASS); + } + if (massOverridden) { + setMassOverridden(true); + setOverrideMass(totalMass); + } + + fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE); + } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 5411440f7..0b85db63d 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -287,6 +287,11 @@ public class Rocket extends ComponentAssembly { refType = type; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } + + @Override + public double getLength() { + return selectedConfiguration.getLength(); + } public double getCustomReferenceLength() { @@ -304,6 +309,17 @@ public class Rocket extends ComponentAssembly { fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } } + + @Override + public double getBoundingRadius() { + double bounding = 0; + for (RocketComponent comp : children) { + if (comp instanceof ComponentAssembly) { + bounding = Math.max(bounding, ((ComponentAssembly) comp).getBoundingRadius()); + } + } + return bounding; + } @@ -403,10 +419,12 @@ public class Rocket extends ComponentAssembly { this.stageMap = r.stageMap; // these flight configurations need to reference the _this_ Rocket: + this.configSet.reset(); this.configSet.setDefault(new FlightConfiguration(this)); for (FlightConfigurationId key : r.configSet.map.keySet()) { this.configSet.set(key, new FlightConfiguration(this, key)); } + this.selectedConfiguration = this.configSet.get(r.getSelectedConfiguration().getId()); this.perfectFinish = r.perfectFinish; @@ -496,9 +514,9 @@ public class Rocket extends ComponentAssembly { // Notify all components first Iterator iterator = this.iterator(true); while (iterator.hasNext()) { - iterator.next().componentChanged(cce); + RocketComponent next = iterator.next(); + next.componentChanged(cce); } - updateConfigurations(); notifyAllListeners(cce); @@ -538,7 +556,6 @@ public class Rocket extends ComponentAssembly { } private void updateConfigurations(){ - this.selectedConfiguration.update(); for( FlightConfiguration config : configSet ){ config.update(); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 23a954e70..637d01465 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1010,7 +1010,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * If the length of a component is settable, the class must define the setter method * itself. */ - public final double getLength() { + public double getLength() { mutex.verify(); return length; } diff --git a/core/src/net/sf/openrocket/scripting/GraalJSScriptEngineFactory.java b/core/src/net/sf/openrocket/scripting/GraalJSScriptEngineFactory.java new file mode 100644 index 000000000..0b71e8501 --- /dev/null +++ b/core/src/net/sf/openrocket/scripting/GraalJSScriptEngineFactory.java @@ -0,0 +1,101 @@ +package net.sf.openrocket.scripting; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import java.util.*; + +import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.HostAccess; + +public class GraalJSScriptEngineFactory implements ScriptEngineFactory { + private static final String ENGINE_NAME = "Graal.js"; + private static final String LANGUAGE = "ECMAScript"; + private static final String LANGUAGE_VERSION = "ECMAScript 262 Edition 11"; + private static final String[] NAMES = new String[]{"js", "JS", "JavaScript", "javascript", LANGUAGE, LANGUAGE.toLowerCase(), ENGINE_NAME, ENGINE_NAME.toLowerCase(), "Graal-js", "graal-js", "Graal.JS", "Graal-JS", "GraalJS", "GraalJSPolyglot"}; + private static final String[] MIME_TYPES = new String[]{"application/javascript", "application/ecmascript", "text/javascript", "text/ecmascript"}; + private static final String[] EXTENSIONS = new String[]{"js", "mjs"}; + private static final List names; + private static final List mimeTypes; + private static final List extensions; + + public GraalJSScriptEngineFactory() { + } + + public ScriptEngine getScriptEngine() { + // https://github.com/oracle/graaljs/blob/master/docs/user/RunOnJDK.md + // https://github.com/oracle/graaljs/blob/master/docs/user/ScriptEngine.md#setting-options-via-bindings + return GraalJSScriptEngine.create(null, + Context.newBuilder("js") + .allowHostAccess(HostAccess.ALL) + .allowHostClassLookup(s -> true) + .option("js.ecmascript-version", "2021")); + } + + public String getEngineName() { + return ENGINE_NAME; + } + + public String getEngineVersion() { + return ((GraalJSScriptEngine)getScriptEngine()).getPolyglotEngine().getVersion(); + } + + public List getExtensions() { + return extensions; + } + + public String getLanguageVersion() { + return LANGUAGE_VERSION; + } + + public String getLanguageName() { + return LANGUAGE; + } + + public List getMimeTypes() { + return mimeTypes; + } + + public List getNames() { + return names; + } + + public String getMethodCallSyntax(final String obj, final String method, final String... args) { + return null; + } + + public String getOutputStatement(final String toDisplay) { + return "print(" + toDisplay + ")"; + } + + public Object getParameter(String key) { + switch (key) { + case "javax.script.name": + return "javascript"; + case "javax.script.engine": + return this.getEngineName(); + case "javax.script.engine_version": + return this.getEngineVersion(); + case "javax.script.language": + return this.getLanguageName(); + case "javax.script.language_version": + return this.getLanguageVersion(); + default: + return null; + } + } + + public String getProgram(final String... statements) { + return null; + } + + static { + List nameList = Arrays.asList(NAMES); + List mimeTypeList = Arrays.asList(MIME_TYPES); + List extensionList = Arrays.asList(EXTENSIONS); + names = Collections.unmodifiableList(nameList); + mimeTypes = Collections.unmodifiableList(mimeTypeList); + extensions = Collections.unmodifiableList(extensionList); + } +} diff --git a/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java b/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java new file mode 100644 index 000000000..42566c8a1 --- /dev/null +++ b/core/src/net/sf/openrocket/scripting/ScriptEngineManagerRedux.java @@ -0,0 +1,300 @@ +/* + * This is a replacement for the ScriptEngineManager which gets around and issue with the sun.misc.ServiceConfigurationError + * which has been removed in Java 9+. If using the ScriptEngineManager from the script-api*.jar then the sun.misc throws + * a ClassNotFoundException. + */ + +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package net.sf.openrocket.scripting; + +import javax.script.*; +import java.util.*; +import java.security.*; +import java.util.ServiceLoader; +import java.util.ServiceConfigurationError; + +/** + * The ScriptEngineManager implements a discovery and instantiation + * mechanism for ScriptEngine classes and also maintains a + * collection of key/value pairs storing state shared by all engines created + * by the Manager. This class uses the service provider mechanism described in the + * {@link java.util.ServiceLoader} class to enumerate all the + * implementations of ScriptEngineFactory.

+ * The ScriptEngineManager provides a method to return a list of all these factories + * as well as utility methods which look up factories on the basis of language name, file extension + * and mime type. + *

+ * The Bindings of key/value pairs, referred to as the "Global Scope" maintained + * by the manager is available to all instances of ScriptEngine created + * by the ScriptEngineManager. The values in the Bindings are + * generally exposed in all scripts. + * + * @author Mike Grogan + * @author A. Sundararajan + * @since 1.6 + */ +public class ScriptEngineManagerRedux { + private static final boolean DEBUG = false; + /** + * The effect of calling this constructor is the same as calling + * ScriptEngineManager(Thread.currentThread().getContextClassLoader()). + * + * @see java.lang.Thread#getContextClassLoader + */ + public ScriptEngineManagerRedux() { + init(Thread.currentThread().getContextClassLoader()); + } + + /** + * This constructor loads the implementations of + * ScriptEngineFactory visible to the given + * ClassLoader using the service provider mechanism.

+ * If loader is null, the script engine factories that are + * bundled with the platform are loaded.
+ * + * @param loader ClassLoader used to discover script engine factories. + */ + public ScriptEngineManagerRedux(ClassLoader loader) { + init(loader); + } + + /** + * Gets the value for the specified key in the Global Scope + * @param key The key whose value is to be returned. + * @return The value for the specified key. + */ + public Object get(String key) { + return _globalScope.get(key); + } + + /** + * getBindings returns the value of the globalScope field. + * ScriptEngineManager sets this Bindings as global bindings for + * ScriptEngine objects created by it. + * + * @return The globalScope field. + */ + public Bindings getBindings() { + return _globalScope; + } + + /** + * Looks up and creates a ScriptEngine for a given name. + * The algorithm first searches for a ScriptEngineFactory that has been + * registered as a handler for the specified name using the registerEngineName + * method. + *

If one is not found, it searches the set of ScriptEngineFactory instances + * stored by the constructor for one with the specified name. If a ScriptEngineFactory + * is found by either method, it is used to create instance of ScriptEngine. + * @param shortName The short name of the ScriptEngine implementation. + * returned by the getNames method of its ScriptEngineFactory. + * @return A ScriptEngine created by the factory located in the search. Returns null + * if no such factory was found. The ScriptEngineManager sets its own globalScope + * Bindings as the GLOBAL_SCOPE Bindings of the newly + * created ScriptEngine. + * @throws NullPointerException if shortName is null. + */ + private Map _factoriesByName = new HashMap<>(); + public synchronized ScriptEngine getEngineByName(String shortName) { + if (shortName == null) { + throw new NullPointerException(); + } + + String key = shortName.toLowerCase(); + if (_factoriesByName.containsKey(key)) { + return getEngineByFactory(_factoriesByName.get(key)); + } + + // Look for registered name first + ScriptEngineFactory factoryNamed; + if (null != (factoryNamed = _nameAssociations.get(key))) { + try { + _factoriesByName.put(key, factoryNamed); + return getEngineByFactory(factoryNamed); + } catch (Exception exp) { + if (DEBUG) { + exp.printStackTrace(); + } + } + } + + Optional factoryName; + List names; + for (ScriptEngineFactory factory : _scriptEngineFactories) { + try { + factoryName = factory.getNames().stream().filter(l -> l.equalsIgnoreCase(shortName)).findFirst(); + if (factoryName.isPresent()) { + _factoriesByName.put(key, factory); + return getEngineByFactory(factory); + } + } catch (Exception exp) { + if (DEBUG) { + exp.printStackTrace(); + } + } + } + + return null; + } + + /** + * Returns a list whose elements are instances of all the ScriptEngineFactory classes + * found by the discovery mechanism. + * @return List of all discovered ScriptEngineFactorys. + */ + public synchronized List getEngineFactories() { + return List.copyOf(_scriptEngineFactories); + } + + /** + * Sets the specified key/value pair in the Global Scope. + * @param key Key to set + * @param value Value to set. + * @throws NullPointerException if key is null. + * @throws IllegalArgumentException if key is empty string. + */ + public void put(String key, Object value) { + _globalScope.put(key, value); + } + + /** + * Registers a ScriptEngineFactory to handle a language + * name. Overrides any such association found using the Discovery mechanism. + * @param name The name to be associated with the ScriptEngineFactory. + * @param factory The class to associate with the given name. + * @throws NullPointerException if any of the parameters is null. + */ + public void registerEngineName(String name, ScriptEngineFactory factory) { + if (name == null || factory == null) { + throw new NullPointerException(); + } + + _nameAssociations.put(name.toLowerCase(), factory); + } + + /** + * setBindings stores the specified Bindings + * in the globalScope field. ScriptEngineManager sets this + * Bindings as global bindings for ScriptEngine + * objects created by it. + * + * @param bindings The specified Bindings + * @throws IllegalArgumentException if bindings is null. + */ + public void setBindings(Bindings bindings) { + if (bindings == null) { + throw new IllegalArgumentException("Global scope cannot be null."); + } + + _globalScope = bindings; + } + + private ScriptEngine getEngineByFactory(ScriptEngineFactory factory) { + if (factory == null) { + return null; + } + + ScriptEngine engine = factory.getScriptEngine(); + engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); + return engine; + } + + private ServiceLoader getServiceLoader(final ClassLoader loader) { + if (loader != null) { + return ServiceLoader.load(ScriptEngineFactory.class, loader); + } else { + return ServiceLoader.loadInstalled(ScriptEngineFactory.class); + } + } + + private void init(final ClassLoader loader) { + _scriptEngineFactories = new TreeSet<>(Comparator.comparing( + ScriptEngineFactory::getEngineName, + Comparator.nullsLast(Comparator.naturalOrder())) + ); + initEngines(loader); + } + + private void initEngines(final ClassLoader loader) { + Iterator itr; + try { + ServiceLoader loaders = AccessController.doPrivileged( + new PrivilegedAction>() { + @Override + public ServiceLoader run() { + return getServiceLoader(loader); + } + }); + + itr = loaders.iterator(); + } catch (ServiceConfigurationError err) { + // } catch (Exception err) { + System.err.println("Can't find ScriptEngineFactory providers: " + err.getMessage()); + if (DEBUG) { + err.printStackTrace(); + } + // do not throw any exception here. user may want to + // manage his/her own factories using this manager + // by explicit registration (by registerXXX) methods. + return; + } + + try { + while (itr.hasNext()) { + try { + ScriptEngineFactory factory = itr.next(); + _scriptEngineFactories.add(factory); + } catch (ServiceConfigurationError err) { + // } catch (Exception err) { + System.err.println("ScriptEngineManager providers.next(): " + err.getMessage()); + if (DEBUG) { + err.printStackTrace(); + } + // one factory failed, but check other factories... + } + } + } catch (ServiceConfigurationError err) { + // } catch (Exception err) { + System.err.println("ScriptEngineManager providers.hasNext(): " + err.getMessage()); + if (DEBUG) { + err.printStackTrace(); + } + // do not throw any exception here. user may want to + // manage his/her own factories using this manager + // by explicit registratation (by registerXXX) methods. + } + } + + /** Set of script engine factories discovered. */ + private TreeSet _scriptEngineFactories; + + /** Map of engine name to script engine factory. */ + private HashMap _nameAssociations = new HashMap<>(); + + /** Global bindings associated with script engines created by this manager. */ + private Bindings _globalScope = new SimpleBindings(); +} diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 1d5b62e76..0ba86fb4c 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -130,8 +130,8 @@ public class BasicEventSimulationEngine implements SimulationEngine { double oldAlt = currentStatus.getRocketPosition().z; if (SimulationListenerHelper.firePreStep(currentStatus)) { - // Step at most to the next event - double maxStepTime = Double.MAX_VALUE; + // Step at most to the next event. If there is no next event, don't step time + double maxStepTime = 0.0; FlightEvent nextEvent = currentStatus.getEventQueue().peek(); if (nextEvent != null) { maxStepTime = MathUtil.max(nextEvent.getTime() - currentStatus.getSimulationTime(), 0.001); diff --git a/core/src/net/sf/openrocket/simulation/GroundStepper.java b/core/src/net/sf/openrocket/simulation/GroundStepper.java index 49c8b5b4f..b13cfde8c 100644 --- a/core/src/net/sf/openrocket/simulation/GroundStepper.java +++ b/core/src/net/sf/openrocket/simulation/GroundStepper.java @@ -20,5 +20,6 @@ public class GroundStepper extends AbstractSimulationStepper { @Override public void step(SimulationStatus status, double timeStep) throws SimulationException { log.trace("step: position=" + status.getRocketPosition() + ", velocity=" + status.getRocketVelocity()); + status.setSimulationTime(status.getSimulationTime() + timeStep); } } diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java index 3569d2aae..953543442 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java +++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingExtension.java @@ -2,7 +2,6 @@ package net.sf.openrocket.simulation.extension.impl; import javax.script.Invocable; import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; import javax.script.ScriptException; import net.sf.openrocket.aerodynamics.Warning; @@ -91,8 +90,7 @@ public class ScriptingExtension extends AbstractSimulationExtension { SimulationListener getListener() throws SimulationException { - ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine engine = manager.getEngineByName(getLanguage()); + ScriptEngine engine = util.getEngineByName(getLanguage()); if (engine == null) { throw new SimulationException("Your JRE does not support the scripting language '" + getLanguage() + "'"); } diff --git a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java index 7259a9787..ef215ae3e 100644 --- a/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java +++ b/core/src/net/sf/openrocket/simulation/extension/impl/ScriptingUtil.java @@ -4,14 +4,14 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; import java.util.List; import java.util.prefs.BackingStoreException; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; +import net.sf.openrocket.scripting.ScriptEngineManagerRedux; +import net.sf.openrocket.scripting.GraalJSScriptEngineFactory; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.BugException; @@ -22,22 +22,34 @@ import com.google.inject.Inject; * Utility class used by the scripting extension and its configurator. */ public class ScriptingUtil { - static final String NODE_ID = ScriptingExtension.class.getCanonicalName(); - private static final List DEFAULT_TRUSTED_HASHES = Arrays.asList( + private static final List DEFAULT_TRUSTED_HASHES = List.of( // Roll control script in roll control example file: "SHA-256:9bf364ce4d4a75f09b29178bf9d6872b232084f73dae20dc7b5b073e54e95a42" - ); + ); /** The name to be chosen from a list of alternatives. If not found, will use the default name. */ - private static final List PREFERRED_LANGUAGE_NAMES = Arrays.asList("JavaScript"); + private static final List PREFERRED_LANGUAGE_NAMES = List.of("JavaScript"); + + private static ScriptEngineManagerRedux manager; @Inject Preferences prefs; - - - + + public ScriptingUtil() { + if (manager == null) { + // using the ScriptEngineManger from javax.script package pulls in the sun.misc.ServiceConfigurationError + // which is removed in Java 9+ which causes a ClassNotFoundException to be thrown. + manager = new ScriptEngineManagerRedux(); + + manager.registerEngineName("Javascript", new GraalJSScriptEngineFactory()); + } + } + + public ScriptEngine getEngineByName(String shortName) { + return manager.getEngineByName(shortName); + } /** * Return the preferred internal language name based on a script language name. @@ -48,38 +60,23 @@ public class ScriptingUtil { if (language == null) { return null; } - - ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName(language); if (engine == null) { return null; } - return getLanguage(engine.getFactory()); + + return getLanguageByFactory(engine.getFactory()); } - - + public List getLanguages() { - List langs = new ArrayList(); - ScriptEngineManager manager = new ScriptEngineManager(); + List languages = new ArrayList<>(); for (ScriptEngineFactory factory : manager.getEngineFactories()) { - langs.add(getLanguage(factory)); + languages.add(getLanguageByFactory(factory)); } - return langs; + return languages; } - - private String getLanguage(ScriptEngineFactory factory) { - for (String name : factory.getNames()) { - if (PREFERRED_LANGUAGE_NAMES.contains(name)) { - return name; - } - } - - return factory.getLanguageName(); - } - - - /** * Test whether the user has indicated this script to be trusted, * or if it is an internally trusted script. @@ -122,7 +119,16 @@ public class ScriptingUtil { throw new BugException(e); } } - + + private String getLanguageByFactory(ScriptEngineFactory factory) { + for (String name : factory.getNames()) { + if (PREFERRED_LANGUAGE_NAMES.contains(name)) { + return name; + } + } + + return factory.getLanguageName(); + } static String normalize(String script) { return script.replaceAll("\r", "").trim(); @@ -132,10 +138,8 @@ public class ScriptingUtil { /* * NOTE: Hash length must be max 80 chars, the max length of a key in a Properties object. */ - String output; MessageDigest digest; - try { digest = MessageDigest.getInstance("SHA-256"); digest.update(language.getBytes(StandardCharsets.UTF_8)); @@ -152,5 +156,4 @@ public class ScriptingUtil { return digest.getAlgorithm() + ":" + output; } - } diff --git a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java index 55a557d70..5493c4a0d 100644 --- a/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java +++ b/core/src/net/sf/openrocket/simulation/listeners/example/RollControlListener.java @@ -35,8 +35,7 @@ public class RollControlListener extends AbstractSimulationListener { // Maximum control fin angle (rad) private static final double MAX_ANGLE = 15 * Math.PI / 180; - - + /* * PID parameters * @@ -47,29 +46,22 @@ public class RollControlListener extends AbstractSimulationListener { private static final double KP = 0.007; private static final double KI = 0.2; - - - - private double rollrate; + private double rollRate; private double prevTime = 0; private double intState = 0; private double finPosition = 0; - - - + @Override public FlightConditions postFlightConditions(SimulationStatus status, FlightConditions flightConditions) { // Store the current roll rate for later use - rollrate = flightConditions.getRollRate(); + rollRate = flightConditions.getRollRate(); return null; } - - + @Override public void postStep(SimulationStatus status) throws SimulationException { - // Activate PID controller only after a specific time if (status.getSimulationTime() < START_TIME) { prevTime = status.getSimulationTime(); @@ -87,23 +79,20 @@ public class RollControlListener extends AbstractSimulationListener { if (finset == null) { throw new SimulationException("A fin set with name '" + CONTROL_FIN_NAME + "' was not found"); } - - + // Determine time step double deltaT = status.getSimulationTime() - prevTime; prevTime = status.getSimulationTime(); - - + // PID controller - double error = SETPOINT - rollrate; + double error = SETPOINT - rollRate; double p = KP * error; intState += error * deltaT; double i = KI * intState; double value = p + i; - - + // Clamp the fin angle between -MAX_ANGLE and MAX_ANGLE if (Math.abs(value) > MAX_ANGLE) { System.err.printf("Attempting to set angle %.1f at t=%.3f, clamping.\n", @@ -111,7 +100,6 @@ public class RollControlListener extends AbstractSimulationListener { value = MathUtil.clamp(value, -MAX_ANGLE, MAX_ANGLE); } - // Limit the fin turn rate if (finPosition < value) { finPosition = Math.min(finPosition + TURNRATE * deltaT, value); @@ -122,6 +110,5 @@ public class RollControlListener extends AbstractSimulationListener { // Set the control fin cant and store the data finset.setCantAngle(finPosition); status.getFlightData().setValue(FIN_CANT_TYPE, finPosition); - } } diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index 64f1de5a0..90da3c320 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -63,11 +63,15 @@ public abstract class Preferences implements ChangeSource { public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch"; public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar"; public static final String MOTOR_HIDE_UNAVAILABLE = "MotorHideUnavailable"; + + public static final String MATCH_FORE_DIAMETER = "MatchForeDiameter"; + public static final String MATCH_AFT_DIAMETER = "MatchAftDiameter"; // Node names public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB"; + private static final String SHOW_MARKERS = "SHOW_MARKERS"; private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; //Preferences related to 3D graphics @@ -469,7 +473,64 @@ public abstract class Preferences implements ChangeSource { public final boolean isAlwaysOpenLeftmostTab() { return this.getBoolean(OPEN_LEFTMOST_DESIGN_TAB, false); } - + + /** + * Set whether pod set/booster markers should only be displayed when the pod set/booster is selected. + * @param enabled true if pod set/booster markers should only be displayed when the pod set/booster is selected, + * false if they should be displayed permanently. + */ + public final void setShowMarkers(boolean enabled) { + this.putBoolean(SHOW_MARKERS, enabled); + } + + /** + * Answer if pod set/booster markers should only be displayed when the pod set/booster is selected + * + * @return true if pod set/booster markers should only be displayed when the pod set/booster is selected, + * false if they should be displayed permanently. + */ + public final boolean isShowMarkers() { + return this.getBoolean(SHOW_MARKERS, false); + } + + /** + * Set whether the component preset chooser dialog should filter by fore diameter when the window is opened. + * @param enabled true if the fore diameter filter should be enabled, + * false if it should be disabled. + */ + public final void setMatchForeDiameter(boolean enabled) { + this.putBoolean(MATCH_FORE_DIAMETER, enabled); + } + + /** + * Answer if the component preset chooser dialog should filter by fore diameter when the window is opened. + * + * @return true if the fore diameter filter should be enabled, + * false if it should be disabled. + */ + public final boolean isMatchForeDiameter() { + return this.getBoolean(MATCH_FORE_DIAMETER, true); + } + + /** + * Set whether the component preset chooser dialog should filter by aft diameter when the window is opened. + * @param enabled true if the aft diameter filter should be enabled, + * false if it should be disabled. + */ + public final void setMatchAftDiameter(boolean enabled) { + this.putBoolean(MATCH_AFT_DIAMETER, enabled); + } + + /** + * Answer if the component preset chooser dialog should filter by aft diameter when the window is opened. + * + * @return true if the aft diameter filter should be enabled, + * false if it should be disabled. + */ + public final boolean isMatchAftDiameter() { + return this.getBoolean(MATCH_AFT_DIAMETER, true); + } + /** * Return the OpenRocket unique ID. * diff --git a/core/test/net/sf/openrocket/simulation/DisableStageTest.java b/core/test/net/sf/openrocket/simulation/DisableStageTest.java index 9b4b0f7df..d4b7d5a1d 100644 --- a/core/test/net/sf/openrocket/simulation/DisableStageTest.java +++ b/core/test/net/sf/openrocket/simulation/DisableStageTest.java @@ -19,6 +19,8 @@ import org.junit.Test; * @author Sibo Van Gool */ public class DisableStageTest extends BaseTestCase { + private final double delta = 0.08; // 8 % error margin (simulations are not exact) + /** * Tests that the simulation results are correct when a single stage is deactivated and re-activated. */ @@ -54,7 +56,6 @@ public class DisableStageTest extends BaseTestCase { simDisabled.getActiveConfiguration().setAllStages(); // Re-enable all stages. - double delta = 0.05; // 5 % error margin (simulations are not exact) compareSims(simOriginal, simDisabled, simulationListener, delta); } @@ -84,7 +85,6 @@ public class DisableStageTest extends BaseTestCase { SimulationListener simulationListener = new AbstractSimulationListener(); - double delta = 0.05; // 5 % error margin (simulations are not exact) compareSims(simRemoved, simDisabled, simulationListener, delta); //// Test re-enableing the stage. @@ -175,7 +175,6 @@ public class DisableStageTest extends BaseTestCase { SimulationListener simulationListener = new AbstractSimulationListener(); - double delta = 0.05; // 5 % error margin (simulations are not exact) compareSims(simRemoved, simDisabled, simulationListener, delta); //// Test re-enableing the stage. @@ -243,7 +242,6 @@ public class DisableStageTest extends BaseTestCase { simDisabled.getActiveConfiguration().setAllStages(); - double delta = 0.05; // 5 % error margin (simulations are not exact) compareSims(simOriginal, simDisabled, simulationListener, delta); } diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index aca598150..af6fe1e98 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: openrocket adopt-info: openrocket -grade: devel +grade: stable summary: A free, fully featured model rocket simulator. description: | OpenRocket is a free, fully featured model rocket simulator that allows you @@ -77,6 +77,7 @@ parts: prime: - -usr/lib/jvm/java-*/lib/security/cacerts - -usr/lib/jvm/java-*/jre/lib/security/cacerts + - -usr/lib/jvm/java-*/lib/security/blacklisted.certs launcher: plugin: dump diff --git a/swing/.classpath b/swing/.classpath index 7daea48ea..6878da13d 100644 --- a/swing/.classpath +++ b/swing/.classpath @@ -19,6 +19,7 @@ + diff --git a/swing/OpenRocket Swing.iml b/swing/OpenRocket Swing.iml index a4dcbfdb1..c9df46ca4 100644 --- a/swing/OpenRocket Swing.iml +++ b/swing/OpenRocket Swing.iml @@ -245,5 +245,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/swing/build.xml b/swing/build.xml index dada6c98c..578f0994c 100644 --- a/swing/build.xml +++ b/swing/build.xml @@ -99,6 +99,11 @@ + + + + + @@ -149,7 +154,7 @@ depends="build"> - + + + + 0.1 + + + + + Delrin + 1420 + BULK + + + + Nylon + 1150 + BULK + + + + + + + + Binder Design-Rail Button Supply House + Std 1010 RB + Standard 1010 Rail Button, Countersunk 8-32 Screw, and T-Nut + Delrin + Polished + 0.4375 + 0.2285 + 0.2975 + 0.0730 + 0.0730 + 0.0000 + + 0.445 + 1.395 + 1.025 + + + + Binder Design-Rail Button Supply House + Std 1515 RB + Standard 1515 Rail Button, Countersunk 10-32 Screw, and T-Nut + Delrin + Polished + 0.6200 + 0.2995 + .4495 + 0.125 + 0.125 + 0.0000 + + 1.465 + 3.365 + 2.675 + + + + Rail-Buttons.com + RB-Micro + 2 Piece Micro Rail Button with 2-56 Screw (10mm Rail) + Nylon + Polished + .1650 + .1195 + 0.1595 + 0.041 + 0.0000 + 0.0465 + + 0.01 + 0.04 + + + + + Rail-Buttons.com + 1PMB + 1 Piece Mini Rail Button with Countersunk 6-32 Screw + Delrin + Polished + .249 + .193 + 0.205 + 0.0380 + 0.0380 + 0.0000 + + 0.090 + 0.415 + + + + + Rail-Buttons.com + RB-10-D + 3 Piece 1010 Rail Button with 8-32 Screw + Delrin + Polished + .278 + .154 + 0.270 + 0.060 + 0.060 + 0.115 + + 0.305 + 1.715 + + + + + Rail-Buttons.com + 1P1010DLX + 1 Piece 1010 Rail Button with Countersunk 8-32 Screw + Delrin + Polished + .3725 + .2480 + 0.305 + 0.078 + 0.078 + 0.0000 + + 0.320 + 1.235 + + + + + Rail-Buttons.com + RB1515S + 1 Piece 1515 Rail Button, Countersunk 10-32 Screw, and T-Nut + Delrin + Polished + 0.49 + 0.29 + 0.56 + 0.1875 + 0.1875 + 0.0000 + + 1.355 + 2.720 + + + + + + diff --git a/swing/resources/datafiles/examples/Simulation extensions and scripting.ork b/swing/resources/datafiles/examples/Simulation extensions and scripting.ork index a1592fc11..e84fc0d9d 100644 Binary files a/swing/resources/datafiles/examples/Simulation extensions and scripting.ork and b/swing/resources/datafiles/examples/Simulation extensions and scripting.ork differ diff --git a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java index e62eac2d5..2fcec306e 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java @@ -5,17 +5,14 @@ import java.util.List; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; -import javax.swing.SwingUtilities; -import net.sf.openrocket.database.ComponentPresetDatabase; -import net.sf.openrocket.gui.configdialog.RocketComponentConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sf.openrocket.gui.configdialog.RocketComponentConfig; import net.sf.openrocket.database.Database; import net.sf.openrocket.database.DatabaseListener; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.preset.ComponentPreset; @@ -30,8 +27,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com private static final Logger log = LoggerFactory.getLogger(PresetModel.class); private static final Translator trans = Application.getTranslator(); - private static final String NONE_SELECTED = trans.get("lbl.select"); - private static final String SELECT_DATABASE = trans.get("lbl.database"); + private static final String NONE_SELECTED = String.format("%s", trans.get("PresetModel.lbl.custompreset")); private final Component parent; private final RocketComponent component; @@ -51,7 +47,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com @Override public int getSize() { - return presets.size() + 2; + return presets.size() + 1; } @Override @@ -59,9 +55,6 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com if (index == 0) { return NONE_SELECTED; } - if (index == getSize() - 1) { - return SELECT_DATABASE; - } return presets.get(index - 1); } @@ -73,21 +66,6 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com throw new BugException("item is null"); } else if (item.equals(NONE_SELECTED)) { component.clearPreset(); - } else if (item.equals(SELECT_DATABASE)) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(PresetModel.this); - ComponentPresetChooserDialog dialog = - new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(parent), component); - dialog.setVisible(true); - ComponentPreset preset = dialog.getSelectedComponentPreset(); - if (preset != null) { - setSelectedItem(preset); - } - ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(PresetModel.this); - } - }); } else { document.addUndoPosition("Use Preset " + component.getComponentName()); component.loadPreset((ComponentPreset) item); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 3566cf3e9..bd15b1b90 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -1,18 +1,24 @@ package net.sf.openrocket.gui.configdialog; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.List; import javax.swing.JDialog; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.util.WindowLocationUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -230,8 +236,9 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis * @param document the document to configure. * @param component the component to configure. * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog + * @param includeUndoModify if true, include a 'Modify component' undo action */ - public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab, boolean includeUndoModify) { if (dialog != null) { // Don't remember the previous tab for rockets or stages, because this will leave you in the override tab for // the next component, which is generally not what you want. @@ -256,15 +263,32 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis dialog = new ComponentConfigDialog(parent, document, component); dialog.setVisible(true); + if (parent instanceof BasicFrame && BasicFrame.getStartupFrame() == parent) { + WindowLocationUtil.moveIfOutsideOfParentMonitor(dialog, parent); + } ////Modify - if (component.getConfigListeners().size() == 0) { - document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); - } else { - document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents")); + if (includeUndoModify) { + if (component.getConfigListeners().size() == 0) { + document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); + } else { + document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents")); + } } } + /** + * A singleton configuration dialog. Will create and show a new dialog if one has not + * previously been used, or update the dialog and show it if a previous one exists. + * + * @param document the document to configure. + * @param component the component to configure. + * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog + */ + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { + ComponentConfigDialog.showDialog(parent, document, component, rememberPreviousTab, true); + } + /** * A singleton configuration dialog. Will create and show a new dialog if one has not * previously been used, or update the dialog and show it if a previous one exists. diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index ba6b077da..b695e5dbf 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -81,6 +81,7 @@ public class FreeformFinSetConfig extends FinSetConfig { private Point dragPoint = null; private FinPointFigure figure = null; + private ScaleScrollPane figurePane = null; private ScaleSelector selector; public FreeformFinSetConfig(OpenRocketDocument d, RocketComponent component, JDialog parent) { @@ -219,7 +220,7 @@ public class FreeformFinSetConfig extends FinSetConfig { // Create the figure figure = new FinPointFigure(finset); - ScaleScrollPane figurePane = new FinPointScrollPane( figure); + figurePane = new FinPointScrollPane( figure); // Create the table tableModel = new FinPointTableModel(); @@ -392,9 +393,10 @@ public class FreeformFinSetConfig extends FinSetConfig { } figure.updateFigure(); } - - revalidate(); - repaint(); + + if (figurePane != null) { + figurePane.revalidate(); + } } private class FinPointScrollPane extends ScaleScrollPane { @@ -408,8 +410,6 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void mousePressed(MouseEvent event) { - int mods = event.getModifiersEx(); - final FreeformFinSet finset = (FreeformFinSet) component; final int pressIndex = getPoint(event); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java index b5e005a4a..be4803c42 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java @@ -29,13 +29,6 @@ public class RailButtonConfig extends RocketComponentConfig { public RailButtonConfig( OpenRocketDocument document, RocketComponent component, JDialog parent) { super(document, component, parent); - - // For DEBUG purposes -// if( component instanceof AxialStage ){ -// System.err.println(" Dumping AxialStage tree info for devel / debugging."); -// System.err.println(component.toDebugTree()); -// } - //// General and General properties tabbedPane.insertTab( trans.get("RailBtnCfg.tab.General"), null, buttonTab( (RailButton)component ), trans.get("RailBtnCfg.tab.GeneralProp"), 0); @@ -59,6 +52,33 @@ public class RailButtonConfig extends RocketComponentConfig { panel.add(new UnitSelector(ODModel), "growx"); panel.add(new BasicSlider(ODModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap"); } + { //// Inner Diameter + panel.add(new JLabel(trans.get("RailBtnCfg.lbl.InnerDiam"))); + DoubleModel IDModel = new DoubleModel(component, "InnerDiameter", UnitGroup.UNITS_LENGTH, 0); + JSpinner IDSpinner = new JSpinner(IDModel.getSpinnerModel()); + IDSpinner.setEditor(new SpinnerEditor(IDSpinner)); + panel.add(IDSpinner, "growx"); + panel.add(new UnitSelector(IDModel), "growx"); + panel.add(new BasicSlider(IDModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para"); + } + { //// Base Height + panel.add(new JLabel(trans.get("RailBtnCfg.lbl.BaseHeight"))); + DoubleModel heightModel = new DoubleModel(component, "BaseHeight", UnitGroup.UNITS_LENGTH, 0); + JSpinner heightSpinner = new JSpinner(heightModel.getSpinnerModel()); + heightSpinner.setEditor(new SpinnerEditor(heightSpinner)); + panel.add(heightSpinner, "growx"); + panel.add(new UnitSelector(heightModel), "growx"); + panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap"); + } + { //// Flange Height + panel.add(new JLabel(trans.get("RailBtnCfg.lbl.FlangeHeight"))); + DoubleModel heightModel = new DoubleModel(component, "FlangeHeight", UnitGroup.UNITS_LENGTH, 0); + JSpinner heightSpinner = new JSpinner(heightModel.getSpinnerModel()); + heightSpinner.setEditor(new SpinnerEditor(heightSpinner)); + panel.add(heightSpinner, "growx"); + panel.add(new UnitSelector(heightModel), "growx"); + panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap"); + } { //// Height panel.add(new JLabel(trans.get("RailBtnCfg.lbl.TotalHeight"))); DoubleModel heightModel = new DoubleModel(component, "TotalHeight", UnitGroup.UNITS_LENGTH, 0); @@ -66,7 +86,7 @@ public class RailButtonConfig extends RocketComponentConfig { heightSpinner.setEditor(new SpinnerEditor(heightSpinner)); panel.add(heightSpinner, "growx"); panel.add(new UnitSelector(heightModel), "growx"); - panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap"); + panel.add(new BasicSlider(heightModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para"); } { //// Angular Position: @@ -79,14 +99,17 @@ public class RailButtonConfig extends RocketComponentConfig { panel.add(new BasicSlider( angleModel.getSliderModel(-Math.PI, Math.PI)), "w 100lp, wrap"); } + primary.add(panel, "grow, gapright 201p"); + panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", "")); + { //// Position relative to: panel.add(new JLabel(trans.get("RailBtnCfg.lbl.PosRelativeTo"))); - + final EnumModel methodModel = new EnumModel(component, "AxialMethod", AxialMethod.axialOffsetMethods ); JComboBox relToCombo = new JComboBox( methodModel ); panel.add( relToCombo, "spanx, growx, wrap"); } - + { //// plus panel.add(new JLabel(trans.get("RailBtnCfg.lbl.Plus")), "right"); DoubleModel offsetModel = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); @@ -96,17 +119,13 @@ public class RailButtonConfig extends RocketComponentConfig { panel.add(offsetSpinner, "growx"); panel.add(new UnitSelector(offsetModel), "growx"); panel.add(new BasicSlider(offsetModel.getSliderModel( - new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE), - new DoubleModel(component.getParent(), "Length"))), + new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE), + new DoubleModel(component.getParent(), "Length"))), "w 100lp, wrap para"); - - } - primary.add(panel, "grow, gapright 201p"); - panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", "")); - + } //// Instance count - panel.add( instanceablePanel(rbc), "span, wrap"); + panel.add(instanceablePanel(rbc), "span, wrap"); //// Material panel.add(materialPanel(Material.Type.BULK),"span, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index dd27f02c3..57e7568d2 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -25,6 +25,7 @@ import javax.swing.JTextField; import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.database.ComponentPresetDatabase; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; @@ -37,6 +38,7 @@ import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; @@ -117,8 +119,20 @@ public class RocketComponentConfig extends JPanel { // If the component supports a preset, show the preset selection box. presetModel = new PresetModel(this, document, component); presetComboBox = new JComboBox(presetModel); + presetComboBox.setMaximumRowCount(25); presetComboBox.setEditable(false); - this.add(presetComboBox, ""); + presetComboBox.setToolTipText(trans.get("PresetModel.combo.ttip")); + this.add(presetComboBox, "growx 110"); + + final JButton selectPreset = new SelectColorButton(trans.get("PresetModel.lbl.partsLib")); + selectPreset.setToolTipText(trans.get("PresetModel.lbl.partsLib.ttip")); + selectPreset.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectPreset(); + } + }); + this.add(selectPreset); } tabbedPane = new JTabbedPane(); @@ -243,6 +257,21 @@ public class RocketComponentConfig extends JPanel { } } + private void selectPreset() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (presetComboBox == null || presetModel == null) return; + ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(presetModel); + ComponentPresetChooserDialog dialog = + new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(RocketComponentConfig.this), + component, presetModel); + dialog.setVisible(true); + ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(presetModel); + } + }); + } + public void clearConfigListeners() { if (appearancePanel != null) { appearancePanel.clearConfigListeners(); @@ -353,7 +382,7 @@ public class RocketComponentConfig extends JPanel { } protected JPanel instanceablePanel( Instanceable inst ){ - JPanel panel = new JPanel( new MigLayout("fill")); + JPanel panel = new JPanel( new MigLayout("fill, insets 0") ); { // Instance Count panel.add(new JLabel(trans.get("RocketCompCfg.lbl.InstanceCount"))); IntegerModel countModel = new IntegerModel(component, "InstanceCount", 1); @@ -369,7 +398,11 @@ public class RocketComponentConfig extends JPanel { separationSpinner.setEditor(new SpinnerEditor(separationSpinner)); panel.add(separationSpinner, "growx"); panel.add(new UnitSelector(separationModel), "growx"); - panel.add(new BasicSlider(separationModel.getSliderModel(0, 0.001, 0.02)), "w 100lp, wrap para"); + double maxSeparationDistance = 0.1; + if (component.getParent() != null && component.getParent().getLength() > 0) { + maxSeparationDistance = component.getParent().getLength(); + } + panel.add(new BasicSlider(separationModel.getSliderModel(0, 0.001, maxSeparationDistance)), "w 100lp, wrap para"); } return panel; } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java index ad53576e6..3a1db6af8 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java @@ -346,7 +346,7 @@ public class ScaleDialog extends JDialog { panel.add(selectionOption, "growx, wrap para*2"); // Select the 'scale component / scale selection and all subcomponents' if a component is selected - if (selection != null && selection.size() > 0) { + if (options.size() > 1 && selection != null && selection.size() > 0) { boolean entireRocket = false; // Flag to scale entire rocket for (RocketComponent component : selection) { if (component instanceof Rocket || (component instanceof AxialStage && !(component instanceof ParallelStage))) { diff --git a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java index b53e4a696..396cd7e5a 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/motor/MotorChooserDialog.java @@ -91,7 +91,7 @@ public class MotorChooserDialog extends JDialog implements CloseableDialog { // Set the closeable dialog after all initialization selectionPanel.setCloseableDialog(this); - GUIUtil.setDisposableDialogOptions(this, cancelButton); + GUIUtil.setWindowIcons(this); } public void setMotorMountAndConfig( FlightConfigurationId _fcid, MotorMount _mount ) { diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java index 08ef4acb1..3a98bd616 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java @@ -11,6 +11,7 @@ import javax.swing.JSpinner; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.unit.UnitGroup; @@ -93,7 +94,6 @@ public class DesignPreferencesPanel extends PreferencesPanel { // // Always open leftmost tab when opening a component edit dialog final JCheckBox alwaysOpenLeftmostTab = new JCheckBox( trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost")); - alwaysOpenLeftmostTab.setSelected(preferences.isAlwaysOpenLeftmostTab()); alwaysOpenLeftmostTab.setToolTipText(trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost.ttip")); alwaysOpenLeftmostTab.addActionListener(new ActionListener() { @@ -103,7 +103,7 @@ public class DesignPreferencesPanel extends PreferencesPanel { .isSelected()); } }); - this.add(alwaysOpenLeftmostTab, "wrap, growx, span 2"); + this.add(alwaysOpenLeftmostTab, "wrap, growx, spanx"); // // Update flight estimates in the design window final JCheckBox updateEstimates = new JCheckBox( @@ -117,5 +117,23 @@ public class DesignPreferencesPanel extends PreferencesPanel { } }); this.add(updateEstimates, "wrap, growx, sg combos "); + + // // Only show pod set/booster markers when they are selected + final JCheckBox showMarkers = new JCheckBox( + trans.get("pref.dlg.checkbox.Markers")); + showMarkers.setToolTipText(trans.get("pref.dlg.checkbox.Markers.ttip")); + showMarkers.setSelected(preferences.isShowMarkers()); + showMarkers.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setShowMarkers(showMarkers + .isSelected()); + // Update all BasicFrame rocket panel figures because it can change due to the preference change + for (BasicFrame frame : BasicFrame.getAllFrames()) { + frame.getRocketPanel().updateFigures(); + } + } + }); + this.add(showMarkers, "wrap, growx, spanx"); } } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java index 3061d6d62..eaf0afa87 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java @@ -7,6 +7,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -25,7 +27,10 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.gui.adaptors.PresetModel; +import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.TypedKey; @@ -43,10 +48,12 @@ public class ComponentPresetChooserDialog extends JDialog { private static final Translator trans = Application.getTranslator(); + private final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); + private final RocketComponent component; - private ComponentPresetTable componentSelectionTable; - private JTextField filterText; + private final ComponentPresetTable componentSelectionTable; + private final JTextField filterText; private JCheckBox foreDiameterFilterCheckBox; private JCheckBox aftDiameterFilterCheckBox; private JCheckBox showLegacyCheckBox; @@ -66,12 +73,14 @@ public class ComponentPresetChooserDialog extends JDialog { private List presets; private ComponentPreset.Type presetType; + private PresetModel presetModel; - public ComponentPresetChooserDialog(Window owner, RocketComponent component) { + public ComponentPresetChooserDialog(Window owner, RocketComponent component, PresetModel presetModel) { super(owner, trans.get("title"), Dialog.ModalityType.APPLICATION_MODAL); this.component = component; this.presetType = component.getPresetType(); + this.presetModel = presetModel; this.presets = Application.getComponentPresetDao().listForType(component.getPresetType()); List> displayedColumnKeys = Arrays.asList(component.getPresetType().getDisplayedColumns()); @@ -149,8 +158,18 @@ public class ComponentPresetChooserDialog extends JDialog { scrollpane.setViewportView(componentSelectionTable); panel.add(scrollpane, "grow, width 700lp, height 300lp, pushy, spanx, wrap rel"); - panel.add(new JLabel(Chars.UP_ARROW + " " + trans.get("lbl.favorites")), "spanx, gapleft 5px, wrap para"); - + panel.add(new StyledLabel(String.format("%s %s", Chars.UP_ARROW, trans.get("lbl.favorites")), -1), "spanx, gapleft 5px, wrap para"); + + // When double-clicking a preset row, apply the preset and close this dialog + componentSelectionTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + // Don't do anything when double-clicking the first column + if (e.getClickCount() == 2 && componentSelectionTable.getSelectedColumn() > 0 && applySelectedPreset()) { + ComponentPresetChooserDialog.this.setVisible(false); + } + } + }); // Close buttons JButton closeButton = new SelectColorButton(trans.get("dlg.but.close")); @@ -158,6 +177,7 @@ public class ComponentPresetChooserDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { ComponentPresetChooserDialog.this.setVisible(false); + applySelectedPreset(); } }); panel.add(closeButton, "spanx, right, tag close"); @@ -169,6 +189,21 @@ public class ComponentPresetChooserDialog extends JDialog { updateFilters(); } + + /** + * Applies the currently selected preset to presetModel. + * + * @return true if the preset was applied, false if otherwise. + */ + private boolean applySelectedPreset() { + if (presetModel == null) return false; + ComponentPreset preset = getSelectedComponentPreset(); + if (preset != null) { + presetModel.setSelectedItem(preset); + return true; + } + return false; + } private JPanel getFilterCheckboxes(XTableColumnModel tm, int legacyColumnIndex) { @@ -222,12 +257,14 @@ public class ComponentPresetChooserDialog extends JDialog { foreDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter")); final SymmetricComponent prevSym = curSym.getPreviousSymmetricComponent(); if (prevSym != null && foreDiameterColumnIndex >= 0) { + foreDiameterFilterCheckBox.setSelected(preferences.isMatchForeDiameter()); foreDiameterFilter = new ComponentPresetRowFilter(prevSym.getAftRadius() * 2.0, foreDiameterColumnIndex); panel.add(foreDiameterFilterCheckBox, "wrap"); foreDiameterFilterCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { updateFilters(); + preferences.setMatchForeDiameter(foreDiameterFilterCheckBox.isSelected()); } }); } @@ -238,12 +275,14 @@ public class ComponentPresetChooserDialog extends JDialog { aftDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter")); final SymmetricComponent nextSym = curSym.getNextSymmetricComponent(); if (nextSym != null && aftDiameterColumnIndex >= 0) { + aftDiameterFilterCheckBox.setSelected(preferences.isMatchAftDiameter()); aftDiameterFilter = new ComponentPresetRowFilter(nextSym.getForeRadius() * 2.0, aftDiameterColumnIndex); panel.add(aftDiameterFilterCheckBox, "wrap"); aftDiameterFilterCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { updateFilters(); + preferences.setMatchAftDiameter(aftDiameterFilterCheckBox.isSelected()); } }); } diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java index c361baebf..62c1bb31b 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java @@ -77,9 +77,11 @@ public class ComponentPresetTable extends JTable { if ( columnIndex != 0 ) { return; } + int selectedRow = ComponentPresetTable.this.getSelectedRow(); ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex); Application.getComponentPresetDao().setFavorite(preset, presetType, (Boolean) aValue); ComponentPresetTable.this.updateFavorites(); + ComponentPresetTable.this.setRowSelectionInterval(selectedRow, selectedRow); } @Override diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java index 6bf137b28..04821f75d 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java @@ -33,7 +33,7 @@ public abstract class ComponentPresetTableColumn extends TableColumn { @Override public Object getValueFromPreset( Set favorites, ComponentPreset preset ) { - return Boolean.valueOf(favorites.contains(preset.preferenceKey())); + return favorites.contains(preset.preferenceKey()); } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java index 5c2d39c1e..84c57a1ec 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java @@ -295,26 +295,37 @@ public class ComponentRenderer { final double ir = r.getInnerDiameter() / 2.0; gl.glRotated(r.getAngleOffset()*180/Math.PI -90 , 1, 0, 0); - //Inner Diameter - glu.gluCylinder(q, ir, ir, r.getTotalHeight(), LOD, 1); + // Base Cylinder + if (r.getBaseHeight() > 0) { + glu.gluCylinder(q, or, or, r.getBaseHeight(), LOD, 1); + glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); + glu.gluDisk(q, 0, or, LOD, 2); + glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); + gl.glTranslated(0, 0, r.getBaseHeight()); + glu.gluDisk(q, 0, or, LOD, 2); + } else { // Draw a closing cap if there is no base + glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); + glu.gluDisk(q, 0, ir, LOD, 2); + glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); + gl.glTranslated(0, 0, r.getBaseHeight()); + } + + // Inner Cylinder + glu.gluCylinder(q, ir, ir, r.getInnerHeight(), LOD, 1); - //Bottom Disc - glu.gluCylinder(q, or, or, r.getBaseHeight(), LOD, 1); - glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); - glu.gluDisk(q, 0, or, LOD, 2); - glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); - gl.glTranslated(0,0,r.getBaseHeight()); - glu.gluDisk(q, 0, or, LOD, 2); - - - //Upper Disc - gl.glTranslated(0,0,r.getTotalHeight() - r.getFlangeHeight() * 2.0); - glu.gluCylinder(q, or, or, r.getFlangeHeight(), LOD, 1); - glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); - glu.gluDisk(q, 0, or, LOD, 2); - glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); - gl.glTranslated(0,0,r.getFlangeHeight()); - glu.gluDisk(q, 0, or, LOD, 2); + // Flange Cylinder + if (r.getFlangeHeight() > 0) { + gl.glTranslated(0, 0, r.getInnerHeight()); + glu.gluCylinder(q, or, or, r.getFlangeHeight(), LOD, 1); + glu.gluQuadricOrientation(q, GLU.GLU_INSIDE); + glu.gluDisk(q, 0, or, LOD, 2); + glu.gluQuadricOrientation(q, GLU.GLU_OUTSIDE); + gl.glTranslated(0, 0, r.getFlangeHeight()); + glu.gluDisk(q, 0, or, LOD, 2); + } else { // Draw a closing cap if there is no flange + gl.glTranslated(0, 0, r.getInnerHeight()); + glu.gluDisk(q, 0, ir, LOD, 2); + } } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java index 2030dba2b..3ce2cd3cc 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java @@ -108,7 +108,7 @@ public class PhotoFrame extends JFrame { settings = new JDialog(this, trans.get("PhotoSettingsConfig.title")) { { - setContentPane(new PhotoSettingsConfig(p)); + setContentPane(new PhotoSettingsConfig(p, document)); pack(); this.setLocationByPlatform(true); GUIUtil.rememberWindowSize(this); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java index eca18a495..23599f7f3 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java @@ -36,7 +36,7 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings private double exhaustScale = 1.0; private double flameAspectRatio = 1.0; - private double sparkConcentration = 0; + private double sparkConcentration = 0.2; private double sparkWeight = 0; private Sky sky = Mountains.instance; @@ -278,5 +278,6 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings public void setSmokeOpacity(double smokeOpacity) { this.smokeOpacity = smokeOpacity; + setSmokeAlpha(smokeOpacity); } } \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java index 6294a6e96..ef27b0dc4 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java @@ -25,8 +25,10 @@ import javax.swing.event.ChangeListener; import com.jogamp.opengl.GL2; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.ColorIcon; import net.sf.openrocket.gui.components.EditableSpinner; import net.sf.openrocket.gui.components.StyledLabel; @@ -125,7 +127,7 @@ public class PhotoSettingsConfig extends JTabbedPane { } } - public PhotoSettingsConfig(PhotoSettings p) { + public PhotoSettingsConfig(PhotoSettings p, OpenRocketDocument document) { super(); setPreferredSize(new Dimension(240, 320)); @@ -170,25 +172,29 @@ public class PhotoSettingsConfig extends JTabbedPane { add(new JLabel(trans.get("PhotoSettingsConfig.lbl.pitch"))); DoubleModel pitchModel = new DoubleModel(p, "Pitch", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(pitchModel.getSpinnerModel()), "growx"); - add(new UnitSelector(pitchModel), "pushx, left, wrap"); + add(new UnitSelector(pitchModel), "growx"); + add(new BasicSlider(pitchModel.getSliderModel(0, 2 * Math.PI)), "pushx, left, wrap"); /// Yaw add(new JLabel(trans.get("PhotoSettingsConfig.lbl.yaw"))); DoubleModel yawModel = new DoubleModel(p, "Yaw", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(yawModel.getSpinnerModel()), "growx"); - add(new UnitSelector(yawModel), "wrap"); + add(new UnitSelector(yawModel), "growx"); + add(new BasicSlider(yawModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// Roll add(new JLabel(trans.get("PhotoSettingsConfig.lbl.roll"))); DoubleModel rollModel = new DoubleModel(p, "Roll", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(rollModel.getSpinnerModel()), "growx"); - add(new UnitSelector(rollModel), "wrap"); + add(new UnitSelector(rollModel), "growx"); + add(new BasicSlider(rollModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// Advance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.advance"))); DoubleModel advanceModel = new DoubleModel(p, "Advance", UnitGroup.UNITS_LENGTH); add(new EditableSpinner(advanceModel.getSpinnerModel()), "growx"); - add(new UnitSelector(advanceModel), "wrap"); + add(new UnitSelector(advanceModel), "growx"); + add(new BasicSlider(advanceModel.getSliderModel(-document.getRocket().getLength(), document.getRocket().getLength())), "wrap"); // Camera add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.camera"), Style.BOLD), "split, gapright para, span"); @@ -198,25 +204,29 @@ public class PhotoSettingsConfig extends JTabbedPane { add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAz"))); DoubleModel viewAzModel = new DoubleModel(p, "ViewAz", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(viewAzModel.getSpinnerModel()), "growx"); - add(new UnitSelector(viewAzModel), "wrap"); + add(new UnitSelector(viewAzModel), "growx"); + add(new BasicSlider(viewAzModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// View altitude add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAlt"))); - DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE); + DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2); add(new EditableSpinner(viewAltModle.getSpinnerModel()), "growx"); - add(new UnitSelector(viewAltModle), "wrap"); + add(new UnitSelector(viewAltModle), "growx"); + add(new BasicSlider(viewAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap"); /// View distance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vDist"))); DoubleModel viewDistanceModel = new DoubleModel(p, "ViewDistance", UnitGroup.UNITS_LENGTH); add(new EditableSpinner(viewDistanceModel.getSpinnerModel()), "growx"); - add(new UnitSelector(viewDistanceModel), "wrap"); + add(new UnitSelector(viewDistanceModel), "growx"); + add(new BasicSlider(viewDistanceModel.getSliderModel(0, 2 * document.getRocket().getLength())), "wrap"); /// FoV add(new JLabel(trans.get("PhotoSettingsConfig.lbl.fov"))); - DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE); + DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE, Math.PI * 57.3/180, Math.PI * 160/180); add(new EditableSpinner(fovModel.getSpinnerModel()), "growx"); - add(new UnitSelector(fovModel), "wrap"); + add(new UnitSelector(fovModel), "growx"); + add(new BasicSlider(fovModel.getSliderModel(Math.PI * 57.3/180, Math.PI * 160/180)), "wrap"); } }); @@ -232,19 +242,24 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Ambiance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.amb"))); - DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", 100, UnitGroup.UNITS_NONE, 0, 100); - add(new EditableSpinner(ambianceModel.getSpinnerModel()), "wrap"); + DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", UnitGroup.UNITS_RELATIVE, 0, 1); + add(new EditableSpinner(ambianceModel.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(ambianceModel)); + add(new BasicSlider(ambianceModel.getSliderModel(0, 1)), "pushx, left, wrap"); /// Light azimuth add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAz"))); DoubleModel lightAzModel = new DoubleModel(p, "LightAz", UnitGroup.UNITS_ANGLE); - add(new EditableSpinner(lightAzModel.getSpinnerModel()), "split 2"); - add(new UnitSelector(lightAzModel), "pushx, left, wrap"); + add(new EditableSpinner(lightAzModel.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(lightAzModel)); + add(new BasicSlider(lightAzModel.getSliderModel(-Math.PI, Math.PI)), "wrap"); /// Light altitude add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAlt"))); - DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE); - add(new EditableSpinner(lightAltModle.getSpinnerModel()), "wrap"); + DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2); + add(new EditableSpinner(lightAltModle.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(lightAltModle)); + add(new BasicSlider(lightAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap"); // Sky add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.sky"), Style.BOLD), "split, span, gapright para"); @@ -292,7 +307,7 @@ public class PhotoSettingsConfig extends JTabbedPane { setSelectedItem(noSky); } } - }, "wrap"); + }, "spanx, wrap"); /// Image credit final JLabel creditLabel = new JLabel(trans.get("PhotoSettingsConfig.lbl.skyCredit")); @@ -304,7 +319,7 @@ public class PhotoSettingsConfig extends JTabbedPane { credit.setOpaque(false); credit.setFocusable(false); credit.setFont(creditLabel.getFont()); - add(credit); + add(credit, "spanx"); final StateChangeListener skyChange = new StateChangeListener() { @Override @@ -332,22 +347,28 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Smoke add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smoke"))); BooleanModel smokeModel = new BooleanModel(p, "Smoke"); - add(new JCheckBox(smokeModel), "split 2, w 15"); + add(new JCheckBox(smokeModel), "split 2, spanx"); - add(smokeColorButton, "pushx, left, wrap"); + add(smokeColorButton, "wrap"); smokeModel.addEnableComponent(smokeColorButton); /// Smoke opacity add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smokeOpacity"))); - DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", 100, UnitGroup.UNITS_NONE, 0, 100); + DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner opacitySpinner = new EditableSpinner(smokeOpacityModel.getSpinnerModel()); - add(opacitySpinner, "wrap"); + UnitSelector opacitySelector = new UnitSelector(smokeOpacityModel); + BasicSlider opacitySlider = new BasicSlider(smokeOpacityModel.getSliderModel(0, 1)); + add(opacitySpinner, "growx"); + add(opacitySelector); + add(opacitySlider, "wrap"); smokeModel.addEnableComponent(opacitySpinner); + smokeModel.addEnableComponent(opacitySelector); + smokeModel.addEnableComponent(opacitySlider); /// Flame add(new JLabel(trans.get("PhotoSettingsConfig.lbl.flame"))); BooleanModel fireModel = new BooleanModel(p, "Flame"); - add(new JCheckBox(fireModel), "split 2, w 15"); + add(new JCheckBox(fireModel), "split 2, spanx"); add(flameColorButton, "wrap"); fireModel.addEnableComponent(flameColorButton); @@ -357,8 +378,11 @@ public class PhotoSettingsConfig extends JTabbedPane { DoubleModel flameAspectModel = new DoubleModel(p, "FlameAspectRatio", 100, UnitGroup.UNITS_NONE, 25, 250); EditableSpinner flameAspectSpinner = new EditableSpinner(flameAspectModel.getSpinnerModel()); - add(flameAspectSpinner, "wrap"); + BasicSlider flameAspectSlider = new BasicSlider(flameAspectModel.getSliderModel(25, 250)); + add(flameAspectSpinner, "growx"); + add(flameAspectSlider, "skip 1, wrap"); fireModel.addEnableComponent(flameAspectSpinner); + fireModel.addEnableComponent(flameAspectSlider); /// Sparks add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparks"))); @@ -369,23 +393,36 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Sparks concentration add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkConcentration"))); - DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration", 100, - UnitGroup.UNITS_NONE, 0, 100); + DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration", + UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner sparkConcentrationSpinner = new EditableSpinner(sparkConcentrationModel.getSpinnerModel()); - add(sparkConcentrationSpinner, "wrap"); + UnitSelector sparkConcentrationSelector = new UnitSelector(sparkConcentrationModel); + BasicSlider sparkConcentrationSlider = new BasicSlider(sparkConcentrationModel.getSliderModel(0, 1)); + add(sparkConcentrationSpinner, "growx"); + add(sparkConcentrationSelector); + add(sparkConcentrationSlider, "wrap"); sparksModel.addEnableComponent(sparkConcentrationSpinner); + sparksModel.addEnableComponent(sparkConcentrationSelector); + sparksModel.addEnableComponent(sparkConcentrationSlider); /// Spark weight add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkWeight"))); - DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", 100, UnitGroup.UNITS_NONE, 0, 100); + DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner sparkWeightSpinner = new EditableSpinner(sparkWeightModel.getSpinnerModel()); - add(sparkWeightSpinner, "wrap"); + UnitSelector sparkWeightSelector = new UnitSelector(sparkWeightModel); + BasicSlider sparkWeightSlider = new BasicSlider(sparkWeightModel.getSliderModel(0, 1)); + add(sparkWeightSpinner, "growx"); + add(sparkWeightSelector); + add(sparkWeightSlider, "wrap"); sparksModel.addEnableComponent(sparkWeightSpinner); + sparksModel.addEnableComponent(sparkWeightSelector); + sparksModel.addEnableComponent(sparkWeightSlider); /// Exhaust scale add(new JLabel(trans.get("PhotoSettingsConfig.lbl.exhaustScale"))); DoubleModel exhaustScaleModel = new DoubleModel(p, "ExhaustScale", 100, UnitGroup.UNITS_NONE, 0, 1000); - add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "wrap"); + add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "growx"); + add(new BasicSlider(exhaustScaleModel.getSliderModel(0, 1000)), "skip 1, wrap"); // Effects add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.effects"), Style.BOLD), "split, span, gapright para"); diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java index 28bce901e..d3876b181 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java @@ -41,7 +41,7 @@ public class RocketInfo implements FigureElement { private final Caret cpCaret = new CPCaret(0,0); private final Caret cgCaret = new CGCaret(0,0); - private final UnitGroup stabilityUnits; + private UnitGroup stabilityUnits; private FlightConfiguration configuration; private double cg = 0, cp = 0; @@ -459,5 +459,6 @@ public class RocketInfo implements FigureElement { public void setCurrentConfig(FlightConfiguration newConfig) { this.configuration = newConfig; + this.stabilityUnits = UnitGroup.stabilityUnits(newConfig); } } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index b5304eba3..e9c10880e 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -117,7 +117,8 @@ public class BasicFrame extends JFrame { * List of currently open frames. When the list goes empty * it is time to exit the application. */ - private static final ArrayList frames = new ArrayList(); + private static final List frames = new ArrayList(); + private static BasicFrame startupFrame = null; // the frame that was created at startup /** @@ -417,18 +418,18 @@ public class BasicFrame extends JFrame { button = new SelectColorButton(actions.getMoveDownAction()); panel.add(button, "sizegroup buttons, aligny 0%"); - button = new SelectColorButton(actions.getEditAction()); - button.setIcon(null); + button = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(button, actions.getEditAction()); button.setMnemonic(0); panel.add(button, "sizegroup buttons, gaptop 20%"); - button = new SelectColorButton(actions.getDuplicateAction()); - button.setIcon(null); + button = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(button, actions.getDuplicateAction()); button.setMnemonic(0); panel.add(button, "sizegroup buttons"); - button = new SelectColorButton(actions.getDeleteAction()); - button.setIcon(null); + button = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(button, actions.getDeleteAction()); button.setMnemonic(0); panel.add(button, "sizegroup buttons"); @@ -489,6 +490,10 @@ public class BasicFrame extends JFrame { return result; } + public RocketPanel getRocketPanel() { + return rocketpanel; + } + /** * Creates the menu for the window. */ @@ -836,19 +841,15 @@ public class BasicFrame extends JFrame { } - //// Analyze - menu = new JMenu(trans.get("main.menu.analyze")); - menu.setMnemonic(KeyEvent.VK_A); - - //// Analyzing the rocket - menu.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.desc")); + // Tools + menu = new JMenu(trans.get("main.menu.tools")); menubar.add(menu); //// Component analysis - item = new JMenuItem(trans.get("main.menu.analyze.componentAnalysis"), KeyEvent.VK_C); + item = new JMenuItem(trans.get("main.menu.tools.componentAnalysis"), KeyEvent.VK_C); //// Analyze the rocket components separately - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.componentAnalysis.desc")); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.componentAnalysis.desc")); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -859,8 +860,8 @@ public class BasicFrame extends JFrame { menu.add(item); //// Optimize - item = new JMenuItem(trans.get("main.menu.analyze.optimization"), KeyEvent.VK_O); - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.optimization.desc")); + item = new JMenuItem(trans.get("main.menu.tools.optimization"), KeyEvent.VK_O); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.optimization.desc")); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -875,8 +876,8 @@ public class BasicFrame extends JFrame { menu.add(item); //// Custom expressions - item = new JMenuItem(trans.get("main.menu.analyze.customExpressions"), KeyEvent.VK_E); - item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.analyze.customExpressions.desc")); + item = new JMenuItem(trans.get("main.menu.tools.customExpressions"), KeyEvent.VK_E); + item.getAccessibleContext().setAccessibleDescription(trans.get("main.menu.tools.customExpressions.desc")); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -1231,6 +1232,19 @@ public class BasicFrame extends JFrame { return menu; } + /** + * Return the frame that was created at the application's startup. + */ + public static BasicFrame getStartupFrame() { + return startupFrame; + } + + /** + * Set the frame that is created at the application's startup. + */ + public static void setStartupFrame(BasicFrame startupFrame) { + BasicFrame.startupFrame = startupFrame; + } /** * Select the tab on the main pane. @@ -1273,7 +1287,7 @@ public class BasicFrame extends JFrame { for (File file : files) { log.info("Opening file: " + file); - if (open(file, parent)) { + if (open(file, parent) != null) { MRUDesignFile opts = MRUDesignFile.getInstance(); opts.addFile(file.getAbsolutePath()); } @@ -1303,7 +1317,7 @@ public class BasicFrame extends JFrame { for (File file : files) { log.info("Opening file: " + file); - if (open(file, this)) { + if (open(file, this) != null) { MRUDesignFile opts = MRUDesignFile.getInstance(); opts.addFile(file.getAbsolutePath()); } @@ -1373,9 +1387,9 @@ public class BasicFrame extends JFrame { * * @param file the file to open. * @param parent the parent component for which a progress dialog is opened. - * @return whether the file was successfully loaded and opened. + * @return the BasicFrame that was created, or null if not created successfully. */ - public static boolean open(File file, Window parent) { + public static BasicFrame open(File file, Window parent) { OpenFileWorker worker = new OpenFileWorker(file); return open(worker, file.getName(), parent, false); } @@ -1388,15 +1402,15 @@ public class BasicFrame extends JFrame { * @param displayName the file name to display in dialogs. * @param parent * @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples. - * @return + * @return the BasicFrame that was created, or null if not created successfully. */ - private static boolean open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) { + private static BasicFrame open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) { //// Open the file in a Swing worker thread log.info("Starting OpenFileWorker"); if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + displayName + "...", worker)) { // // User cancelled the operation log.info("User cancelled the OpenFileWorker"); - return false; + return null; } //// Handle the document @@ -1415,7 +1429,7 @@ public class BasicFrame extends JFrame { JOptionPane.showMessageDialog(parent, "File not found: " + displayName, "Error opening file", JOptionPane.ERROR_MESSAGE); - return false; + return null; } else if (cause instanceof RocketLoadException) { @@ -1424,7 +1438,7 @@ public class BasicFrame extends JFrame { "Unable to open file '" + displayName + "': " + cause.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE); - return false; + return null; } else { @@ -1468,7 +1482,7 @@ public class BasicFrame extends JFrame { ComponentConfigDialog.showDialog(frame, doc, doc.getRocket()); } - return true; + return frame; } @@ -1774,24 +1788,27 @@ public class BasicFrame extends JFrame { /** * Opens a new design file or the last design file, if set in the preferences. * Can be used for reopening the application or opening it the first time. + * @return the BasicFrame that was created */ - public static void reopen() { + public static BasicFrame reopen() { if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) { - BasicFrame.newAction(); + return BasicFrame.newAction(); } else { String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile(); if (lastFile != null) { log.info("Opening last design file: " + lastFile); - if (!BasicFrame.open(new File(lastFile), null)) { + BasicFrame frame = BasicFrame.open(new File(lastFile), null); + if (frame == null) { MRUDesignFile.getInstance().removeFile(lastFile); - BasicFrame.newAction(); + return BasicFrame.newAction(); } else { MRUDesignFile.getInstance().addFile(lastFile); + return frame; } } else { - BasicFrame.newAction(); + return BasicFrame.newAction(); } } } @@ -1799,8 +1816,9 @@ public class BasicFrame extends JFrame { /** * Open a new design window with a basic rocket+stage. + * @return the BasicFrame that was created */ - public static void newAction() { + public static BasicFrame newAction() { log.info("New action initiated"); OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket(); @@ -1808,6 +1826,7 @@ public class BasicFrame extends JFrame { BasicFrame frame = new BasicFrame(doc); frame.replaceable = true; frame.setVisible(true); + return frame; } @@ -1871,6 +1890,13 @@ public class BasicFrame extends JFrame { return null; } + /** + * Return all BasicFrame instances + */ + public static List getAllFrames() { + return frames; + } + /** * Checks whether all the BasicFrames are closed. * @return true if all the BasicFrames are closed, false if not diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java index 8ed2338c9..0ff03e261 100644 --- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java +++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java @@ -488,7 +488,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable { } } - ComponentConfigDialog.showDialog(parent, document, component, false); + ComponentConfigDialog.showDialog(parent, document, component, false, false); } } diff --git a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java index 5582e4fca..3ff152d82 100644 --- a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java +++ b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java @@ -73,7 +73,7 @@ public final class MRUDesignFileAction extends JMenu { @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); - if (BasicFrame.open(new File(command), parent)) { + if (BasicFrame.open(new File(command), parent) != null) { MRUDesignFile.getInstance().addFile(command); } else { diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index 86a3ea6bf..15ed1d597 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -12,6 +12,7 @@ import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -189,6 +190,35 @@ public class RocketActions { return moveDownAction; } + /** + * Tie an action to a JButton, without using the icon or text of the action for the button. + * + * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the + * icon to null and set the button text: this causes a bug where the text of the icon becomes much smaller than is intended. + * + * @param button button to tie the action to + * @param action action to tie to the button + * @param text text to display on the button + */ + public static void tieActionToButtonNoIcon(JButton button, Action action, String text) { + button.setAction(action); + button.setIcon(null); + button.setText(text); + } + + /** + * Tie an action to a JButton, without using the icon of the action for the button. + * + * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the + * icon to null: this causes a bug where the text of the icon becomes much smaller than is intended. + * + * @param button button to tie the action to + * @param action action to tie to the button + */ + public static void tieActionToButtonNoIcon(JButton button, Action action) { + button.setAction(action); + button.setIcon(null); + } //////// Helper methods for the actions diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index f8ac84ec5..cd46a4880 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -11,6 +11,7 @@ import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -19,6 +20,8 @@ import java.util.Arrays; import java.util.Comparator; import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; @@ -98,366 +101,91 @@ public class SimulationPanel extends JPanel { private final JButton plotButton; private final JPopupMenu pm; + private final SimulationAction editSimulationAction; + private final SimulationAction runSimulationAction; + private final SimulationAction plotSimulationAction; + private final SimulationAction duplicateSimulationAction; + private final SimulationAction deleteSimulationAction; + public SimulationPanel(OpenRocketDocument doc) { super(new MigLayout("fill", "[grow][][][][][][grow]")); this.document = doc; + // Simulation actions + SimulationAction newSimulationAction = new NewSimulationAction(); + editSimulationAction = new EditSimulationAction(); + runSimulationAction = new RunSimulationAction(); + plotSimulationAction = new PlotSimulationAction(); + duplicateSimulationAction = new DuplicateSimulationAction(); + deleteSimulationAction = new DeleteSimulationAction(); - //////// The simulation action buttons + //////// The simulation action buttons //////// //// New simulation button - { - JButton button = new SelectColorButton(trans.get("simpanel.but.newsimulation")); - //// Add a new simulation - button.setToolTipText(trans.get("simpanel.but.ttip.newsimulation")); - button.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Simulation sim = new Simulation(document.getRocket()); - sim.setName(document.getNextSimulationName()); - - int n = document.getSimulationCount(); - document.addSimulation(sim); - simulationTableModel.fireTableDataChanged(); - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(n, n); - - openDialog(false, sim); - } - }); - this.add(button, "skip 1, gapright para"); - } + JButton newButton = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(newButton, newSimulationAction, trans.get("simpanel.but.newsimulation")); + newButton.setToolTipText(trans.get("simpanel.but.ttip.newsimulation")); + this.add(newButton, "skip 1, gapright para"); //// Edit simulation button - editButton = new SelectColorButton(trans.get("simpanel.but.editsimulation")); - //// Edit the selected simulation + editButton = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(editButton, editSimulationAction, trans.get("simpanel.but.editsimulation")); editButton.setToolTipText(trans.get("simpanel.but.ttip.editsim")); - editButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - editSimulation(); - } - }); this.add(editButton, "gapright para"); //// Run simulations - runButton = new SelectColorButton(trans.get("simpanel.but.runsimulations")); - //// Re-run the selected simulations + runButton = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(runButton, runSimulationAction, trans.get("simpanel.but.runsimulations")); runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu")); - runButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - runSimulation(); - } - }); this.add(runButton, "gapright para"); //// Delete simulations button - deleteButton = new SelectColorButton(trans.get("simpanel.but.deletesimulations")); - //// Delete the selected simulations + deleteButton = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(deleteButton, deleteSimulationAction, trans.get("simpanel.but.deletesimulations")); deleteButton.setToolTipText(trans.get("simpanel.but.ttip.deletesim")); - deleteButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - deleteSimulation(); - } - }); this.add(deleteButton, "gapright para"); //// Plot / export button - plotButton = new SelectColorButton(trans.get("simpanel.but.plotexport")); - // button = new SelectColorButton("Plot flight"); - plotButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - plotSimulation(); - } - }); + plotButton = new SelectColorButton(); + RocketActions.tieActionToButtonNoIcon(plotButton, plotSimulationAction, trans.get("simpanel.but.plotexport")); this.add(plotButton, "wrap para"); //////// The simulation table + simulationTableModel = new SimulationTableModel(); - simulationTableModel = new ColumnTableModel( - - //// Status and warning column - new Column("") { - private JLabel label = null; - - @Override - public Object getValueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - // Initialize the label - if (label == null) { - label = new StyledLabel(2f); - label.setIconTextGap(1); - // label.setFont(label.getFont().deriveFont(Font.BOLD)); - } - - // Set simulation status icon - Simulation.Status status = document.getSimulation(row).getStatus(); - label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status)); - - - // Set warning marker - if (status == Simulation.Status.NOT_SIMULATED || - status == Simulation.Status.EXTERNAL) { - - label.setText(""); - - } else { - - WarningSet w = document.getSimulation(row).getSimulatedWarnings(); - if (w == null) { - label.setText(""); - } else if (w.isEmpty()) { - label.setForeground(OK_COLOR); - label.setText(OK_TEXT); - } else { - label.setForeground(WARNING_COLOR); - label.setText(WARNING_TEXT); - } - } - - return label; - } - - @Override - public int getExactWidth() { - return 36; - } - - @Override - public Class getColumnClass() { - return JLabel.class; - } - }, - - //// Simulation name - //// Name - new Column(trans.get("simpanel.col.Name")) { - @Override - public Object getValueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - return document.getSimulation(row).getName(); - } - - @Override - public int getDefaultWidth() { - return 125; - } - - @Override - public Comparator getComparator() { - return new AlphanumComparator(); - } - }, - - //// Simulation configuration - new Column(trans.get("simpanel.col.Configuration")) { - @Override - public Object getValueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()){ - return null; - } - - Rocket rkt = document.getRocket(); - FlightConfigurationId fcid = document.getSimulation(row).getId(); - return descriptor.format( rkt, fcid); - } - - @Override - public int getDefaultWidth() { - return 125; - } - }, - - //// Launch rod velocity - new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getLaunchRodVelocity(); - } - - }, - - //// Apogee - new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getMaxAltitude(); - } - }, - - //// Velocity at deployment - new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getDeploymentVelocity(); - } - }, - - //// Deployment Time from Apogee - new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"), - trans.get("simpanel.col.OptimumCoastTime.ttip"), - UnitGroup.UNITS_SHORT_TIME) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null || data.getBranchCount() == 0) - return null; - - double val = data.getBranch(0).getOptimumDelay(); - if ( Double.isNaN(val) ) { - return null; - } - return val; - } - }, - - //// Maximum velocity - new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getMaxVelocity(); - } - }, - - //// Maximum acceleration - new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getMaxAcceleration(); - } - }, - - //// Time to apogee - new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getTimeToApogee(); - } - }, - - //// Flight time - new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getFlightTime(); - } - }, - - //// Ground hit velocity - new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) { - @Override - public Double valueAt(int row) { - if (row < 0 || row >= document.getSimulationCount()) - return null; - - FlightData data = document.getSimulation(row).getSimulatedData(); - if (data == null) - return null; - - return data.getGroundHitVelocity(); - } - } - - ) { - - private static final long serialVersionUID = 8686456963492628476L; - - @Override - public int getRowCount() { - return document.getSimulationCount(); - } - }; - - // Override processKeyBinding so that the JTable does not catch - // key bindings used in menu accelerators simulationTable = new ColumnTable(simulationTableModel) { - private static final long serialVersionUID = -5799340181229735630L; - - @Override - protected boolean processKeyBinding(KeyStroke ks, - KeyEvent e, - int condition, - boolean pressed) { - return false; - } }; ColumnTableRowSorter simulationTableSorter = new ColumnTableRowSorter(simulationTableModel); simulationTable.setRowSorter(simulationTableSorter); simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer()); simulationTableModel.setColumnWidths(simulationTable.getColumnModel()); - - pm = new JPopupMenu(); - pm.add(new EditSimulationAction()); - pm.add(new DuplicateSimulationAction()); - pm.add(new DeleteSimulationAction()); - pm.addSeparator(); - pm.add(new RunSimulationAction()); - pm.add(new PlotSimulationAction()); + // Context menu + pm = new JPopupMenu(); + pm.add(editSimulationAction); + pm.add(duplicateSimulationAction); + pm.add(deleteSimulationAction); + pm.addSeparator(); + pm.add(runSimulationAction); + pm.add(plotSimulationAction); + + // The normal left/right and tab/shift-tab key action traverses each cell/column of the table instead of going to the next row. + InputMap im = simulationTable.getInputMap(); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "Action.NextRowCycle"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "Action.NextRow"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK), "Action.PreviousRowCycle"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Action.PreviousRow"); + ActionMap am = simulationTable.getActionMap(); + am.put("Action.NextRow", new NextRowAction(simulationTable, false)); + am.put("Action.NextRowCycle", new NextRowAction(simulationTable, true)); + am.put("Action.PreviousRow", new PreviousRowAction(simulationTable, false)); + am.put("Action.PreviousRowCycle", new PreviousRowAction(simulationTable, true)); // Mouse listener to act on double-clicks simulationTable.addMouseListener(new MouseAdapter() { @@ -496,11 +224,14 @@ public class SimulationPanel extends JPanel { }); simulationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - private int previousRow = -1; + private int previousSelectedRow = -1; + private int previousSelectedRowCount = 0; public void valueChanged(ListSelectionEvent event) { - if (simulationTable.getSelectedRow() != previousRow) { + if ((simulationTable.getSelectedRow() != previousSelectedRow) || + (simulationTable.getSelectedRowCount() != previousSelectedRowCount)) { updateButtonStates(); - previousRow = simulationTable.getSelectedRow(); + previousSelectedRow = simulationTable.getSelectedRow(); + previousSelectedRowCount = simulationTable.getSelectedRowCount(); } } }); @@ -532,6 +263,19 @@ public class SimulationPanel extends JPanel { updateButtonStates(); } + private void newSimulation() { + Simulation sim = new Simulation(document.getRocket()); + sim.setName(document.getNextSimulationName()); + + int n = document.getSimulationCount(); + document.addSimulation(sim); + simulationTableModel.fireTableDataChanged(); + simulationTable.clearSelection(); + simulationTable.addRowSelectionInterval(n, n); + + openDialog(false, sim); + } + private void plotSimulation() { int selected = simulationTable.getSelectedRow(); if (selected < 0) { @@ -690,23 +434,11 @@ public class SimulationPanel extends JPanel { } private void updateButtonStates() { - int[] selection = simulationTable.getSelectedRows(); - if (selection.length == 0) { - editButton.setEnabled(false); - runButton.setEnabled(false); - deleteButton.setEnabled(false); - plotButton.setEnabled(false); - } else { - if (selection.length > 1) { - plotButton.setEnabled(false); - } else { - plotButton.setEnabled(true); - } - editButton.setEnabled(true); - runButton.setEnabled(true); - deleteButton.setEnabled(true); - } - + editSimulationAction.updateEnabledState(); + deleteSimulationAction.updateEnabledState(); + runSimulationAction.updateEnabledState(); + plotSimulationAction.updateEnabledState(); + duplicateSimulationAction.updateEnabledState(); } /// when the simulation tab is selected this run outdated simulated if appropriate. @@ -778,7 +510,31 @@ public class SimulationPanel extends JPanel { } } - class EditSimulationAction extends AbstractAction { + private abstract static class SimulationAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + public abstract void updateEnabledState(); + } + + class NewSimulationAction extends SimulationAction { + public NewSimulationAction() { + putValue(NAME, trans.get("simpanel.but.newsimulation")); + this.putValue(MNEMONIC_KEY, KeyEvent.VK_N); + this.putValue(SMALL_ICON, Icons.FILE_NEW); + } + + @Override + public void actionPerformed(ActionEvent e) { + newSimulation(); + } + + @Override + public void updateEnabledState() { + setEnabled(true); + } + } + + class EditSimulationAction extends SimulationAction { public EditSimulationAction() { putValue(NAME, trans.get("simpanel.pop.edit")); this.putValue(MNEMONIC_KEY, KeyEvent.VK_E); @@ -790,9 +546,14 @@ public class SimulationPanel extends JPanel { public void actionPerformed(ActionEvent e) { editSimulation(); } + + @Override + public void updateEnabledState() { + setEnabled(simulationTable.getSelectedRowCount() == 1); + } } - class RunSimulationAction extends AbstractAction { + class RunSimulationAction extends SimulationAction { public RunSimulationAction() { putValue(NAME, trans.get("simpanel.pop.run")); putValue(SMALL_ICON, Icons.SIM_RUN); @@ -802,9 +563,14 @@ public class SimulationPanel extends JPanel { public void actionPerformed(ActionEvent e) { runSimulation(); } + + @Override + public void updateEnabledState() { + setEnabled(simulationTable.getSelectedRowCount() > 0); + } } - class DeleteSimulationAction extends AbstractAction { + class DeleteSimulationAction extends SimulationAction { public DeleteSimulationAction() { putValue(NAME, trans.get("simpanel.pop.delete")); putValue(MNEMONIC_KEY, KeyEvent.VK_D); @@ -816,9 +582,14 @@ public class SimulationPanel extends JPanel { public void actionPerformed(ActionEvent e) { deleteSimulation(); } + + @Override + public void updateEnabledState() { + setEnabled(simulationTable.getSelectedRowCount() > 0); + } } - class PlotSimulationAction extends AbstractAction { + class PlotSimulationAction extends SimulationAction { public PlotSimulationAction() { putValue(NAME, trans.get("simpanel.pop.plot")); putValue(SMALL_ICON, Icons.SIM_PLOT); @@ -828,9 +599,14 @@ public class SimulationPanel extends JPanel { public void actionPerformed(ActionEvent e) { plotSimulation(); } + + @Override + public void updateEnabledState() { + setEnabled(simulationTable.getSelectedRowCount() == 1); + } } - class DuplicateSimulationAction extends AbstractAction { + class DuplicateSimulationAction extends SimulationAction { public DuplicateSimulationAction() { putValue(NAME, trans.get("simpanel.pop.duplicate")); putValue(MNEMONIC_KEY, KeyEvent.VK_D); @@ -842,7 +618,12 @@ public class SimulationPanel extends JPanel { public void actionPerformed(ActionEvent e) { duplicateSimulation(); } - } + + @Override + public void updateEnabledState() { + setEnabled(simulationTable.getSelectedRowCount() > 0); + } + } public static class CellTransferable implements Transferable { @@ -955,6 +736,318 @@ public class SimulationPanel extends JPanel { return tip; } + } + + private class SimulationTableModel extends ColumnTableModel { + private static final long serialVersionUID = 8686456963492628476L; + + public SimulationTableModel() { + super( + //// Status and warning column + new Column("") { + private JLabel label = null; + + @Override + public Object getValueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + // Initialize the label + if (label == null) { + label = new StyledLabel(2f); + label.setIconTextGap(1); + // label.setFont(label.getFont().deriveFont(Font.BOLD)); + } + + // Set simulation status icon + Simulation.Status status = document.getSimulation(row).getStatus(); + label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status)); + + + // Set warning marker + if (status == Simulation.Status.NOT_SIMULATED || + status == Simulation.Status.EXTERNAL) { + + label.setText(""); + + } else { + + WarningSet w = document.getSimulation(row).getSimulatedWarnings(); + if (w == null) { + label.setText(""); + } else if (w.isEmpty()) { + label.setForeground(OK_COLOR); + label.setText(OK_TEXT); + } else { + label.setForeground(WARNING_COLOR); + label.setText(WARNING_TEXT); + } + } + + return label; + } + + @Override + public int getExactWidth() { + return 36; + } + + @Override + public Class getColumnClass() { + return JLabel.class; + } + }, + + //// Simulation name + //// Name + new Column(trans.get("simpanel.col.Name")) { + @Override + public Object getValueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + return document.getSimulation(row).getName(); + } + + @Override + public int getDefaultWidth() { + return 125; + } + + @Override + public Comparator getComparator() { + return new AlphanumComparator(); + } + }, + + //// Simulation configuration + new Column(trans.get("simpanel.col.Configuration")) { + @Override + public Object getValueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) { + return null; + } + + Rocket rkt = document.getRocket(); + FlightConfigurationId fcid = document.getSimulation(row).getId(); + return descriptor.format(rkt, fcid); + } + + @Override + public int getDefaultWidth() { + return 125; + } + }, + + //// Launch rod velocity + new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getLaunchRodVelocity(); + } + + }, + + //// Apogee + new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getMaxAltitude(); + } + }, + + //// Velocity at deployment + new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getDeploymentVelocity(); + } + }, + + //// Deployment Time from Apogee + new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"), + trans.get("simpanel.col.OptimumCoastTime.ttip"), UnitGroup.UNITS_SHORT_TIME) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null || data.getBranchCount() == 0) + return null; + + double val = data.getBranch(0).getOptimumDelay(); + if (Double.isNaN(val)) { + return null; + } + return val; + } + }, + + //// Maximum velocity + new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getMaxVelocity(); + } + }, + + //// Maximum acceleration + new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getMaxAcceleration(); + } + }, + + //// Time to apogee + new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getTimeToApogee(); + } + }, + + //// Flight time + new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getFlightTime(); + } + }, + + //// Ground hit velocity + new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) { + @Override + public Double valueAt(int row) { + if (row < 0 || row >= document.getSimulationCount()) + return null; + + FlightData data = document.getSimulation(row).getSimulatedData(); + if (data == null) + return null; + + return data.getGroundHitVelocity(); + } + } + ); + } + + @Override + public int getRowCount() { + return document.getSimulationCount(); + } + } + + private static class NextRowAction extends AbstractAction { + private final JTable table; + private final boolean cycle; + + /** + * Action for cycling through the next row of the table. + * @param table table for which the action is intended + * @param cycle whether to go back to the first row if the end is reached. + */ + public NextRowAction(JTable table, boolean cycle) { + this.table = table; + this.cycle = cycle; + } + + @Override + public void actionPerformed(ActionEvent e) { + int nextRow = table.getSelectedRow() + 1; + + if (nextRow >= table.getRowCount()) { + if (cycle) { + nextRow = 0; + } else { + return; + } + } + + table.getSelectionModel().setSelectionInterval(nextRow, nextRow); + table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0); + } + + } + + private static class PreviousRowAction extends AbstractAction { + private final JTable table; + private final boolean cycle; + + /** + * Action for cycling through the previous row of the table. + * @param table table for which the action is intended + * @param cycle whether to go back to the last row if the current row is the first one of the table. + */ + public PreviousRowAction(JTable table, boolean cycle) { + this.table = table; + this.cycle = cycle; + } + + @Override + public void actionPerformed(ActionEvent e) { + int nextRow = table.getSelectedRow() - 1; + + if (nextRow < 0) { + if (cycle) { + nextRow = table.getRowCount() - 1; + } else { + return; + } + } + + table.getSelectionModel().setSelectionInterval(nextRow, nextRow); + table.getColumnModel().getSelectionModel().setSelectionInterval(0, 0); + } } } diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java index a02e626f9..a638ba8d0 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java @@ -6,6 +6,9 @@ import javax.swing.ToolTipManager; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.components.BasicTree; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + @SuppressWarnings("serial") public class ComponentTree extends BasicTree { @@ -13,7 +16,21 @@ public class ComponentTree extends BasicTree { public ComponentTree(OpenRocketDocument document) { super(); this.setModel(new ComponentTreeModel(document.getRocket(), this)); - + + addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { } + + @Override + public void keyPressed(KeyEvent e) { + if ((e.getKeyCode() == KeyEvent.VK_A) && ((e.getModifiersEx() & KeyEvent.META_DOWN_MASK) != 0)) { + setSelectionInterval(1, getRowCount()); // Don't select the rocket (row 0) + } + } + + @Override + public void keyReleased(KeyEvent e) { } + }); this.setCellRenderer(new ComponentTreeRenderer()); this.setDragEnabled(true); diff --git a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java index 1aad92e50..77b9b687c 100644 --- a/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java +++ b/swing/src/net/sf/openrocket/gui/preset/PresetEditorDialog.java @@ -72,7 +72,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener { private static final long serialVersionUID = -3298642844886682536L; - private static Translator trans = Application.getTranslator(); + private static final Translator trans = Application.getTranslator(); private static final Logger log = LoggerFactory.getLogger(PresetEditorDialog.class); @@ -82,138 +82,137 @@ public class PresetEditorDialog extends JDialog implements ItemListener { * Input of non-negative decimals. */ final PresetInputVerifier NON_NEGATIVE_INTEGER = new PresetInputVerifier(Pattern.compile(NON_NEGATIVE_INTEGER_FIELD)); - - private final JPanel contentPanel = new JPanel(); - private DeselectableComboBox typeCombo; - private JTextField mfgTextField; - private MaterialChooser materialChooser; + + private final DeselectableComboBox typeCombo; + private final JTextField mfgTextField; + private final MaterialChooser materialChooser; private MaterialHolder holder = null; - private JTextField ncPartNoTextField; - private JTextField ncDescTextField; - private DoubleModel ncLength; - private JCheckBox ncFilledCB; - private JComboBox ncShapeCB; - private DoubleModel ncAftDia; - private DoubleModel ncAftShoulderDia; - private DoubleModel ncAftShoulderLen; - private DoubleModel ncMass; + private final JTextField ncPartNoTextField; + private final JTextField ncDescTextField; + private final DoubleModel ncLength; + private final JCheckBox ncFilledCB; + private final JComboBox ncShapeCB; + private final DoubleModel ncAftDia; + private final DoubleModel ncAftShoulderDia; + private final DoubleModel ncAftShoulderLen; + private final DoubleModel ncMass; private ImageIcon ncImage; - private JButton ncImageBtn; + private final JButton ncImageBtn; - private JTextField trPartNoTextField; - private JTextField trDescTextField; - private DoubleModel trLength; - private DoubleModel trAftDia; - private DoubleModel trAftShoulderDia; - private DoubleModel trAftShoulderLen; - private DoubleModel trForeDia; - private DoubleModel trForeShoulderDia; - private DoubleModel trForeShoulderLen; + private final JTextField trPartNoTextField; + private final JTextField trDescTextField; + private final DoubleModel trLength; + private final DoubleModel trAftDia; + private final DoubleModel trAftShoulderDia; + private final DoubleModel trAftShoulderLen; + private final DoubleModel trForeDia; + private final DoubleModel trForeShoulderDia; + private final DoubleModel trForeShoulderLen; private DoubleModel trMass; private ImageIcon trImage; - private JCheckBox trFilledCB; - private JComboBox trShapeCB; - private JButton trImageBtn; + private final JCheckBox trFilledCB; + private final JComboBox trShapeCB; + private final JButton trImageBtn; - private JTextField btPartNoTextField; - private JTextField btDescTextField; - private DoubleModel btMass; - private DoubleModel btInnerDia; - private DoubleModel btOuterDia; - private DoubleModel btLength; + private final JTextField btPartNoTextField; + private final JTextField btDescTextField; + private final DoubleModel btMass; + private final DoubleModel btInnerDia; + private final DoubleModel btOuterDia; + private final DoubleModel btLength; private ImageIcon btImage; - private JButton btImageBtn; + private final JButton btImageBtn; - private JTextField tcPartNoTextField; - private JTextField tcDescTextField; - private DoubleModel tcMass; - private DoubleModel tcInnerDia; - private DoubleModel tcOuterDia; - private DoubleModel tcLength; + private final JTextField tcPartNoTextField; + private final JTextField tcDescTextField; + private final DoubleModel tcMass; + private final DoubleModel tcInnerDia; + private final DoubleModel tcOuterDia; + private final DoubleModel tcLength; private ImageIcon tcImage; - private JButton tcImageBtn; + private final JButton tcImageBtn; - private JTextField bhPartNoTextField; - private JTextField bhDescTextField; - private DoubleModel bhOuterDia; - private DoubleModel bhLength; - private DoubleModel bhMass; + private final JTextField bhPartNoTextField; + private final JTextField bhDescTextField; + private final DoubleModel bhOuterDia; + private final DoubleModel bhLength; + private final DoubleModel bhMass; private ImageIcon bhImage; - private JButton bhImageBtn; + private final JButton bhImageBtn; - private JTextField crPartNoTextField; - private JTextField crDescTextField; - private DoubleModel crOuterDia; - private DoubleModel crInnerDia; - private DoubleModel crThickness; - private DoubleModel crMass; + private final JTextField crPartNoTextField; + private final JTextField crDescTextField; + private final DoubleModel crOuterDia; + private final DoubleModel crInnerDia; + private final DoubleModel crThickness; + private final DoubleModel crMass; private ImageIcon crImage; - private JButton crImageBtn; + private final JButton crImageBtn; - private JTextField ebPartNoTextField; - private JTextField ebDescTextField; - private DoubleModel ebOuterDia; - private DoubleModel ebInnerDia; - private DoubleModel ebThickness; - private DoubleModel ebMass; + private final JTextField ebPartNoTextField; + private final JTextField ebDescTextField; + private final DoubleModel ebOuterDia; + private final DoubleModel ebInnerDia; + private final DoubleModel ebThickness; + private final DoubleModel ebMass; private ImageIcon ebImage; - private JButton ebImageBtn; + private final JButton ebImageBtn; - private JTextField llPartNoTextField; - private JTextField llDescTextField; - private DoubleModel llOuterDia; - private DoubleModel llInnerDia; - private DoubleModel llLength; - private DoubleModel llMass; + private final JTextField llPartNoTextField; + private final JTextField llDescTextField; + private final DoubleModel llOuterDia; + private final DoubleModel llInnerDia; + private final DoubleModel llLength; + private final DoubleModel llMass; private ImageIcon llImage; - private JButton llImageBtn; + private final JButton llImageBtn; - private JTextField rbPartNoTextField; - private JTextField rbDescTextField; - private DoubleModel rbOuterDia; - private DoubleModel rbInnerDia; - private DoubleModel rbHeight; - private DoubleModel rbStandoffHeight; - private DoubleModel rbFlangeHeight; - private DoubleModel rbMass; + private final JTextField rbPartNoTextField; + private final JTextField rbDescTextField; + private final DoubleModel rbOuterDia; + private final DoubleModel rbInnerDia; + private final DoubleModel rbHeight; + private final DoubleModel rbStandoffHeight; + private final DoubleModel rbFlangeHeight; + private final DoubleModel rbMass; private ImageIcon rbImage; - private JButton rbImageBtn; + private final JButton rbImageBtn; - private JTextField stPartNoTextField; - private JTextField stDescTextField; - private DoubleModel stThickness; - private DoubleModel stWidth; - private DoubleModel stLength; - private DoubleModel stMass; + private final JTextField stPartNoTextField; + private final JTextField stDescTextField; + private final DoubleModel stThickness; + private final DoubleModel stWidth; + private final DoubleModel stLength; + private final DoubleModel stMass; private ImageIcon stImage; - private JButton stImageBtn; + private final JButton stImageBtn; // Parachute Specific - private JTextField pcPartNoTextField; - private JTextField pcDescTextField; - private DoubleModel pcDiameter; + private final JTextField pcPartNoTextField; + private final JTextField pcDescTextField; + private final DoubleModel pcDiameter; private DoubleModel pcSpillDia; private DoubleModel pcSurfaceArea; private DoubleModel pcDragCoefficient; // Canopy material = private MaterialChooser materialChooser; - private JTextField pcSides; - private JTextField pcLineCount; - private DoubleModel pcLineLength; + private final JTextField pcSides; + private final JTextField pcLineCount; + private final DoubleModel pcLineLength; private DoubleModel pcPackedLength; private DoubleModel pcPackedDiameter; - private MaterialChooser pcLineMaterialChooser; - private DoubleModel pcMass; + private final MaterialChooser pcLineMaterialChooser; + private final DoubleModel pcMass; private ImageIcon pcImage; - private JButton pcImageBtn; + private final JButton pcImageBtn; private final JFileChooser imageChooser = createImageChooser(); - private JPanel componentOverlayPanel; + private final JPanel componentOverlayPanel; - private PresetResultListener resultListener; + private final PresetResultListener resultListener; - private static Map componentMap = new HashMap(); + private static final Map componentMap = new HashMap(); private static final String NOSE_CONE_KEY = "NoseCone.NoseCone"; private static final String BODY_TUBE_KEY = "BodyTube.BodyTube"; @@ -263,6 +262,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener { getContentPane().setMinimumSize(new Dimension(200, 200)); setBounds(100, 100, 825, 610); getContentPane().setLayout(new BorderLayout()); + JPanel contentPanel = new JPanel(); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER); contentPanel.setLayout(new MigLayout("", "[][grow][94.00,grow][232.0,grow][130.00][grow]", "[][][20.00,grow][grow]")); @@ -1582,8 +1582,8 @@ public class PresetEditorDialog extends JDialog implements ItemListener { rbHeight.setValue(preset.get(ComponentPreset.HEIGHT)); rbHeight.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } - if (preset.has(ComponentPreset.STANDOFF_HEIGHT)) { - rbStandoffHeight.setValue(preset.get(ComponentPreset.STANDOFF_HEIGHT)); + if (preset.has(ComponentPreset.BASE_HEIGHT)) { + rbStandoffHeight.setValue(preset.get(ComponentPreset.BASE_HEIGHT)); rbStandoffHeight.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } if (preset.has(ComponentPreset.FLANGE_HEIGHT)) { @@ -1610,8 +1610,8 @@ public class PresetEditorDialog extends JDialog implements ItemListener { pcDiameter.setValue(preset.get(ComponentPreset.DIAMETER)); pcDiameter.setCurrentUnit(UnitGroup.UNITS_LENGTH.getDefaultUnit()); } - if (preset.has(ComponentPreset.PARACHUTE_CD)) { - pcDragCoefficient.setValue(preset.get(ComponentPreset.PARACHUTE_CD)); + if (preset.has(ComponentPreset.CD)) { + pcDragCoefficient.setValue(preset.get(ComponentPreset.CD)); pcDragCoefficient.setCurrentUnit(UnitGroup.UNITS_COEFFICIENT.getDefaultUnit()); } setMaterial(materialChooser, preset, matHolder, Material.Type.SURFACE, ComponentPreset.MATERIAL); @@ -2132,7 +2132,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener { props.put(ComponentPreset.TYPE, ComponentPreset.Type.RAIL_BUTTON); props.put(ComponentPreset.OUTER_DIAMETER, rbOuterDia.getValue()); props.put(ComponentPreset.INNER_DIAMETER, rbInnerDia.getValue()); - props.put(ComponentPreset.STANDOFF_HEIGHT, rbStandoffHeight.getValue()); + props.put(ComponentPreset.BASE_HEIGHT, rbStandoffHeight.getValue()); props.put(ComponentPreset.FLANGE_HEIGHT, rbFlangeHeight.getValue()); props.put(ComponentPreset.DESCRIPTION, rbDescTextField.getText()); props.put(ComponentPreset.PARTNO, rbPartNoTextField.getText()); @@ -2179,7 +2179,7 @@ public class PresetEditorDialog extends JDialog implements ItemListener { props.put(ComponentPreset.MANUFACTURER, Manufacturer.getManufacturer(mfgTextField.getText())); props.put(ComponentPreset.PARTNO, pcPartNoTextField.getText()); props.put(ComponentPreset.DESCRIPTION, pcDescTextField.getText()); - props.put(ComponentPreset.PARACHUTE_CD, pcDragCoefficient.getValue()); + props.put(ComponentPreset.CD, pcDragCoefficient.getValue()); Material material = (Material) materialChooser.getSelectedItem(); if (material != null) { props.put(ComponentPreset.MATERIAL, material); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java new file mode 100644 index 000000000..35b048551 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java @@ -0,0 +1,122 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; + +/** + * Shapes of an "empty"/virtual object, e.g. a podset without any children. + * The shape is a center square with additional lines on the north, east, south and west side of the square. + * + * @author Sibo Van Gool + */ +public class EmptyShapes extends RocketComponentShape { + /** + * Returns the empty shape in the side view. + * @param radius radius of the center square + */ + public static Shape[] getShapesSide(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double x = instanceAbsoluteLocation.x; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + final Shape[] s = new Shape[5]; + // Center square + s[0] = new Rectangle2D.Double(x - radius, y - radius, 2 * radius, 2 * radius); + // Line North + s[1] = new Line2D.Double(x, y + radius, x, y + radius + lineLength); + // Line East + s[2] = new Line2D.Double(x + radius, y, x + radius + lineLength, y); + // Line South + s[3] = new Line2D.Double(x, y - radius, x, y - radius - lineLength); + // Line West + s[4] = new Line2D.Double(x - radius, y, x - radius - lineLength, y); + + return s; + } + + /** + * Returns the empty shape in the side view, with an additional square encompassing the shape that can be used + * for selecting the object. + * @param radius radius of the center square + */ + public static Shape[] getShapesSideWithSelectionSquare(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double x = instanceAbsoluteLocation.x; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + Shape[] shapes = getShapesSide(transformation, radius); + + // Invisible shape for selecting the component (= a square encompassing the component) + Shape selectionShape = new Rectangle2D.Double(x - radius - lineLength, y - radius - lineLength, + lineLength * 2 + radius * 2, lineLength * 2 + radius * 2); + + Shape[] finalShapes = new Shape[shapes.length + 1]; + System.arraycopy(shapes, 0, finalShapes, 0, shapes.length); + finalShapes[finalShapes.length - 1] = selectionShape; + + return finalShapes; + } + + /** + * Returns the empty shape in the side view. + * @param radius radius of the center square + */ + public static Shape[] getShapesBack(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double z = instanceAbsoluteLocation.z; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + final Shape[] s = new Shape[5]; + // Center square + s[0] = new Rectangle2D.Double(z - radius, y - radius, 2 * radius, 2 * radius); + // Line North + s[1] = new Line2D.Double(z, y + radius, z, y + radius + lineLength); + // Line East + s[2] = new Line2D.Double(z + radius, y, z + radius + lineLength, y); + // Line South + s[3] = new Line2D.Double(z, y - radius, z, y - radius - lineLength); + // Line West + s[4] = new Line2D.Double(z - radius, y, z - radius - lineLength, y); + + return s; + } + + /** + * Returns the empty shape in the back view, with an additional square encompassing the shape that can be used + * for selecting the object. + * @param radius radius of the center square + */ + public static Shape[] getShapesBackWithSelectionSquare(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double z = instanceAbsoluteLocation.z; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + Shape[] shapes = getShapesBack(transformation, radius); + + // Invisible shape for selecting the component (= a square encompassing the component) + Shape selectionShape = new Rectangle2D.Double(z - radius - lineLength, y - radius - lineLength, + lineLength * 2 + radius * 2, lineLength * 2 + radius * 2); + + Shape[] finalShapes = new Shape[shapes.length + 1]; + System.arraycopy(shapes, 0, finalShapes, 0, shapes.length); + finalShapes[finalShapes.length - 1] = selectionShape; + + return finalShapes; + } + + private static double getLineLength(double radius) { + return radius * 3; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java new file mode 100644 index 000000000..565a23591 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; + +public class ParallelStageShapes extends RocketComponentShape { + public static final Color boosterColor = new Color(198,163,184); + + public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) { + ParallelStage booster = (ParallelStage)component; + double radius = getDisplayRadius(booster); + + Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(boosterColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) { + ParallelStage booster = (ParallelStage)component; + double radius = getDisplayRadius(booster); + + Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(boosterColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + private static double getDisplayRadius(ParallelStage booster) { + return booster.getRocket().getBoundingRadius() * 0.03; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java new file mode 100644 index 000000000..7ef928f2d --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.rocketcomponent.PodSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; + +public class PodSetShapes extends RocketComponentShape { + public static final Color podsetColor = new Color(160,160,215); + + public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) { + PodSet podset = (PodSet)component; + double radius = getDisplayRadius(podset); + + Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(podsetColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) { + PodSet podset = (PodSet)component; + double radius = getDisplayRadius(podset); + + Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(podsetColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + private static double getDisplayRadius(PodSet podset) { + return podset.getRocket().getBoundingRadius() * 0.03; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java index 8250c41b8..26c95a24d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RailButtonShapes.java @@ -29,7 +29,7 @@ public class RailButtonShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesSide( final RocketComponent component, final Transformation transformation) { final RailButton btn = (RailButton)component; - final double baseHeight = btn.getStandoff(); + final double baseHeight = btn.getBaseHeight(); final double innerHeight = btn.getInnerHeight(); final double flangeHeight = btn.getFlangeHeight(); @@ -53,72 +53,73 @@ public class RailButtonShapes extends RocketComponentShape { Path2D.Double path = new Path2D.Double(); Path2D.Double pathInvis = new Path2D.Double(); // Path for the invisible triangles - {// central pillar - final double drawWidth = outerDiameter; - final double drawHeight = outerDiameter*sinr; - final Point2D.Double center = new Point2D.Double( loc.x, loc.y ); - Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - - path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+baseHeightcos) ), false); - path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+baseHeightcos) ), false); - - path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+baseHeightcos), drawWidth, drawHeight), false); + {// base cylinder + if (baseHeight > 0) { + final double drawWidth = outerDiameter; + final double drawHeight = outerDiameter * sinr; + final Point2D.Double center = new Point2D.Double(loc.x, loc.y); + Point2D.Double lowerLeft = new Point2D.Double(center.x - outerRadius, center.y - outerRadius * sinr); + path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - // Invisible rectangle - double y_invis; - if (baseHeightcos >= 0) { - y_invis = center.y; + path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + baseHeightcos)), false); + path.append(new Line2D.Double((center.x + outerRadius), center.y, (center.x + outerRadius), (center.y + baseHeightcos)), false); + + path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + baseHeightcos), drawWidth, drawHeight), false); + + // Invisible rectangle + double y_invis; + if (baseHeightcos >= 0) { + y_invis = center.y; + } else { + y_invis = center.y + baseHeightcos; + } + pathInvis.append(new Rectangle2D.Double(center.x - outerRadius, y_invis, drawWidth, Math.abs(baseHeightcos)), false); } - else { - y_invis = center.y + baseHeightcos; - } - pathInvis.append(new Rectangle2D.Double(center.x-outerRadius, y_invis, drawWidth, Math.abs(baseHeightcos)), false); } - {// inner flange + {// inner cylinder final double drawWidth = innerDiameter; - final double drawHeight = innerDiameter*sinr; - final Point2D.Double center = new Point2D.Double( loc.x, loc.y + baseHeightcos); - final Point2D.Double lowerLeft = new Point2D.Double( center.x - innerRadius, center.y-innerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - - path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+innerHeightcos) ), false); - path.append( new Line2D.Double( (center.x+innerRadius), center.y, (center.x+innerRadius), (center.y+innerHeightcos) ), false); - - path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+innerHeightcos), drawWidth, drawHeight), false); + final double drawHeight = innerDiameter * sinr; + final Point2D.Double center = new Point2D.Double(loc.x, loc.y + baseHeightcos); + final Point2D.Double lowerLeft = new Point2D.Double(center.x - innerRadius, center.y - innerRadius * sinr); + path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); + + path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + innerHeightcos)), false); + path.append(new Line2D.Double((center.x + innerRadius), center.y, (center.x + innerRadius), (center.y + innerHeightcos)), false); + + path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + innerHeightcos), drawWidth, drawHeight), false); // Invisible rectangle double y_invis; if (innerHeightcos >= 0) { y_invis = center.y; - } - else { + } else { y_invis = center.y + innerHeightcos; } - pathInvis.append(new Rectangle2D.Double(center.x-innerRadius, y_invis, drawWidth, Math.abs(innerHeightcos)), false); + pathInvis.append(new Rectangle2D.Double(center.x - innerRadius, y_invis, drawWidth, Math.abs(innerHeightcos)), false); } - {// outer flange - final double drawWidth = outerDiameter; - final double drawHeight = outerDiameter*sinr; - final Point2D.Double center = new Point2D.Double( loc.x, loc.y+baseHeightcos+innerHeightcos); - final Point2D.Double lowerLeft = new Point2D.Double( center.x - outerRadius, center.y-outerRadius*sinr); - path.append( new Ellipse2D.Double( lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - - path.append( new Line2D.Double( lowerLeft.x, center.y, lowerLeft.x, (center.y+flangeHeightcos) ), false); - path.append( new Line2D.Double( (center.x+outerRadius), center.y, (center.x+outerRadius), (center.y+flangeHeightcos) ), false); - - path.append( new Ellipse2D.Double( lowerLeft.x, (lowerLeft.y+flangeHeightcos), drawWidth, drawHeight), false); + {// flange cylinder + if (flangeHeight > 0) { + final double drawWidth = outerDiameter; + final double drawHeight = outerDiameter * sinr; + final Point2D.Double center = new Point2D.Double(loc.x, loc.y + baseHeightcos + innerHeightcos); + final Point2D.Double lowerLeft = new Point2D.Double(center.x - outerRadius, center.y - outerRadius * sinr); + path.append(new Ellipse2D.Double(lowerLeft.x, lowerLeft.y, drawWidth, drawHeight), false); - // Invisible rectangle - double y_invis; - if (flangeHeightcos >= 0) { - y_invis = center.y; + path.append(new Line2D.Double(lowerLeft.x, center.y, lowerLeft.x, (center.y + flangeHeightcos)), false); + path.append(new Line2D.Double((center.x + outerRadius), center.y, (center.x + outerRadius), (center.y + flangeHeightcos)), false); + + path.append(new Ellipse2D.Double(lowerLeft.x, (lowerLeft.y + flangeHeightcos), drawWidth, drawHeight), false); + + // Invisible rectangle + double y_invis; + if (flangeHeightcos >= 0) { + y_invis = center.y; + } else { + y_invis = center.y + flangeHeightcos; + } + pathInvis.append(new Rectangle2D.Double(center.x - outerRadius, y_invis, drawWidth, Math.abs(flangeHeightcos)), false); } - else { - y_invis = center.y + flangeHeightcos; - } - pathInvis.append(new Rectangle2D.Double(center.x-outerRadius, y_invis, drawWidth, Math.abs(flangeHeightcos)), false); } RocketComponentShape[] shapes = RocketComponentShape.toArray(new Shape[]{ path }, component); @@ -136,7 +137,7 @@ public class RailButtonShapes extends RocketComponentShape { public static RocketComponentShape[] getShapesBack( final RocketComponent component, final Transformation transformation) { final RailButton btn = (RailButton)component; - final double baseHeight = btn.getStandoff(); + final double baseHeight = btn.getBaseHeight(); final double innerHeight = btn.getInnerHeight(); final double flangeHeight = btn.getFlangeHeight(); @@ -159,7 +160,9 @@ public class RailButtonShapes extends RocketComponentShape { Path2D.Double path = new Path2D.Double(); // base - path.append( getRotatedRectangle( loc.z, loc.y, outerRadius, baseHeight, combined_angle_rad), false ); + if (baseHeight > 0) { + path.append(getRotatedRectangle(loc.z, loc.y, outerRadius, baseHeight, combined_angle_rad), false); + } {// inner final double delta_r = baseHeight; @@ -167,11 +170,13 @@ public class RailButtonShapes extends RocketComponentShape { final double delta_z = delta_r*sinr; path.append( getRotatedRectangle( loc.z+delta_z, loc.y+delta_y, innerRadius, innerHeight, combined_angle_rad), false); } - {// outer flange - final double delta_r = baseHeight + innerHeight; - final double delta_y = delta_r*cosr; - final double delta_z = delta_r*sinr; - path.append( getRotatedRectangle( loc.z+delta_z, loc.y+delta_y, outerRadius, flangeHeight, combined_angle_rad), false); + {// flange + if (flangeHeight > 0) { + final double delta_r = baseHeight + innerHeight; + final double delta_y = delta_r * cosr; + final double delta_z = delta_r * sinr; + path.append(getRotatedRectangle(loc.z + delta_z, loc.y + delta_y, outerRadius, flangeHeight, combined_angle_rad), false); + } } return RocketComponentShape.toArray( new Shape[]{ path }, component); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java index 725713b80..c132bfd3d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java @@ -64,6 +64,10 @@ public class RocketComponentShape { return new RocketComponentShape[0]; } + public Color getColor() { + return color; + } + public void setColor(Color color) { this.color = color; } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 3226abee2..0fe7cf400 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -17,6 +17,9 @@ import java.awt.geom.Rectangle2D; import java.util.*; import java.util.Map.Entry; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.PodSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,7 +29,6 @@ import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; -import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.InstanceContext; import net.sf.openrocket.rocketcomponent.MotorMount; @@ -52,6 +54,7 @@ import net.sf.openrocket.util.Transformation; public class RocketFigure extends AbstractScaleFigure { private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); + protected final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure"; private static final String ROCKET_FIGURE_SUFFIX = "Shapes"; @@ -279,38 +282,38 @@ public class RocketFigure extends AbstractScaleFigure { Color borderColor = ((SwingPreferences)Application.getPreferences()).getMotorBorderColor(); FlightConfiguration config = rocket.getSelectedConfiguration(); - for( MotorConfiguration curInstance : config.getActiveMotors()){ + for (MotorConfiguration curInstance : config.getActiveMotors()) { MotorMount mount = curInstance.getMount(); Motor motor = curInstance.getMotor(); double motorLength = motor.getLength(); double motorRadius = motor.getDiameter() / 2; RocketComponent mountComponent = ((RocketComponent) mount); - + // .getLocation() will return all the parent instances of this owning component, AND all of it's own instances as well. // so, just draw a motor once for each Coordinate returned... Coordinate[] mountLocations = mount.getLocations(); - + double mountLength = mountComponent.getLength(); // System.err.println("Drawing Motor: "+motor.getDesignation()+" (x"+mountLocations.length+")"); - for ( Coordinate curMountLocation : mountLocations ){ - Coordinate curMotorLocation = curMountLocation.add( mountLength - motorLength + mount.getMotorOverhang(), 0, 0); + for (Coordinate curMountLocation : mountLocations) { + Coordinate curMotorLocation = curMountLocation.add(mountLength - motorLength + mount.getMotorOverhang(), 0, 0); // System.err.println(String.format(" mount instance: %s => %s", curMountLocation.toString(), curMotorLocation.toString() )); - - // rotate by figure's axial rotation: - curMotorLocation = this.axialRotation.transform(curMotorLocation); + + // rotate by figure's axial rotation: + curMotorLocation = this.axialRotation.transform(curMotorLocation); { Shape s; if (currentViewType == RocketPanel.VIEW_TYPE.SideView) { - s = new Rectangle2D.Double( curMotorLocation.x, - (curMotorLocation.y - motorRadius), - motorLength, - 2 * motorRadius); + s = new Rectangle2D.Double(curMotorLocation.x, + (curMotorLocation.y - motorRadius), + motorLength, + 2 * motorRadius); } else { s = new Ellipse2D.Double((curMotorLocation.z - motorRadius), - (curMotorLocation.y - motorRadius), - 2 * motorRadius, - 2 * motorRadius); + (curMotorLocation.y - motorRadius), + 2 * motorRadius, + 2 * motorRadius); } g2.setColor(fillColor); g2.fill(s); @@ -375,23 +378,41 @@ public class RocketFigure extends AbstractScaleFigure { // allShapes is an output buffer -- it stores all the generated shapes allShapes.clear(); - - for(Entry> entry: config.getActiveInstances().entrySet() ) { + + for (Entry> entry : config.getActiveInstances().entrySet()) { final RocketComponent comp = entry.getKey(); - + + // Only draw podsets when they are selected + if ((comp instanceof PodSet || comp instanceof ParallelStage) && preferences.isShowMarkers()) { + boolean selected = false; + + // Check if component is in the selection + for (RocketComponent component : selection) { + if (comp == component) { + selected = true; + break; + } + } + if (!selected) continue; + } + final ArrayList contextList = entry.getValue(); - for(InstanceContext context: contextList ) { + for (InstanceContext context : contextList) { final Transformation currentTransform = this.axialRotation.applyTransformation(context.transform); - allShapes = addThisShape( allShapes, this.currentViewType, comp, currentTransform); + allShapes = addThisShape(allShapes, this.currentViewType, comp, currentTransform); } - } + } } /** * Gets the shapes required to draw the component. - * - * @param component + * + * @param allShapes output buffer for the shapes to add to + * @param viewType the view type to draw the component in + * @param component component to draw and add to + * @param transformation transformation to apply to the component before drawing it + * @param color color to draw the component in * * @return the ArrayList containing all the shapes to draw. */ @@ -399,10 +420,11 @@ public class RocketFigure extends AbstractScaleFigure { PriorityQueue allShapes, // this is the output parameter final RocketPanel.VIEW_TYPE viewType, final RocketComponent component, - final Transformation transformation) { + final Transformation transformation, + final net.sf.openrocket.util.Color color) { Reflection.Method m; - if(( component instanceof Rocket)||( component instanceof ComponentAssembly )){ + if ((component instanceof Rocket) || (component instanceof AxialStage && !(component instanceof ParallelStage))){ // no-op; no shapes here return allShapes; } @@ -431,9 +453,35 @@ public class RocketFigure extends AbstractScaleFigure { RocketComponentShape[] returnValue = (RocketComponentShape[]) m.invokeStatic(component, transformation); + + if (color != null) { + for (RocketComponentShape rcs : returnValue) { + if (rcs.getColor() == net.sf.openrocket.util.Color.INVISIBLE) continue; // don't change the color of invisible (often selection) components + rcs.setColor(color); + } + } + allShapes.addAll(Arrays.asList(returnValue)); return allShapes; } + + /** + * Gets the shapes required to draw the component. + * + * @param allShapes output buffer for the shapes to add to + * @param viewType the view type to draw the component in + * @param component component to draw and add to + * @param transformation transformation to apply to the component before drawing it + * + * @return the ArrayList containing all the shapes to draw. + */ + private static PriorityQueue addThisShape( + PriorityQueue allShapes, // this is the output parameter + final RocketPanel.VIEW_TYPE viewType, + final RocketComponent component, + final Transformation transformation) { + return addThisShape(allShapes, viewType, component, transformation, null); + } /** diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 92df95906..9f4f309f7 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -252,7 +252,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change }); } - private void updateFigures() { + public void updateFigures() { if (!is3d) figure.updateFigure(); else diff --git a/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java new file mode 100644 index 000000000..d4443954c --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.gui.util; + +import java.awt.GraphicsDevice; +import java.awt.Window; + +/** + * Helper class for setting the location of a Swing window. E.g. to check when the window is outside the screen and + * recenter it if so. + * + * @author Sibo Van Gool + */ +public abstract class WindowLocationUtil { + /** + * Sets the location of the window, but don't go outside the screen. + * @param window the window to move + * @param x the target x position on the screen + * @param y the target y position on the screen + */ + public static void setLocationWithinScreen(Window window, int x, int y) { + java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + java.awt.Dimension windowSize = window.getSize(); + int screenWidth = screenSize.width; + int screenHeight = screenSize.height; + int windowWidth = windowSize.width; + int windowHeight = windowSize.height; + int xPos = x; + int yPos = y; + if (xPos + windowWidth > screenWidth) { + xPos = screenWidth - windowWidth; + } + if (yPos + windowHeight > screenHeight) { + yPos = screenHeight - windowHeight; + } + window.setLocation(xPos, yPos); + } + + /** + * Moves the window to the center of the screen if its location is outside the boundary of the screen. + * @param window window to move + */ + public static void moveIfOutsideOfMonitor(Window window) { + GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice(); + if (currentDevice != null && window.isVisible() && + !currentDevice.getDefaultConfiguration().getBounds().contains(window.getLocationOnScreen())) { + int width = currentDevice.getDefaultConfiguration().getBounds().width; + int height = currentDevice.getDefaultConfiguration().getBounds().height; + window.setLocation( + ((width / 2) - (window.getSize().width / 2)) + currentDevice.getDefaultConfiguration().getBounds().x, + ((height / 2) - (window.getSize().height / 2)) + currentDevice.getDefaultConfiguration().getBounds().y + ); + } + } + + /** + * Moves the window to the center of the screen if it is on another monitor as the parent window, or if its location + * is outside the boundary of the parent's monitor screen. + * @param window window to move + * @param parent parent window + */ + public static void moveIfOutsideOfParentMonitor(Window window, Window parent) { + GraphicsDevice parentDevice = parent.getGraphicsConfiguration().getDevice(); + GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice(); + if (parentDevice != null && (currentDevice == null || currentDevice != parentDevice || + (window.isVisible() && !parentDevice.getDefaultConfiguration().getBounds().contains( + window.getLocationOnScreen())))) { + int width = parentDevice.getDefaultConfiguration().getBounds().width; + int height = parentDevice.getDefaultConfiguration().getBounds().height; + window.setLocation( + ((width / 2) - (window.getSize().width / 2)) + parentDevice.getDefaultConfiguration().getBounds().x, + ((height / 2) - (window.getSize().height / 2)) + parentDevice.getDefaultConfiguration().getBounds().y + ); + } + } +} diff --git a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java index 1fd7c2eae..d49753dac 100644 --- a/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java +++ b/swing/src/net/sf/openrocket/simulation/extension/impl/ScriptingConfigurator.java @@ -8,7 +8,6 @@ import java.awt.event.FocusListener; import java.util.Set; import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -33,7 +32,6 @@ import com.google.inject.Inject; @Plugin public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfigurator { - @Inject private ScriptingUtil util; @@ -66,7 +64,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi }); panel.add(languageSelector, "wrap para"); - text = new RSyntaxTextArea(extension.getScript(), 20, 80); text.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT); text.setCodeFoldingEnabled(true); @@ -91,7 +88,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi RTextScrollPane scroll = new RTextScrollPane(text); panel.add(scroll, "spanx, grow, wrap para"); - BooleanModel enabled = new BooleanModel(extension, "Enabled"); JCheckBox check = new JCheckBox(enabled); check.setText(trans.get("SimulationExtension.scripting.text.enabled")); @@ -116,7 +112,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi }); panel.add(button, "wrap rel"); - StyledLabel label = new StyledLabel(trans.get("SimulationExtension.scripting.text.trusted.msg"), -1, Style.ITALIC); panel.add(label); @@ -130,7 +125,6 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi util.setTrustedScript(extension.getLanguage(), extension.getScript(), trusted.isSelected()); } - private void setLanguage(String language) { if (language == null) { language = ""; @@ -144,8 +138,7 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi } private String findSyntaxLanguage(String language) { - ScriptEngineManager manager = new ScriptEngineManager(); - ScriptEngine engine = manager.getEngineByName(language); + ScriptEngine engine = util.getEngineByName(language); if (engine != null) { Set supported = TokenMakerFactory.getDefaultInstance().keySet(); @@ -163,5 +156,4 @@ public class ScriptingConfigurator extends AbstractSwingSimulationExtensionConfi return SyntaxConstants.SYNTAX_STYLE_NONE; } - } diff --git a/swing/src/net/sf/openrocket/startup/SwingStartup.java b/swing/src/net/sf/openrocket/startup/SwingStartup.java index 5d6ff3386..462549e11 100644 --- a/swing/src/net/sf/openrocket/startup/SwingStartup.java +++ b/swing/src/net/sf/openrocket/startup/SwingStartup.java @@ -223,7 +223,8 @@ public class SwingStartup { // Starting action (load files or open new document) log.info("Opening main application window"); if (!handleCommandLine(args)) { - BasicFrame.reopen(); + BasicFrame startupFrame = BasicFrame.reopen(); + BasicFrame.setStartupFrame(startupFrame); } // Check whether update info has been fetched or whether it needs more time @@ -298,7 +299,7 @@ public class SwingStartup { // Check command-line for files boolean opened = false; for (String file : args) { - if (BasicFrame.open(new File(file), null)) { + if (BasicFrame.open(new File(file), null) != null) { opened = true; } } diff --git a/swing/src/net/sf/openrocket/utils/Scripting.java b/swing/src/net/sf/openrocket/utils/Scripting.java index ca1a2993d..ec347a189 100644 --- a/swing/src/net/sf/openrocket/utils/Scripting.java +++ b/swing/src/net/sf/openrocket/utils/Scripting.java @@ -5,16 +5,17 @@ import java.util.Collections; import java.util.List; import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; import org.fife.ui.rsyntaxtextarea.TokenMakerFactory; +import net.sf.openrocket.scripting.ScriptEngineManagerRedux; + public class Scripting { public static void main(String[] args) { System.out.println("Scripting APIs:"); - ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngineManagerRedux manager = new ScriptEngineManagerRedux(); for (ScriptEngineFactory factory : manager.getEngineFactories()) { System.out.println(" engineName=" + factory.getEngineName() + " engineVersion=" + factory.getEngineVersion() + diff --git a/swing/test/net/sf/openrocket/IntegrationTest.java b/swing/test/net/sf/openrocket/IntegrationTest.java index 24cb6178e..e0a280f09 100644 --- a/swing/test/net/sf/openrocket/IntegrationTest.java +++ b/swing/test/net/sf/openrocket/IntegrationTest.java @@ -71,7 +71,7 @@ public class IntegrationTest { private Action undoAction, redoAction; private AerodynamicCalculator aeroCalc = new BarrowmanCalculator(); - private FlightConfiguration config; + private FlightConfigurationId fcid; private FlightConditions conditions; private String massComponentID = null; @@ -112,14 +112,12 @@ public class IntegrationTest { undoAction = UndoRedoAction.newUndoAction(document); redoAction = UndoRedoAction.newRedoAction(document); - FlightConfigurationId fcid = document.getSimulation(0).getFlightConfigurationId(); - config = document.getRocket().getFlightConfiguration(fcid); + fcid = document.getSimulation(0).getFlightConfigurationId(); + FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid); conditions = new FlightConditions(config); // Test undo state checkUndoState(null, null); - - InnerTube mmt = (InnerTube)config.getRocket().getChild(0).getChild(1).getChild(2); // Compute cg+cp + altitude // double cgx, double mass, double cpx, double cna) @@ -330,6 +328,7 @@ public class IntegrationTest { } private void checkCgCp(double cgx, double mass, double cpx, double cna) { + FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid); final RigidBody launchData = MassCalculator.calculateLaunch(config); final Coordinate cg = launchData.getCenterOfMass(); assertEquals(cgx, cg.x, 0.001);